|
//實(shí)例89:將"渴望"樂(lè)譜寫(xiě)入AT24C02并讀出播放
#include <reg51.h> // 包含51單片機(jī)寄存器定義的頭文件 #include <intrins.h> //包含_nop_()函數(shù)定義的頭文件 #define OP_READ 0xa1 // 器件地址以及讀取操作,0xa1即為1010 0001B #define OP_WRITE 0xa0 // 器件地址以及寫(xiě)入操作,0xa1即為1010 0000B sbit SDA=P3^4; //將串行數(shù)據(jù)總線SDA位定義在為P3.4引腳 sbit SCL=P3^3; //將串行時(shí)鐘總線SDA位定義在為P3.3引腳 sbit sound=P3^7; //將sound位定義為P3.7,從該引腳輸出音頻 unsigned int C; //儲(chǔ)存定時(shí)器的定時(shí)常數(shù) //以下是C調(diào)低音的音頻宏定義
#define l_dao 262 //將“l_dao”宏定義為低音“1”的頻率262Hz #define l_re 286 //將“l_re”宏定義為低音“2”的頻率286Hz #define l_mi 311 //將“l_mi”宏定義為低音“3”的頻率311Hz #define l_fa 349 //將“l_fa”宏定義為低音“4”的頻率349Hz #define l_sao 392 //將“l_sao”宏定義為低音“5”的頻率392Hz #define l_la 440 //將“l_a”宏定義為低音“6”的頻率440Hz #define l_xi 494 //將“l_xi”宏定義為低音“7”的頻率494Hz //以下是C調(diào)中音的音頻宏定義 #define dao 523 //將“dao”宏定義為中音“1”的頻率523Hz #define re 587 //將“re”宏定義為中音“2”的頻率587Hz #define mi 659 //將“mi”宏定義為中音“3”的頻率659Hz #define fa 698 //將“fa”宏定義為中音“4”的頻率698Hz #define sao 784 //將“sao”宏定義為中音“5”的頻率784Hz #define la 880 //將“la”宏定義為中音“6”的頻率880Hz #define xi 987 //將“xi”宏定義為中音“7”的頻率523Hz //以下是C調(diào)高音的音頻宏定義 #define h_dao 1046 //將“h_dao”宏定義為高音“1”的頻率1046Hz #define h_re 1174 //將“h_re”宏定義為高音“2”的頻率1174Hz #define h_mi 1318 //將“h_mi”宏定義為高音“3”的頻率1318Hz #define h_fa 1396 //將“h_fa”宏定義為高音“4”的頻率1396Hz #define h_sao 1567 //將“h_sao”宏定義為高音“5”的頻率1567Hz #define h_la 1760 //將“h_la”宏定義為高音“6”的頻率1760Hz #define h_xi 1975 //將“h_xi”宏定義為高音“7”的頻率1975Hz /******************************************* 函數(shù)功能:節(jié)拍的延時(shí)的基本單位,延時(shí)200ms ******************************************/ void delay() { unsigned char i,j; for(i=0;i<250;i++) for(j=0;j<250;j++) ; } /************************************************************************** 以下是對(duì)AT24C02進(jìn)行讀寫(xiě)操作的源程序 *************************************************************************/ /***************************************************** 函數(shù)功能:延時(shí)1ms (3j+2)*i=(3×33+2)×10=1010(微秒),可以認(rèn)為是1毫秒 ***************************************************/ void delay1ms() { unsigned char i,j; for(i=0;i<10;i++) for(j=0;j<33;j++) ; } /***************************************************** 函數(shù)功能:延時(shí)若干毫秒 入口參數(shù):n ***************************************************/ void delaynms(unsigned char n) { unsigned char i; for(i=0;i<n;i++) delay1ms(); } /*************************************************** 函數(shù)功能:開(kāi)始數(shù)據(jù)傳送 ***************************************************/ void start() { SDA = 1; //SDA初始化為高電平"1" SCL = 1; //開(kāi)始數(shù)據(jù)傳送時(shí),要求SCL為高電平"1" _nop_(); //等待一個(gè)機(jī)器周期 _nop_(); //等待一個(gè)機(jī)器周期 SDA = 0; //SDA的下降沿被認(rèn)為是開(kāi)始信號(hào) _nop_(); //等待一個(gè)機(jī)器周期 _nop_(); //等待一個(gè)機(jī)器周期 _nop_(); //等待一個(gè)機(jī)器周期 _nop_(); //等待一個(gè)機(jī)器周期 SCL = 0; //SCL為低電平時(shí),SDA上數(shù)據(jù)才允許變化(即允許以后的數(shù)據(jù)傳遞) } /*************************************************** 函數(shù)功能:結(jié)束數(shù)據(jù)傳送 ***************************************************/ void stop() { SDA = 0; //SDA初始化為低電平"0" _nop_(); //等待一個(gè)機(jī)器周期 _nop_(); //等待一個(gè)機(jī)器周期 SCL = 1; //結(jié)束數(shù)據(jù)傳送時(shí),要求SCL為高電平"1" _nop_(); //等待一個(gè)機(jī)器周期 _nop_(); //等待一個(gè)機(jī)器周期 _nop_(); //等待一個(gè)機(jī)器周期 _nop_(); //等待一個(gè)機(jī)器周期 SDA = 1; //SDA的上升沿被認(rèn)為是結(jié)束信號(hào) } /*************************************************** 函數(shù)功能:從AT24Cxx讀取數(shù)據(jù) 出口參數(shù):x ***************************************************/ unsigned char ReadData() { unsigned char i; unsigned char x; //儲(chǔ)存從AT24Cxx中讀出的數(shù)據(jù) for(i = 0;i < 8;i++) { SCL = 1; //SCL置為高電平 x<<=1; //將x中的各二進(jìn)位向左移一位 x|=(unsigned char)SDA; //將SDA上的數(shù)據(jù)通過(guò)按位"或"運(yùn)算存入x中 SCL = 0; //在SCL的下降沿讀出數(shù)據(jù) } return(x); //將讀取的數(shù)據(jù)返回 } /*************************************************** 函數(shù)功能:向AT24Cxx的當(dāng)前地址寫(xiě)入數(shù)據(jù) 入口參數(shù):y (儲(chǔ)存待寫(xiě)入的數(shù)據(jù)) ***************************************************/ //在調(diào)用此數(shù)據(jù)寫(xiě)入函數(shù)前需首先調(diào)用開(kāi)始函數(shù)start(),所以SCL=0 bit WriteCurrent(unsigned char y) { unsigned char i; bit ack_bit; //儲(chǔ)存應(yīng)答位 for(i = 0; i < 8; i++) // 循環(huán)移入8個(gè)位 { SDA = (bit)(y&0x80); //通過(guò)按位"與"運(yùn)算將最高位數(shù)據(jù)送到S //因?yàn)閭魉蜁r(shí)高位在前,低位在后 _nop_(); //等待一個(gè)機(jī)器周期 SCL = 1; //在SCL的上升沿將數(shù)據(jù)寫(xiě)入AT24Cxx _nop_(); //等待一個(gè)機(jī)器周期 _nop_(); //等待一個(gè)機(jī)器周期 SCL = 0; //將SCL重新置為低電平,以在SCL線形成傳送數(shù)據(jù)所需的8?jìng)€(gè)脈沖 y <<= 1; //將y中的各二進(jìn)位向左移一位 } SDA = 1; // 發(fā)送設(shè)備(主機(jī))應(yīng)在時(shí)鐘脈沖的高電平期間(SCL=1)釋放SDA線, //以讓SDA線轉(zhuǎn)由接收設(shè)備(AT24Cxx)控制 _nop_(); //等待一個(gè)機(jī)器周期 _nop_(); //等待一個(gè)機(jī)器周期 SCL = 1; //根據(jù)上述規(guī)定,SCL應(yīng)為高電平 _nop_(); //等待一個(gè)機(jī)器周期 _nop_(); //等待一個(gè)機(jī)器周期 _nop_(); //等待一個(gè)機(jī)器周期 _nop_(); //等待一個(gè)機(jī)器周期 ack_bit = SDA; //接受設(shè)備(AT24Cxx)向SDA送低電平,表示已經(jīng)接收到一個(gè)字節(jié) //若送高電平,表示沒(méi)有接收到,傳送異常 SCL = 0; //SCL為低電平時(shí),SDA上數(shù)據(jù)才允許變化(即允許以后的數(shù)據(jù)傳遞) return ack_bit;// 返回AT24Cxx應(yīng)答位 } /*************************************************** 函數(shù)功能:向AT24Cxx中的指定地址寫(xiě)入數(shù)據(jù) 入口參數(shù):add (儲(chǔ)存指定的地址);dat(儲(chǔ)存待寫(xiě)入的數(shù)據(jù)) ***************************************************/ void WriteSet(unsigned char add, unsigned char dat) { start(); //開(kāi)始數(shù)據(jù)傳遞 WriteCurrent(OP_WRITE); //選擇要操作的AT24Cxx芯片,并告知要對(duì)其寫(xiě)入數(shù)據(jù) WriteCurrent(add); //寫(xiě)入指定地址 WriteCurrent(dat); //向當(dāng)前地址(上面指定的地址)寫(xiě)入數(shù)據(jù) stop(); //停止數(shù)據(jù)傳遞 delaynms(4); //1個(gè)字節(jié)的寫(xiě)入周期為1ms, 最好延時(shí)1ms以上 } /*************************************************** 函數(shù)功能:從AT24Cxx中的當(dāng)前地址讀取數(shù)據(jù) 出口參數(shù):x (儲(chǔ)存讀出的數(shù)據(jù)) ***************************************************/ unsigned char ReadCurrent() { unsigned char x; start(); //開(kāi)始數(shù)據(jù)傳遞 WriteCurrent(OP_READ); //選擇要操作的AT24Cxx芯片,并告知要讀其數(shù)據(jù) x=ReadData(); //將讀取的數(shù)據(jù)存入x stop(); //停止數(shù)據(jù)傳遞 return x; //返回讀取的數(shù)據(jù) } /*************************************************** 函數(shù)功能:從AT24Cxx中的指定地址讀取數(shù)據(jù) 入口參數(shù):set_addr 出口參數(shù):x ***************************************************/ unsigned char ReadSet(unsigned char set_addr) { start(); //開(kāi)始數(shù)據(jù)傳遞 WriteCurrent(OP_WRITE); //選擇要操作的AT24Cxx芯片,并告知要對(duì)其寫(xiě)入數(shù)據(jù) WriteCurrent(set_addr); //寫(xiě)入指定地址 return(ReadCurrent()); //從指定地址讀出數(shù)據(jù)并返回 } /*************************************************** 函數(shù)功能:主函數(shù) ***************************************************/ main(void) { unsigned char i,j; unsigned char temp; //儲(chǔ)存壓縮后的音頻 unsigned char Ji; //儲(chǔ)存音符節(jié)拍 unsigned char N; //儲(chǔ)存音符的最大個(gè)數(shù)以在AT24C02中為音符和節(jié)拍分配存儲(chǔ)空間 unsigned int fr; //儲(chǔ)存解壓縮后的音頻 //以下是《渴望》片頭曲的一段簡(jiǎn)譜 unsigned int code f[]={re,mi,re,dao,l_la,dao,l_la, l_sao,l_mi,l_sao,l_la,dao, l_la,dao,sao,la,mi,sao, re, mi,re,mi,sao,mi, l_sao,l_mi,l_sao,l_la,dao, l_la,l_la,dao,l_la,l_sao,l_re,l_mi, l_sao, re,re,sao,la,sao, fa,mi,sao,mi, la,sao,mi,re,mi,l_la,dao, re, mi,re,mi,sao,mi, l_sao,l_mi,l_sao,l_la,dao, l_la,dao,re,l_la,dao,re,mi, re, l_la,dao,re,l_la,dao,re,mi, re, 0x00}; //以頻率0x00作為簡(jiǎn)譜的結(jié)束標(biāo)志 //以下是簡(jiǎn)譜中每個(gè)音符的節(jié)拍 unsigned char code JP[ ]={4,1,1,4,1,1,2, 2,2,2,2,8, 4,2,3,1,2,2, 10, 4,2,2,4,4, 2,2,2,2,4, 2,2,2,2,2,2,2, 10, 4,4,4,2,2, 4,2,4,4, 4,2,2,2,2,2,2, 10, 4,2,2,4,4, 2,2,2,2,6, 4,2,2,4,1,1,4, 10, 4,2,2,4,1,1,4, 10 }; EA=1; //開(kāi)總中斷 ET0=1; //定時(shí)器T0中斷允許 TMOD=0x00; // 使用定時(shí)器T0的模式1(13位計(jì)數(shù)器) SDA = 1; // SDA=1,SCL=1,使主從設(shè)備處于空閑狀態(tài) SCL = 1; while(1) //無(wú)限循環(huán) { i=0; //從第1個(gè)音符頻率f[0]開(kāi)始寫(xiě)入AT24C02 while(f[i]!=0x01) //只要沒(méi)有讀到結(jié)束標(biāo)志就繼續(xù)寫(xiě)入 { temp=(unsigned char)(f[i]/8); //將音頻壓縮為較小的字符變量 WriteSet(0x00+i,temp); //在指定地址寫(xiě)入數(shù)據(jù)壓縮后的音頻 i++; //指向下一個(gè)音符音頻 } N=i; //將音符的最大個(gè)數(shù)存于N i=0; //從第一個(gè)音符節(jié)拍JP[0]開(kāi)始寫(xiě)入AT24C02 while(f[i]!=0x00) { WriteSet(0x00+N+i,JP[i]); //在指定地址寫(xiě)入音符的節(jié)拍 i++; //指向下一個(gè)音符音頻 } for(i=0;i<N;i++) { temp=ReadSet(0x00+i); //讀出音頻 Ji=ReadSet(0x00+N+i); //讀出節(jié)拍 fr=8*temp; //將音頻解壓 C=460830/fr; //定時(shí)常數(shù)的計(jì)算公式 TH0=(8192-C)/32; //可證明這是13位計(jì)數(shù)器TH0高8位的賦初值方法 TL0=(8192-C)%32; //可證明這是13位計(jì)數(shù)器TL0低5位的賦初值方法 TR0=1; //啟動(dòng)定時(shí)器T0 for(j=0;j<Ji;j++) //控制節(jié)拍數(shù) delay(); //延時(shí)1個(gè)節(jié)拍單位 TR0=0; //關(guān)閉定時(shí)器T0 } sound=1; //播放完畢后,關(guān)閉蜂鳴器 for(i=0;i<8;i++) //播放完畢后,停頓一段時(shí)間后繼續(xù)播放 delay(); } } ///////////////////////// /*********************************************************** 函數(shù)功能:定時(shí)器T0的中斷服務(wù)子程序,使P3.7引腳輸出音頻的方波 ************************************************************/ void Time0(void ) interrupt 1 using 1 { TH0=(8192-C)/32; //可證明這是13位計(jì)數(shù)器TH0高8位的賦初值方法 TL0=(8192-C)%32; //可證明這是13位計(jì)數(shù)器TL0低5位的賦初值方法 sound=!sound; //將P3.7引腳輸出電平取反,形成方波 } |
|
|