//实例89:将"渴望"乐谱写入AT24C02并读出播放
#include <reg51.h> // 包含51单片机寄存器定义的头文件 #include <intrins.h> //包含_nop_()函数定义的头文件 #define OP_READ 0xa1 // 器件地址以及读取操作,0xa1即为1010 0001B #define OP_WRITE 0xa0 // 器件地址以及写入操作,0xa1即为1010 0000B sbit SDA=P3^4; //将串行数据总线SDA位定义在为P3.4引脚 sbit SCL=P3^3; //将串行时钟总线SDA位定义在为P3.3引脚 sbit sound=P3^7; //将sound位定义为P3.7,从该引脚输出音频 unsigned int C; //储存定时器的定时常数 //以下是C调低音的音频宏定义
#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调中音的音频宏定义 #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调高音的音频宏定义 #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 /******************************************* 函数功能:节拍的延时的基本单位,延时200ms ******************************************/ void delay() { unsigned char i,j; for(i=0;i<250;i++) for(j=0;j<250;j++) ; } /************************************************************************** 以下是对AT24C02进行读写操作的源程序 *************************************************************************/ /***************************************************** 函数功能:延时1ms (3j+2)*i=(3×33+2)×10=1010(微秒),可以认为是1毫秒 ***************************************************/ void delay1ms() { unsigned char i,j; for(i=0;i<10;i++) for(j=0;j<33;j++) ; } /***************************************************** 函数功能:延时若干毫秒 入口参数:n ***************************************************/ void delaynms(unsigned char n) { unsigned char i; for(i=0;i<n;i++) delay1ms(); } /*************************************************** 函数功能:开始数据传送 ***************************************************/ void start() { SDA = 1; //SDA初始化为高电平"1" SCL = 1; //开始数据传送时,要求SCL为高电平"1" _nop_(); //等待一个机器周期 _nop_(); //等待一个机器周期 SDA = 0; //SDA的下降沿被认为是开始信号 _nop_(); //等待一个机器周期 _nop_(); //等待一个机器周期 _nop_(); //等待一个机器周期 _nop_(); //等待一个机器周期 SCL = 0; //SCL为低电平时,SDA上数据才允许变化(即允许以后的数据传递) } /*************************************************** 函数功能:结束数据传送 ***************************************************/ void stop() { SDA = 0; //SDA初始化为低电平"0" _nop_(); //等待一个机器周期 _nop_(); //等待一个机器周期 SCL = 1; //结束数据传送时,要求SCL为高电平"1" _nop_(); //等待一个机器周期 _nop_(); //等待一个机器周期 _nop_(); //等待一个机器周期 _nop_(); //等待一个机器周期 SDA = 1; //SDA的上升沿被认为是结束信号 } /*************************************************** 函数功能:从AT24Cxx读取数据 出口参数:x ***************************************************/ unsigned char ReadData() { unsigned char i; unsigned char x; //储存从AT24Cxx中读出的数据 for(i = 0;i < 8;i++) { SCL = 1; //SCL置为高电平 x<<=1; //将x中的各二进位向左移一位 x|=(unsigned char)SDA; //将SDA上的数据通过按位"或"运算存入x中 SCL = 0; //在SCL的下降沿读出数据 } return(x); //将读取的数据返回 } /*************************************************** 函数功能:向AT24Cxx的当前地址写入数据 入口参数:y (储存待写入的数据) ***************************************************/ //在调用此数据写入函数前需首先调用开始函数start(),所以SCL=0 bit WriteCurrent(unsigned char y) { unsigned char i; bit ack_bit; //储存应答位 for(i = 0; i < 8; i++) // 循环移入8个位 { SDA = (bit)(y&0x80); //通过按位"与"运算将最高位数据送到S //因为传送时高位在前,低位在后 _nop_(); //等待一个机器周期 SCL = 1; //在SCL的上升沿将数据写入AT24Cxx _nop_(); //等待一个机器周期 _nop_(); //等待一个机器周期 SCL = 0; //将SCL重新置为低电平,以在SCL线形成传送数据所需的8个脉冲 y <<= 1; //将y中的各二进位向左移一位 } SDA = 1; // 发送设备(主机)应在时钟脉冲的高电平期间(SCL=1)释放SDA线, //以让SDA线转由接收设备(AT24Cxx)控制 _nop_(); //等待一个机器周期 _nop_(); //等待一个机器周期 SCL = 1; //根据上述规定,SCL应为高电平 _nop_(); //等待一个机器周期 _nop_(); //等待一个机器周期 _nop_(); //等待一个机器周期 _nop_(); //等待一个机器周期 ack_bit = SDA; //接受设备(AT24Cxx)向SDA送低电平,表示已经接收到一个字节 //若送高电平,表示没有接收到,传送异常 SCL = 0; //SCL为低电平时,SDA上数据才允许变化(即允许以后的数据传递) return ack_bit;// 返回AT24Cxx应答位 } /*************************************************** 函数功能:向AT24Cxx中的指定地址写入数据 入口参数:add (储存指定的地址);dat(储存待写入的数据) ***************************************************/ void WriteSet(unsigned char add, unsigned char dat) { start(); //开始数据传递 WriteCurrent(OP_WRITE); //选择要操作的AT24Cxx芯片,并告知要对其写入数据 WriteCurrent(add); //写入指定地址 WriteCurrent(dat); //向当前地址(上面指定的地址)写入数据 stop(); //停止数据传递 delaynms(4); //1个字节的写入周期为1ms, 最好延时1ms以上 } /*************************************************** 函数功能:从AT24Cxx中的当前地址读取数据 出口参数:x (储存读出的数据) ***************************************************/ unsigned char ReadCurrent() { unsigned char x; start(); //开始数据传递 WriteCurrent(OP_READ); //选择要操作的AT24Cxx芯片,并告知要读其数据 x=ReadData(); //将读取的数据存入x stop(); //停止数据传递 return x; //返回读取的数据 } /*************************************************** 函数功能:从AT24Cxx中的指定地址读取数据 入口参数:set_addr 出口参数:x ***************************************************/ unsigned char ReadSet(unsigned char set_addr) { start(); //开始数据传递 WriteCurrent(OP_WRITE); //选择要操作的AT24Cxx芯片,并告知要对其写入数据 WriteCurrent(set_addr); //写入指定地址 return(ReadCurrent()); //从指定地址读出数据并返回 } /*************************************************** 函数功能:主函数 ***************************************************/ main(void) { unsigned char i,j; unsigned char temp; //储存压缩后的音频 unsigned char Ji; //储存音符节拍 unsigned char N; //储存音符的最大个数以在AT24C02中为音符和节拍分配存储空间 unsigned int fr; //储存解压缩后的音频 //以下是《渴望》片头曲的一段简谱 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作为简谱的结束标志 //以下是简谱中每个音符的节拍 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; //开总中断 ET0=1; //定时器T0中断允许 TMOD=0x00; // 使用定时器T0的模式1(13位计数器) SDA = 1; // SDA=1,SCL=1,使主从设备处于空闲状态 SCL = 1; while(1) //无限循环 { i=0; //从第1个音符频率f[0]开始写入AT24C02 while(f[i]!=0x01) //只要没有读到结束标志就继续写入 { temp=(unsigned char)(f[i]/8); //将音频压缩为较小的字符变量 WriteSet(0x00+i,temp); //在指定地址写入数据压缩后的音频 i++; //指向下一个音符音频 } N=i; //将音符的最大个数存于N i=0; //从第一个音符节拍JP[0]开始写入AT24C02 while(f[i]!=0x00) { WriteSet(0x00+N+i,JP[i]); //在指定地址写入音符的节拍 i++; //指向下一个音符音频 } for(i=0;i<N;i++) { temp=ReadSet(0x00+i); //读出音频 Ji=ReadSet(0x00+N+i); //读出节拍 fr=8*temp; //将音频解压 C=460830/fr; //定时常数的计算公式 TH0=(8192-C)/32; //可证明这是13位计数器TH0高8位的赋初值方法 TL0=(8192-C)%32; //可证明这是13位计数器TL0低5位的赋初值方法 TR0=1; //启动定时器T0 for(j=0;j<Ji;j++) //控制节拍数 delay(); //延时1个节拍单位 TR0=0; //关闭定时器T0 } sound=1; //播放完毕后,关闭蜂鸣器 for(i=0;i<8;i++) //播放完毕后,停顿一段时间后继续播放 delay(); } } ///////////////////////// /*********************************************************** 函数功能:定时器T0的中断服务子程序,使P3.7引脚输出音频的方波 ************************************************************/ void Time0(void ) interrupt 1 using 1 { TH0=(8192-C)/32; //可证明这是13位计数器TH0高8位的赋初值方法 TL0=(8192-C)%32; //可证明这是13位计数器TL0低5位的赋初值方法 sound=!sound; //将P3.7引脚输出电平取反,形成方波 } |
|