找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1061|回复: 0
收起左侧

单片机音乐发生器 proteus仿真

[复制链接]
ID:782260 发表于 2021-3-26 13:34 | 显示全部楼层 |阅读模式
  1. #include <reg51.h>
  2. #include <string.h>
  3. #define uchar unsigned char
  4. #define uint unsigned int
  5. sbit SDA=P1^0;          //DS1302 数据线
  6. sbit CLK=P1^1;          //DSB1302 时钟线
  7. sbit RST=P1^2;          //DS1302 复位线
  8. sbit RS=P2^0;           //LCD 寄存器选择
  9. sbit RW=P2^1;          //LCD 读/写控制
  10. sbit EN=P2^2;          //LCD 启用
  11. sbit K1=P3^4;          //选择
  12. sbit K2=P3^5;          //加
  13. sbit K3=P3^6;          //减
  14. sbit K4=P3^7;          //确定
  15. uchar tCount=0;
  16. uchar dat;             //定义参数
  17. //一年中每个月的天数,二月的天数由年份决定
  18. uchar MonthsDays[]={31,0,31,30,31,30,31,31,30,31,30,31};
  19. //周日,周一到周六{0,1-6} [读取 DS1302 时分别是 1-7]
  20. uchar *WEEK[]={"SUN","MON","TUS","WEN","THU","FRI","SAT"};
  21. //LCD 显示缓冲
  22. uchar LCD_DSY_BUFFER1[]={"0019 00-00-00  "};
  23. uchar LCD_DSY_BUFFER2[]={"HWW   00:00:00 "};
  24. uchar DateTime[7]; //所读取的日期时间
  25. char Adjust_Index=-1;//当前调节的时间对象:秒,分,时,日,月,年(0,1,2,3,4,6)
  26. uchar Change_Flag[]="-MHDM-Y";
  27. //(分,时,日,月,年) (不调节秒周)
  28. //延时,向 DS1302 写、读一字节以及从 DS1302 指定位置度、写数据的程序
  29. //延时---------------------------------------------------
  30. void DelayMS(uint x)
  31. {
  32.   uchar i;
  33.   while(x--)  for(i=0;i<120;i++);
  34. }
  35. //向 DS1302 写入一个字节
  36. void Write_A_Byte_TO_DS1302(uchar x)
  37. {
  38.   uchar i;
  39.   for(i=0;i<8;i++)
  40.   {
  41.      SDA=x&1;CLK=1; CLK=0; x>>=1;
  42.   }
  43. }
  44. //从 DA1302 读取一字节---------------------------------------
  45. uchar Get_A_Byte_FROM_DS1302()
  46. {
  47.   uchar i,b,t;
  48.   for(i=0;i<8;i++)
  49.   {
  50.      b>>=1; t=SDA;b|=t<<7;CLK=1;CLK=0;
  51.   }
  52.   //BCD 码转换
  53.   return b/16*10+b%16;
  54. }
  55. //从 DS1302 指定的位置读数据--------------------------------------
  56. uchar Read_Data(uchar addr)
  57. {
  58.   uchar dat;
  59.   RST=0;CLK=0;RST=1;
  60.   Write_A_Byte_TO_DS1302(addr);
  61.   dat=Get_A_Byte_FROM_DS1302();
  62.   CLK=1;RST=0;
  63.   return dat;
  64. }
  65. // 向 DS1302 某地址写入数据-------------------------------------------
  66. void Write_DS1302(uchar addr,uchar dat)
  67. {
  68.   CLK=0;RST=1;
  69.   Write_A_Byte_TO_DS1302(addr);
  70.   Write_A_Byte_TO_DS1302(dat);
  71.   CLK=0;RST=0;
  72. }
  73. //-------------------------------------------
  74. //------设置时间-------------------------------------
  75. void SET_DS1302()
  76. {
  77. uchar i;
  78. Write_DS1302(0x8E,0x00); //写控制字,取消写保护
  79. for(i=1;i<7;i++) //分时日月年依次写入
  80. {
  81.   //分的起始地址 10000010(0x82),后面续依次是时.日.月.周.年.写入地址每次递增 2  
  82.   Write_DS1302(0x80+ 2*i,(DateTime[i]/10<<4)|(DateTime[i]%10)); }
  83.   Write_DS1302(0x8E,0x80);//加保护
  84. }
  85. //---------------------------------------------------
  86. //读取当前日期时间
  87. //---------------------------------------------------
  88. void GetTime()
  89. {
  90.   uchar i;
  91.   for(i=0;i<7;i++)
  92.   {
  93.    DateTime[i]=Read_Data(0x81+2*i);
  94.   }
  95. }
  96. //---------------------------------------------------
  97. //1602LCD 的若干显示控制代码
  98. //uchar Read_LCD_State() 读 LCD 状态
  99. uchar Read_LCD_State()
  100. {
  101.   uchar state;
  102.   RS=0;RW=1;EN=1;DelayMS(1);state=P0;EN=0;DelayMS(1);
  103.   return state;
  104. }
  105. //void LCD_Busy_Wait() 忙等待

  106. void LCD_Busy_Wait()
  107. {
  108.   while((Read_LCD_State()&0x80)==0x80);
  109.   DelayMS(5);
  110. }
  111. //void  Write_LCD_Data(uchar dat) 向 LCD 写数据
  112. void Write_LCD_Data(uchar dat)
  113. {
  114.   LCD_Busy_Wait();
  115.   RS=1;RW=0;EN=0;P0=dat;EN=1;DelayMS(1);EN=0;
  116. }
  117. //void  Write_LCD_Command(uchar cmd) 写 LCD 指令
  118. void  Write_LCD_Command(uchar cmd)
  119. {
  120.   LCD_Busy_Wait();
  121.   RS=0;RW=0;EN=0;P0=cmd;EN=1;DelayMS(1);EN=0;
  122. }
  123. // Void  Init_LCD()  LCD 初始化
  124. void  Init_LCD()
  125. {
  126.   Write_LCD_Command(0x38);DelayMS(1);
  127.   Write_LCD_Command(0x01);DelayMS(1);
  128.   Write_LCD_Command(0x06);DelayMS(1);
  129.   Write_LCD_Command(0x0c);DelayMS(1);
  130. }
  131. //void Set_LCD_POS(uchar P) 设置液晶显示位置
  132. void Set_LCD_POS(uchar p)
  133. {
  134.   Write_LCD_Command(p|0x80);
  135. }
  136. //------------------------------------------------------
  137. //在 LCD 上显示字符串
  138. void Display_LCD_String(uchar p,uchar *s)
  139. {
  140.   uchar i;
  141.   Set_LCD_POS(p);
  142.   for(i=0;i<16;i++)
  143.   {
  144.   Write_LCD_Data(s[i]); DelayMS(1);
  145.   }
  146. }
  147. //-----------------------------------------------------------
  148. //日期与时间值转换为数字字符
  149. void Format_DateTime(uchar d,uchar *a)
  150. {
  151.   a[0]=d/10+'0';a[1]=d%10+'0';
  152. }
  153. //----------------------------------------------------------
  154. //判断是否为闰年
  155. uchar isLeapYear(uint y)
  156. {
  157.   return(y%4==0&y%100!=0)||(y%400==0);
  158. }
  159. //-----------------------------------------------------------
  160. //求自 2000.1.1 开始的任何一天是星期几
  161. //函数没有通过,求出总天数后再求星期几
  162. //因为求总天数可能会超越 uint 的范围
  163. void RefreshWeekDay()
  164. {  
  165.   uint i,d,w=5;
  166.   //已知 1999.12.31 是周五
  167.   for(i=2000;i<2000+DateTime[6];i++)
  168.   {
  169.         d=isLeapYear(i)?366:365;
  170.         w=(w+d)%7;
  171.   }
  172. d=0;
  173. for(i=1;i<DateTime[4];i++) d+=MonthsDays[i];
  174. d+=DateTime[3];
  175. //保存星期,0~6 表示星期日至周六,为了与DS1302 的星期格式匹配,返回值需要加 1 DateTime[5]=(w+d)%7+1;
  176. }
  177. //----------------------------------------------------------
  178. void DateTime_Adjust(char x)
  179. {
  180.   switch(Adjust_Index)
  181.   {
  182.     case 6://年 00-99
  183.          if(x==1&DateTime[6]<99) DateTime[6]++;
  184.          if(x==-1&DateTime[6]>0) DateTime[6]--;
  185.          //获取 2 月天数
  186.          MonthsDays[2]=isLeapYear(2000+DateTime[6])?29:28;
  187.          //如果年份变化后当前月份的天数大于上限则设为上限
  188.          if(DateTime[3]>MonthsDays[DateTime[4]])
  189.          DateTime[3]=MonthsDays[DateTime[4]]; RefreshWeekDay();
  190.          //刷新星期
  191.         break;
  192.     case 4:
  193.         //月 01-12
  194.         if(x==1&DateTime[4]<12)  DateTime[4]++;
  195.         if(x==-1&DateTime[4]>1)  DateTime[4]--;
  196.         //获取 2 月天数
  197.          MonthsDays[2]=isLeapYear(2000+DateTime[6])?29:28;
  198.          //如果月份变化后当前月份的天数大于上限则设为上限
  199.          if(DateTime[3]>MonthsDays[DateTime[4]])
  200.          DateTime[3]=MonthsDays[DateTime[4]];
  201.          RefreshWeekDay();
  202.          //刷新星期
  203.          break;
  204.         case 3://日00-28/29/30/31;调节之前首先根据年份得出该年中2月的天数        
  205.               MonthsDays[2]=isLeapYear(2000+DateTime[6])?29:28;
  206.           //根据当前月份决定调节日期的上限
  207.           if(x==1&DateTime[3]<MonthsDays[DateTime[4]])  DateTime[3]++;
  208.           if(x==-1&DateTime[3]>0)  DateTime[3]--;
  209.           RefreshWeekDay();//刷新星期
  210.           break;
  211.     case 2://时
  212.           if(x==1&DateTime[2]<23) DateTime[2]++;
  213.           if(x==-1&DateTime[2]>0) DateTime[2]--;
  214.           break;
  215.     case 1://分
  216.           if(x==1&DateTime[1]<59)  DateTime[1]++;
  217.           if(x==-1&DateTime[1]>0)  DateTime[1]--;
  218.           break;
  219.   }
  220. }
  221. //---------------------------------------------------------
  222. //定时器 0 每秒刷新 LCD 显示
  223. void T0_INT() interrupt 1
  224. {
  225.   TH0=-50000/256;
  226.   TL0=-50000%256;
  227.   if(++tCount!=2) return;
  228.   tCount=0;
  229.   //按指定的格式生成待显示的日期时间串
  230.   Format_DateTime(DateTime[6],LCD_DSY_BUFFER1+5);   
  231.   Format_DateTime(DateTime[4],LCD_DSY_BUFFER1+8);   
  232.   Format_DateTime(DateTime[3],LCD_DSY_BUFFER1+11);
  233.   //星期 strcpy(LCD_DSY_BUFFER1+13,WEEK[DateTime[5]-1]);
  234.   //时分秒
  235.   Format_DateTime(DateTime[2],LCD_DSY_BUFFER2+5);   
  236.   Format_DateTime(DateTime[1],LCD_DSY_BUFFER2+8);  
  237.   Format_DateTime(DateTime[0],LCD_DSY_BUFFER2+11);
  238.   //显示年月日,星期,时分秒
  239.   Display_LCD_String(0x00,LCD_DSY_BUFFER1);  
  240.   Display_LCD_String(0x40,LCD_DSY_BUFFER2);
  241. }
  242.   //---键盘中断(INT0)-----------------------------------------
  243. void EX_INT0() interrupt 0
  244. {
  245.   if(K1==0) //选择调整对象(Y M D H M)
  246.   {
  247.     while(K1==0);
  248.         if(Adjust_Index==-1||Adjust_Index==1) Adjust_Index=7;
  249.         Adjust_Index--;
  250.     if(Adjust_Index==5) Adjust_Index=4;//跳过对星期的调节
  251.     LCD_DSY_BUFFER2[13]='[';
  252.     LCD_DSY_BUFFER2[14]=Change_Flag[Adjust_Index];
  253.         LCD_DSY_BUFFER2[15]=']';
  254.         }
  255.         else if(K2==0)//加
  256.    {
  257.      while(K2==0); DateTime_Adjust(1);
  258.    }
  259.    else if(K3==0)//减
  260.    {
  261.      while(K3==0); DateTime_Adjust(-1);
  262.    }
  263.    else if(K4==0)//确定
  264. {
  265.   while(K4==0); SET_DS1302(); //将调整后的时间写入 1302
  266.   LCD_DSY_BUFFER2[13]=' ';
  267.   LCD_DSY_BUFFER2[14]=' ';
  268.   LCD_DSY_BUFFER2[15]=' ';
  269.   Adjust_Index=-1;
  270. //操作索引重设为-1,时间继续正常显示
  271. }
  272. }
  273. //---------------------------------------------------------------
  274. //主程序 void main()
  275. void main()
  276. {
  277.   Init_LCD();//液晶初始化
  278.   IE=0x83; //允许INT0,T0中断
  279.   IP=0x01;
  280.   IT0=0x01;
  281.   TMOD=0x01;
  282.   TH0=-50000/256;
  283.   TL0=-50000%256;
  284.   TR0=1;
  285.   while(1)
  286.   {
  287.   //如果为执行调整操作则正常读取当前时间
  288.   if(Adjust_Index==-1) GetTime();
  289.   }
  290. }
复制代码

新建 WinRAR 压缩文件.rar

14.79 KB, 下载次数: 5, 下载积分: 黑币 -5

回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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