找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2417|回复: 3
收起左侧

基于STC12C5A60S2的带有掉电记忆功能的音乐盒

[复制链接]
ID:475984 发表于 2019-2-12 18:11 | 显示全部楼层 |阅读模式
晶振值33.1776MHz
功能简介:K16控制暂停与继续
          K4 上一曲
     K12下一曲
    数码管显示当前歌曲序号

具有lcd1602显示功能

应用EEPROM具有掉电记忆
默认演奏上次断电所演奏的歌曲
主函数
  1. #include <stc12c5a60s2.h>
  2. #define Xtal 33177600  //赋晶振值
  3. #define All 4          //歌曲总数


  4. sbit BUZZ = P1^3;  //蜂鸣器控制引脚
  5. sbit KEY = P2^4;   //按键定义
  6. sbit KEY4 = P2^7;
  7. sbit KEY12 = P2^5;
  8. /*sbit ADDR0 = P2^0; //数码管显示控制
  9. sbit ADDR1 = P2^1;
  10. sbit ADDR2 = P2^2;
  11. sbit ENLED = P1^1; //总线收发器*/

  12. bit KeySta = 1;  //当前按键状态
  13. bit KeySta4 = 1;  
  14. bit KeySta12 = 1;
  15. bit backup = 1;  //默认弹起时时一
  16. bit backup4 = 1;
  17. bit backup12 = 1;
  18. bit cntkey = 0;  //判断暂停还是播放
  19. bit breakflage = 0;//用按键改变歌曲标志

  20. unsigned char keybuf = 0xFF;
  21. unsigned char keybuf4 = 0xFF;
  22. unsigned char keybuf12 = 0xFF;
  23. unsigned char Num; //第几首歌
  24. unsigned char sizeofNote;//节拍的多少

  25. void E2WriteByte(unsigned char addr, unsigned char dat);
  26. unsigned char E2ReadByte(unsigned char addr);
  27. void Delay_ms(unsigned int ms);//延时函数
  28. extern void I2CStart();
  29. extern void I2CStop();
  30. extern unsigned char I2CReadNAK();
  31. extern bit I2CWrite(unsigned char dat);
  32. extern void LcdWriteCmd(unsigned char cmd);
  33. extern void LcdWaitReady();
  34. extern void InitLcd1602();
  35. extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);

  36. void lcd1602updata();
  37. void PlayControl(unsigned char Num);//歌曲选择函数
  38. void Play();//演奏函数

  39. //unsigned char code LedChar[] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E};//数码管显示字符转换表
  40. unsigned int code NoteFrequ[] = {  //中音1-7低音1-7和高音1-7对应频率列表
  41.     523,  587,  659,  698,  784,  880,  988,  //中音1-7
  42.     1047, 1175, 1319, 1397, 1568, 1760, 1976,  //高音1-7
  43.           262, 294, 330, 349, 392, 440, 494,300//低音1-7  最后一个纯属凑数
  44. };
  45. unsigned int code NoteReload[] = { //中音1-7和高音1-7对应的定时器重载值
  46.     65536 - (Xtal/12) / (523*2),  //中音1
  47.     65536 - (Xtal/12) / (587*2),  //2
  48.     65536 - (Xtal/12) / (659*2),  //3
  49.     65536 - (Xtal/12) / (698*2),  //4
  50.     65536 - (Xtal/12) / (784*2),  //5
  51.     65536 - (Xtal/12) / (880*2),  //6
  52.     65536 - (Xtal/12) / (988*2),  //7
  53.     65536 - (Xtal/12) / (1047*2), //高音1
  54.     65536 - (Xtal/12) / (1175*2), //2
  55.     65536 - (Xtal/12) / (1319*2), //3
  56.     65536 - (Xtal/12) / (1397*2), //4
  57.     65536 - (Xtal/12) / (1568*2), //5
  58.     65536 - (Xtal/12) / (1760*2), //6
  59.     65536 - (Xtal/12) / (1976*2), //7
  60.           65536 - (Xtal/12) / (262*2), //低音1
  61.     65536 - (Xtal/12) / (294*2), //2
  62.     65536 - (Xtal/12) / (330*2), //3
  63.     65536 - (Xtal/12) / (349*2), //4
  64.     65536 - (Xtal/12) / (392*2), //5
  65.     65536 - (Xtal/12) / (440*2), //6
  66.     65536 - (Xtal/12) / (494*2), //7
  67.                 65536 - (Xtal/12) / (300*2), //纯属凑数
  68. };

  69. unsigned char *SongNote, *SongBeat;//指针
  70. unsigned char code XiaoXingXingNote[] = {   //小星星
  71.         1, 1, 5, 5,    6, 6, 5,    4, 4, 3, 3,    2, 2, 1,  
  72.         5, 5, 4, 4,    3, 3, 2,    5, 5, 4, 4,    3, 3, 2 ,
  73.         1, 1, 5, 5,    6, 6, 5,    4, 4, 3, 3,    2, 2, 1, 0xff    };

  74. unsigned char code XiaoXingXingBeat[] = {
  75.         4, 4, 4, 4,    4, 4, 8,    4, 4, 4, 4,    4, 4, 8,
  76.         4, 4, 4, 4,    4, 4, 8,    4, 4, 4, 4,    4, 4, 8,
  77.         4, 4, 4, 4,    4, 4, 8,    4, 4, 4, 4,    4, 4, 8,};
  78. unsigned char code TwoTigerNote[] = {    //两只老虎音符表
  79.         1,   2,   3, 1,    1,   2,   3, 1,   3, 4, 5,   3, 4, 5,
  80.         5,6, 5,4, 3, 1,    5,6, 5,4, 3, 1,   1, 5, 1,   1, 5, 1,0xff};

  81. unsigned char code TwoTigerBeat[] = {    //两只老虎节拍表,4表示一拍,1就是1/4拍,8就是2拍
  82.         4,   4,   4, 4,    4,   4,   4, 4,   4, 4, 8,   4, 4, 8,
  83.         3,1, 3,1, 4, 4,    3,1, 3,1, 4, 4,   4, 4, 8,   4, 4, 8,
  84.     };
  85. unsigned char code HappybirthdayNote[] = {  //生日快乐歌
  86.         5, 5, 6, 5, 8, 7, 22, 5, 5, 6, 5, 9, 8, 5, 5, 12, 10, 8, 7, 6, 11, 11,  10, 8, 9, 8,0xff };
  87. unsigned char code HappybirthdayBeat[] = {
  88.         2, 2, 4, 4, 4, 4, 4, 2, 2,  4, 4, 4, 8, 2, 2, 4, 4, 4, 4, 8, 2, 2, 4, 4, 4, 8};       
  89. unsigned char code         ZhiduanqingchangNote[] = { //纸短情长
  90.               //8, 9, 10, 9, 8, 6, 22, 11, 10, 9, 22, 7, 8, 9, 7, 7, 5, 22, 7, 9, 8, 8, 3, 4, 2, 3, 1, 1, 5, 2,  //前奏
  91.         19, 3, 3, 3, 2, 3, 1, 2, 2, 2, 1, 2, 5, 1, 1, 1, 20, 1, 20, 19,     2, 2, 2, 3, 20, 19, 4, 4, 4, 3, 4, 1, 2, 2, 2, 1, 2, 5,
  92.                1, 1, 1, 20, 1, 20, 3, 2, 2, 1, 1, 22, 22, 22, 1, 1, 2,             3, 3, 3, 22, 3, 2, 1, 21, 6, 5, 22, 5, 6, 7, 8, 3, 3, 22, 8, 7, 8,
  93.               7, 3, 5, 5, 6, 8, 5, 6, 22, 6, 5, 4,5, 3, 2, 1, 20, 1,               3, 2, 2, 1, 2, 5, 1, 2, 5, 22, 22, 22, 1, 1, 2,
  94.         3, 3, 2, 3, 22, 3, 2, 1, 5, 5, 3, 5, 22, 5, 6, 7,                   8, 8, 8, 8, 8, 7, 6, 7, 6, 3, 5, 5, 5, 6, 8, 5, 6, 22, 6, 5, 4,
  95.         5, 3, 2, 1, 6, 1, 3, 2, 2, 1, 2, 1, 20, 1, 22,                      22, 20, 1, 3, 2, 2, 22, 2, 1, 2, 1, 1, 22, 20, 1, 1, 0xff};       
  96. unsigned char code ZhiduanqingchangBeat[] = {
  97.               //2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 8, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 8, 8, 12, 4, 5, 4, 8, 8, 4,   //前奏
  98.         8, 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 2, 4, 2,       2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 4, 5, 2, 2, 2, 2, 2, 5,
  99.         2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 8, 4, 4, 2, 2, 2, 2,                4, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2,
  100.         4, 2, 5, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 4, 2, 5, 1, 1,             2, 2, 2, 2, 2, 4, 2, 16, 16,4, 4, 2, 2, 2, 2,
  101.         2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,                   2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 2, 4, 2, 2, 2, 4, 2, 2, 2, 2, 2,
  102.         2, 4, 2, 5, 1, 1, 2, 2, 2, 2, 2, 4, 1, 12, 4,                     2, 1, 1, 2, 2, 8, 2, 1, 1, 2, 2, 8, 2, 2, 12, 16};
  103. bit enable = 1;   //蜂鸣器发声使能标志
  104. bit tmrflag = 0;  //定时器中断完成标志
  105. unsigned char T0RH = 0xFF;  //T0重载值的高字节
  106. unsigned char T0RL = 0x00;  //T0重载值的低字节

  107. void main()
  108. {
  109.     unsigned char dat;
  110.     EA = 1;       //使能全局中断
  111.     TMOD = 0x11;  //配置T0,T1工作在模式1
  112.     TH0 = T0RH;   
  113.     TL0 = T0RL;
  114.     ET0 = 1;      //使能T0中断
  115.           ET1 = 1;      //使能T1中断
  116.           TR1 = 1;      //启动T1
  117.          // ENLED = 0x00; //使能总线收发器
  118.          // ADDR2=1; ADDR1=0; ADDR0=1;//选择最右边的数码管
  119.           P23 = 0;      //启用KEY
  120.           InitLcd1602();
  121.           dat = E2ReadByte(0x01);    //读取指定地址上的一个字节
  122.           if(dat>All||dat<=0)
  123.                         Num = 1;
  124.                 else
  125.     Num = dat;
  126.     while (1)
  127.     {
  128.                         //P0 = LedChar[Num]; //数码管显示当前歌曲序号
  129.                         LcdWriteCmd(0x01);  //清屏
  130.                         lcd1602updata();
  131.       PlayControl(Num);                       
  132.                         if(breakflage == 1)
  133.                                 breakflage = 0;
  134.                         else
  135.                                 Delay_ms(500);
  136.     }
  137. }

  138. void PlayControl(unsigned char Num)
  139. {
  140.         E2WriteByte(0x01, Num);    //再写回到对应的地址上
  141.         switch(Num)
  142.         {
  143.                 case 4:SongNote = XiaoXingXingNote;    SongBeat =  XiaoXingXingBeat;  sizeofNote = sizeof(XiaoXingXingNote);    Play();break;
  144.     case 2:SongNote = TwoTigerNote;        SongBeat =TwoTigerBeat;        sizeofNote = sizeof(TwoTigerNote);        Play();break;
  145.                 case 3:SongNote = HappybirthdayNote;   SongBeat =HappybirthdayBeat;   sizeofNote = sizeof(HappybirthdayNote);   Play();break;
  146.                 case 1:SongNote = ZhiduanqingchangNote;SongBeat =ZhiduanqingchangBeat;sizeofNote = sizeof(ZhiduanqingchangNote);Play();break;               
  147.                 default:break;
  148.         }
  149. }
  150. void lcd1602updata()
  151. {
  152.         switch(Num)
  153.         {
  154.                 case 1:LcdShowStr(4,0,"1.zhiduanqingchang");break;
  155.           case 2:LcdShowStr(4,0,"2.liangzhilaohu");break;
  156.           case 3:LcdShowStr(4,0,"3.zhunishengrikuaile");break;
  157.           case 4:LcdShowStr(4,0,"4.xiaoxingxing");break;
  158.                 default:break;
  159.         }
  160. }
  161. /* 演奏函数 */
  162. void Play()//演奏函数
  163. {
  164.     unsigned char beat;   //当前节拍索引
  165.     unsigned char note;   //当前节拍对应的音符
  166.     unsigned int time = 0;      //当前节拍计时
  167.     unsigned int beatTime = 0;  //当前节拍总时间
  168.     unsigned int soundTime = 0; //当前节拍需发声时间

  169.     for (beat=0; beat<sizeofNote; )  //用节拍索引作为循环变量
  170.     {
  171.                                 if(breakflage == 1)
  172.                                         break;
  173.         while (!tmrflag);  //每次定时器中断完成后,检测并处理节拍
  174.         tmrflag = 0;
  175.         if (time == 0)  //当前节拍播完则启动一个新节拍
  176.         {
  177.                                         if(breakflage == 1)
  178.                                                 break;
  179.             note = SongNote[beat] - 1;
  180.                                           if(note == 0xfe)
  181.                                                 {
  182.                                                         if(Num == All)
  183.                                                                 Num = 1;
  184.                                                         else
  185.                                                         Num++;
  186.                                                         break;
  187.                                                 }
  188.             T0RH = NoteReload[note] >> 8;
  189.             T0RL = NoteReload[note];
  190.             beatTime = (SongBeat[beat] * NoteFrequ[note]) >> 2;//计算节拍总时间,右移2位相当于除4,移位代替除法可以加快执行速度
  191.             soundTime = beatTime - (beatTime >> 2);//计算发声时间,
  192.             enable = 1;  //指示蜂鸣器开始发声
  193.                                                 if (note == 21)//简谱的0 等待
  194.                                                         enable =0;
  195.             time++;
  196.         }
  197.         else  //当前节拍未播完则处理当前节拍
  198.         {
  199.             if (time >= beatTime)  //当前持续时间到达节拍总时间时归零,
  200.             {                      //并递增节拍索引,以准备启动新节拍
  201.                 time = 0;
  202.                 beat++;
  203.             }
  204.             else  //当前持续时间未达到总时间时,
  205.             {
  206.                 time++;   //累加时间计数
  207.                 if (time == soundTime)  //到达发声时间后,指示关闭蜂鸣器,
  208.                 {                       //插入0.25*总时间的静音间隔,
  209.                     enable = 0;         //用以区分连续的两个节拍
  210.                 }
  211.             }
  212.         }
  213.     }
  214. }
  215. void Delay_ms(unsigned int ms)
  216. {
  217.         for(;ms > 0;ms--)
  218.         {
  219.         unsigned char i, j;

  220.         i = 33;
  221.         j = 66;
  222.         do
  223.         {
  224.                 while (--j);
  225.         } while (--i);
  226.   }
  227. }
  228. unsigned char E2ReadByte(unsigned char addr)
  229. {
  230.         unsigned char dat;

  231.         I2CStart();
  232.         I2CWrite(0x51<<1);//发送E2PROM地址
  233.         I2CWrite(addr);//要读取的地址
  234.         I2CStart();        //重新发送起始信号
  235.         I2CWrite((0x51<<1) |0x01);
  236.         dat = I2CReadNAK();
  237.         I2CStop();

  238.         return dat;
  239. }
  240. void E2WriteByte(unsigned char addr, unsigned char dat)
  241. {
  242.         I2CStart();
  243.         I2CWrite(0x51<<1);//发送E2PROM地址
  244.         I2CWrite(addr);//要写入的地址
  245.         I2CWrite(dat);//要写入的数据
  246.         I2CStop();
  247. }
  248. /*控制蜂鸣器发声 */
  249. void InterruptTimer0() interrupt 1
  250. {
  251.     TH0 = T0RH;   //重新加载重载值
  252.     TL0 = T0RL;
  253.     tmrflag = 1;
  254.     if (enable)   //使能时反转蜂鸣器控制电平
  255.         BUZZ = ~BUZZ;
  256.     else          //未使能时关闭蜂鸣器
  257.         BUZZ = 1;
  258. }
  259. /*按键扫描*/
  260. void InterruptTimer1() interrupt 3 //按键部分参看之前的帖子 带有计次功能的秒表
  261. {
  262.         TH1 = 0xC9;
  263.         TL1 = 0xEA;
  264.        
  265.           keybuf = (keybuf<<1) | KEY;
  266.     if (keybuf == 0x00)
  267.         KeySta = 0;
  268.     else if (keybuf == 0xFF)
  269.         KeySta = 1;
  270.     else{}
  271.                        
  272.           keybuf4 = (keybuf4<<1) | KEY4;
  273.     if (keybuf4 == 0x00)
  274.         KeySta4 = 0;
  275.     else if (keybuf4 == 0xFF)
  276.         KeySta4 = 1;
  277.     else{}
  278.                        
  279.          keybuf12= (keybuf12<<1) | KEY12;
  280.     if (keybuf12 == 0x00)
  281.         KeySta12 = 0;
  282.     else if (keybuf12 == 0xFF)
  283.         KeySta12 = 1;
  284.     else{}
  285.                        
  286.                               if (KeySta != backup)  
  287.         {
  288.             if (backup == 0)        
  289.                                                 {
  290.                                                         cntkey=~cntkey;
  291.                                                         TR0 = cntkey;
  292.                                                 }
  293.             backup = KeySta;               
  294.         }
  295.                 if (KeySta4 != backup4)
  296.                 {
  297.                         if(backup4 ==0)
  298.                         {
  299.                                 breakflage = 1;
  300.                                 if(Num <All)
  301.                                   Num++;
  302.                           else if(Num == All)
  303.                                         Num =1;
  304.                                 LcdWriteCmd(0x01);  //清屏
  305.                                 lcd1602updata();
  306.                                 //P0 = LedChar[Num];
  307.                         }
  308.                         backup4 = KeySta4;
  309.                 }               
  310.                
  311.                 if (KeySta12 != backup12)
  312.                 {
  313.                         if(backup12 ==0)
  314.                         {
  315.                                 breakflage = 1;
  316.                                 if(Num >1)
  317.                                   Num--;
  318.                           else if(Num == 1)
  319.                                         Num =All;
  320.                                
  321.                                 LcdWriteCmd(0x01);  //清屏
  322.                                 lcd1602updata();
  323.                                 //P0 = LedChar[Num];
  324.                         }
  325.                         backup12 = KeySta12;
  326.                 }                       
  327.                
  328. }
复制代码
LCD1602函数

  1. #include <STC12C5A60S2.h>
  2. #include <intrins.h>

  3. #define LCD1602_DB  P0
  4. sbit LCD1602_RS = P2^0;
  5. sbit LCD1602_RW = P2^1;
  6. sbit LCD1602_E  = P1^2;
  7. # pragma optimize(4, SPEED)
  8. void delayus(unsigned int n)
  9. {
  10.     extern void _nop_(void);
  11.     register unsigned char i = n, j = (n>>8);
  12.     _nop_(); _nop_(); _nop_();
  13.     if ((--i) | j)
  14.     {
  15.         do
  16.         {
  17.             _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_();
  18.             if (0xFF == (i--)) j--; else {_nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_();};
  19.         } while (i | j);
  20.     }
  21. }

  22. void delayms(unsigned int n) {while (n--) delayus(1000);}
  23. /* 等待液晶准备好 */
  24. void LcdWaitReady()
  25. {
  26.     unsigned char sta;
  27.    
  28.     LCD1602_DB = 0xFF;
  29.     LCD1602_RS = 0;
  30.     LCD1602_RW = 1;
  31.     do {
  32.         LCD1602_E = 1;
  33.         sta = LCD1602_DB; //读取状态字
  34.         LCD1602_E = 0;
  35.     } while (sta & 0x80); //bit7等于1表示液晶正忙,重复检测直到其等于0为止
  36. }
  37. /* 向LCD1602液晶写入一字节命令,cmd-待写入命令值 */
  38. void LcdWriteCmd(unsigned char cmd)
  39. {
  40.         LcdWaitReady();
  41.         LCD1602_RS = 0;
  42.         LCD1602_RW = 0;
  43.         LCD1602_DB = cmd;
  44.             delayus(1);
  45.         LCD1602_E = 1;    delayus(1);
  46.         LCD1602_E = 0;    delayms(2);
  47. }
  48. void LcdWriteDat(unsigned char dat)
  49. {
  50.         LcdWaitReady();
  51.         LCD1602_RS = 1;
  52.         LCD1602_RW = 0;
  53.         LCD1602_DB = dat;    delayus(1);
  54.         LCD1602_E = 1;    delayus(1);
  55.         LCD1602_E = 0;    delayms(2);
  56. }
  57. /* 设置显示RAM起始地址,亦即光标位置,(x,y)-对应屏幕上的字符坐标 */
  58. void LcdSetCursor(unsigned char x, unsigned char y)
  59. {
  60.     unsigned char addr;
  61.    
  62.     if (y == 0)  //由输入的屏幕坐标计算显示RAM的地址
  63.         addr = 0x00 + x;  //第一行字符地址从0x00起始
  64.     else
  65.         addr = 0x40 + x;  //第二行字符地址从0x40起始
  66.     LcdWriteCmd(addr | 0x80);  //设置RAM地址
  67. }
  68. /* 在液晶上显示字符串,(x,y)-对应屏幕上的起始坐标,str-字符串指针 */
  69. void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str)
  70. {
  71.         bit flag = 0;
  72.         unsigned char Num = 0;
  73.     LcdSetCursor(x, y);   //设置起始地址
  74.           
  75.     while (*str != '\0')  //连续写入字符串数据,直到检测到结束符
  76.     {
  77.         LcdWriteDat(*str++);
  78.                          Num ++;
  79.                         if(Num >=12)
  80.                         {
  81.                                 flag = 1;
  82.                                 break;
  83.                         }
  84.     }
  85.                 if(flag == 1)
  86.                 {
  87.       LcdSetCursor(0, 1);
  88.                         while (*str != '\0')  //连续写入字符串数据,直到检测到结束符
  89.     {
  90.         LcdWriteDat(*str++);
  91.     }
  92.                 }
  93. }
  94. /* 初始化1602液晶 */
  95. void InitLcd1602()
  96. {
  97.     LcdWriteCmd(0x38);  //16*2显示,5*7点阵,8位数据接口
  98.     LcdWriteCmd(0x0C);  //显示器开,光标关闭
  99.     LcdWriteCmd(0x06);  //文字不动,地址自动+1
  100.     LcdWriteCmd(0x01);  //清屏
  101. }
复制代码




回复

使用道具 举报

ID:1 发表于 2019-2-13 00:43 | 显示全部楼层
本帖需要重新编辑补全电路原理图,源码,详细说明与图片即可获得100+黑币(帖子下方有编辑按钮)
回复

使用道具 举报

ID:229821 发表于 2019-3-29 20:11 | 显示全部楼层
下面还有吗??
回复

使用道具 举报

ID:399179 发表于 2019-11-22 21:53 来自手机 | 显示全部楼层
saya0769 发表于 2019-3-29 20:11
下面还有吗??

掉电记忆程序怎么写?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表