找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4749|回复: 1
打印 上一主题 下一主题
收起左侧

基于51单片机的声控灯设计与protues仿真程序

[复制链接]
跳转到指定楼层
楼主
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)


单片机源程序如下:
  1. #include<reg51.h>
  2. #include<intrins.h>
  3.   #include<string.h>
  4. #define uchar unsigned char
  5. #define uint unsigned int
  6. #define delayNOP() {_nop_();_nop_();_nop_();_nop_();}
  7. sbit SDA=P1^0;  // DS1302数据线
  8. sbit CLK=P1^1;  //         DS1302时钟线
  9. sbit RST=P1^2;  // DS1302复位线
  10. //DS18B20数据端口定义
  11. sbit DQ=P2^4;    //DS18B20数据端口
  12. sbit LCD_RS=P2^5;   // LCD寄存器选择
  13. sbit LCD_RW=P2^6;   //         LCD读写/写控制
  14. sbit LCD_EN=P2^7;   //  LCD启用
  15. sbit K1=P3^2;   //  选择
  16. sbit K2=P3^3;   // 确定
  17. sbit K3=P3^5;   // 加
  18. sbit K4=P3^6;  // 减


  19. sbit key_1=P3^0;        //自动手动切换
  20. sbit key_2=P3^1;        //关灯,按一下,关一个
  21. sbit key_3=P3^4;        //开灯,按一下,开一个
  22. uchar code zz[]={0x08,0x0c,0x04,0x06,0x02,0x03,0x01,0x09};
  23. uchar code fz[]={0x09,0x01,0x03,0x02,0x06,0x04,0x0c,0x08};  
  24. sbit R_LED=P2^0;   //红灯
  25. sbit Y_LED=P2^1;   //黄灯
  26. sbit G_LED=P2^2;   //绿灯
  27. sbit B_LED=P2^3;   //蓝灯

  28. sbit guangmin=P1^3;          //光敏电阻传感器
  29. sbit ren1=P1^4;                  //红外热释电传感器1
  30. sbit ren2=P1^5;                  //红外热释电传感器2
  31. sbit ren3=P1^6;                  //红外热释电传感器3
  32. sbit ren4=P1^7;                  //红外热释电传感器4




  33. //温度字符
  34. uchar code Temperature_Char[8]={0x0c,0x12,0x12,0x0c,0x00,0x00,0x00,0x00};
  35. //温度小数对照表
  36. uchar code df_Table[]={0,1,1,2,3,3,4,4,5,6,6,7,8,8,9,9};
  37. uchar CurrentT=0; //当前读取温度的整数部分
  38. uchar Temp_Value[]={0x00,0x00};         //从DS18B20读取的温度值
  39. uchar Display_Digit[]={0,0,0,0};//待显示的各温度数位
  40. bit DS18B20_IS_OK=1;//传感器正常标志

  41. uchar tCount=0;
  42. //一年中每个月的天数,2月的天数由年份决定
  43. uchar MonthsDays[]={0,31,0,31,30,31,30,31,31,30,31,30,31};
  44. //周日,每周一到周六(0,1-6)【读取DS1302时分别是1-7】
  45. uchar *WEEK[]={"SUN","MON","TUS","WEN","THU","FRI","SAT"};

  46. //LCD显示缓冲
  47. uchar LCD_DSY_BUFFER1[]={"00-00-00        "};
  48. uchar LCD_DSY_BUFFER2[]={"00-00-00      "};
  49. uchar DateTime[7];  //所读取的日期时间
  50. uchar Adjust_flag=0; //当前调节的时间对像:秒,分,时,日,月,年(1,2,3,4,5,6)
  51. uchar Change_Flag[]=" YMDHM";//(分,时,日,月,年)(不调节秒与周)

  52. //延时
  53. void DelayMS(uchar x)
  54. {uchar i;
  55. while(x--)
  56. for(i=0;i<120;i++);
  57. }

  58. //向DS1302写入一个字节
  59. void Write_A_Byte_TO_DS1302(uchar x)
  60. {uchar i;
  61. for(i=0;i<8;i++)
  62. {SDA=x&1;  CLK=1;  CLK=0; x>>=1;
  63. }
  64. }

  65. //从DS11302读取一个字节
  66. uchar Get_A_Byte_FROM_DS1302()
  67. {uchar i,b,t;
  68. for(i=0;i<8;i++)
  69. {b>>=1; t=SDA; b|=t<<7;  CLK=1;CLK=0;
  70. }
  71. //BCD码转换
  72. return b/16*10+b%16;
  73. }
  74. //从DS1302指定位置读数据
  75. uchar Read_Data(uchar addr)
  76. {uchar dat;
  77. RST=0; CLK=0; RST=1;
  78. Write_A_Byte_TO_DS1302(addr);
  79. dat=Get_A_Byte_FROM_DS1302();
  80. CLK=1;RST=0;
  81. return dat;
  82. }

  83. //向DS1302某地址写入数据
  84. void Write_DS1302(uchar addr,uchar dat)
  85. {CLK=0;RST=1;
  86. Write_A_Byte_TO_DS1302(addr);
  87. Write_A_Byte_TO_DS1302(dat);
  88. CLK=0;RST=0;
  89. }

  90. //设置时间
  91. void SET_DS1302()
  92. { uchar i;
  93. Write_DS1302(0x8e,0x00); //写控制字,取消写保护
  94. //分时日月年依次写入
  95. for(i=0;i<7;i++)
  96. {
  97. //分的起始地址10000010(0x82),后面连续依次是时,日,月,周,年,写入 地址每次增2
  98. Write_DS1302(0X80+2*i,(DateTime[i]/10<<4)|(DateTime[i]%10));
  99. }
  100. Write_DS1302(0x8e,0x80);//加保护
  101. }

  102. //读取当前的时间
  103. void GetTime()
  104. {
  105.   uchar i;
  106.   for(i=0;i<7;i++)
  107.    {
  108.      DateTime[i]=Read_Data(0x81+2*i);
  109.    }
  110. }


  111. //LCD忙等待
  112. bit LCD_Busy_Check()
  113. {bit result;
  114. LCD_RS=0;
  115. LCD_RW=1;
  116. LCD_EN=1;
  117. delayNOP();
  118. result=(bit)(P0&0x80);
  119. LCD_EN=0;
  120. return result;
  121. }

  122. //写指令
  123. void Write_LCD_Command(uchar cmd)
  124. {while(LCD_Busy_Check());
  125. LCD_RS=0;LCD_RW=0;LCD_EN=0;        _nop_();         _nop_();
  126. P0=cmd;
  127. delayNOP();
  128. LCD_EN=1;
  129. delayNOP();
  130. LCD_EN=0;
  131. }
  132. //写数据
  133. void Write_LCD_Data(uchar dat)
  134. {while(LCD_Busy_Check());
  135. LCD_RS=1;LCD_RW=0;LCD_EN=0;
  136. P0=dat;
  137. delayNOP();
  138. LCD_EN=1;
  139. delayNOP();
  140. LCD_EN=0;
  141. }

  142. //初始化
  143. void Init_LCD()
  144. {Write_LCD_Command(0x01);DelayMS(5);
  145. Write_LCD_Command(0x38);DelayMS(5);
  146. Write_LCD_Command(0x0c);DelayMS(5);
  147. Write_LCD_Command(0x06);DelayMS(5);
  148. }
  149. //设置显示位置
  150. void Set_LCD_POS(uchar pos)
  151. {        Write_LCD_Command(pos|0x80);
  152.   }

  153. //在LCD上显示字符串
  154. void Display_LCD_String(uchar p,uchar *s)
  155. {uchar i;
  156. Set_LCD_POS(p);
  157. for(i=0;i<16;i++)
  158. {Write_LCD_Data(s[i]);
  159. DelayMS(1);
  160. }
  161. }

  162. //日期与时间值转换为数字字符
  163. void Format_DateTime(uchar d,uchar *a)
  164. {a[0]=d/10+'0';
  165. a[1]=d%10+'0';
  166. }

  167. //判断是否为闰年
  168. uchar isLeapYear(uint y)
  169. {return (y%4==0&&y%100!=0)||(y%400==0);
  170. }

  171. //求自2000.1.1开始的任何一天是星期几
  172. void RefreshWeekDay()
  173. {uint i,d,w=5; //已知1999.12.31是星期五
  174. for(i=2000;i<2000+DateTime[6];i++)
  175. { d=isLeapYear(i)?366:365;
  176. w=(w+d)%7;
  177. }
  178. d=0;
  179. for(i=1;i<DateTime[4];i++)
  180. d+=MonthsDays[i];
  181. d+=DateTime[3];
  182. //保存星期,0~6表示星期日,星期一,二.....六,为了与DS1302的星期格式匹配,返回值需要加1
  183. DateTime[5]=(w+d)%7+1;
  184. }

  185. //延时函数2
  186. void Delay(uint x)
  187. {while(--x);
  188. }
  189. /***************初始化18B20温度函数******************/
  190. uchar Init_DS18B20()
  191. {
  192.    uchar status;
  193.    DQ=1;
  194.    Delay(8);
  195.    DQ=0;
  196.    Delay(90);
  197.    DQ=1;
  198.    Delay(8);
  199.    status=DQ;
  200.    Delay(100);
  201.    DQ=1;
  202.    return status;
  203. }

  204. //读一字节
  205. uchar ReadOneByte()
  206. {uchar i,dat=0;
  207. DQ=1;_nop_();
  208. for(i=0;i<8;i++)
  209. {DQ=0;dat>>=1;DQ=1;_nop_();_nop_();
  210. if(DQ)dat|=0x80;
  211. Delay(30);
  212. DQ=1;
  213. }
  214. return dat;
  215. }

  216. //写一字节
  217. void WriteOneByte(uchar dat)
  218. {uchar i;
  219. for(i=0;i<8;i++)
  220. { DQ=0;DQ=dat&0x01;Delay(5);DQ=1;dat>>=1;
  221. }
  222. }

  223. //读取温度值
  224. void Read_Temperature()
  225. {
  226. //延时值与负数标识
  227. uchar t=150,ng=0;
  228. //高5位全为1(0xf8)则为负数,为负数时取反加1,并设置负数标识

  229. if(Init_DS18B20()==1)  //DS18B20故障
  230. DS18B20_IS_OK=0;
  231. else
  232. {WriteOneByte(0xcc);   //跳过序列号
  233. WriteOneByte(0x44);//启动温度转换
  234. Init_DS18B20();
  235. WriteOneByte(0xcc);   //跳过序列号
  236. WriteOneByte(0xBE);//读取温度寄存器
  237. Temp_Value[0]=ReadOneByte();  //温度低8位
  238. Temp_Value[1]=ReadOneByte();  //温度高8位
  239. DS18B20_IS_OK=1;
  240. if((Temp_Value[1]&0xf8)==0xf8)
  241. {
  242. Temp_Value[1]=~Temp_Value[1];
  243. Temp_Value[0]=~Temp_Value[0]+1;
  244. if(Temp_Value[0]==0x00)
  245. Temp_Value[1]++;
  246. //负数标志置1
  247. ng=1;
  248. }
  249. //查表得到温度的小数部分
  250. Display_Digit[0]=df_Table[Temp_Value[0]&0x0f];
  251. //获取温度整数部分(高字节中的低三位与低字节中的高4位,无符号)
  252. CurrentT=((Temp_Value[0]&0xf0)>>4)|((Temp_Value[1]&0X07)<<4);
  253. //将整数部分分解为3位待显示数字
  254. Display_Digit[3]=CurrentT/100;
  255. Display_Digit[2]=CurrentT%100/10;
  256. Display_Digit[1]=CurrentT%10;

  257. //刷新LCD显示缓冲
  258. LCD_DSY_BUFFER2[13]= Display_Digit[0]+'0';
  259. LCD_DSY_BUFFER2[12]='.'        ;
  260. LCD_DSY_BUFFER2[11]=Display_Digit[1]+'0';
  261. LCD_DSY_BUFFER2[10]=Display_Digit[2]+'0';
  262. LCD_DSY_BUFFER2[9]=Display_Digit[3]+'0';
  263. //高位为0时不显示
  264. if(Display_Digit[3]==0)         LCD_DSY_BUFFER2[9]=' ';
  265. //高位为0,且次高位为0时,次高位不显示
  266. if(Display_Digit[2]==0&&Display_Digit[3]==0)
  267. LCD_DSY_BUFFER2[10]=' ';

  268. //负数符号显示在恰当的位置
  269. if(ng)
  270. {if(LCD_DSY_BUFFER2[10]==' ')
  271. LCD_DSY_BUFFER2[10]='-';
  272. else
  273. if(LCD_DSY_BUFFER2[9]==' ')
  274. LCD_DSY_BUFFER2[9]='-';
  275. else
  276.   LCD_DSY_BUFFER2[8]='-';
  277. }
  278. }
  279. }


  280. //键盘中断 0
  281. void EX_INT0() interrupt 0
  282. {if(K1==0)  //选择调整对象(Y M D H M)
  283. { while(K1==0);
  284. Adjust_flag++;
  285. LCD_DSY_BUFFER1[13]='[';
  286. LCD_DSY_BUFFER1[14]=Change_Flag[Adjust_flag];
  287. LCD_DSY_BUFFER1[15]=']';
  288. if(Adjust_flag==6)
  289. {Adjust_flag=0;
  290. LCD_DSY_BUFFER1[13]=' ';
  291. LCD_DSY_BUFFER1[14]=' ';
  292. LCD_DSY_BUFFER1[15]=' ';

  293. }
  294. }
  295. }

  296. //键盘中断1
  297. void EX_INT1() interrupt 2
  298. {
  299. while(K2==0);
  300. SET_DS1302();//将调整后的时间写入DS1302
  301. LCD_DSY_BUFFER1[13]=' ';
  302. LCD_DSY_BUFFER1[14]=' ';
  303. LCD_DSY_BUFFER1[15]=' ';
  304. Adjust_flag=0;//操作索引重设为-1,时间继续正常显示
  305. }


  306. //定时器0每秒刷新LCD显示
  307. void T0_INT() interrupt 1
  308. { uchar i;
  309. TH0=-50000/256;
  310. TL0=-50000%256;
  311. Set_LCD_POS(0X4e);Write_LCD_Data(0Xdf);
  312. Set_LCD_POS(0X4f);Write_LCD_Data('C');
  313. if(++tCount!=2) return;
  314. tCount=0;
  315. Read_Temperature();
  316. //按指定的格式生成待显示的日期时间串
  317. Format_DateTime(DateTime[6],LCD_DSY_BUFFER1);
  318. Format_DateTime(DateTime[4],LCD_DSY_BUFFER1+3);
  319. Format_DateTime(DateTime[3],LCD_DSY_BUFFER1+6);
  320. //星期
  321. strcpy(LCD_DSY_BUFFER1+9,WEEK[DateTime[5]-1]);
  322. //时分秒
  323. Format_DateTime(DateTime[2],LCD_DSY_BUFFER2);
  324. Format_DateTime(DateTime[1],LCD_DSY_BUFFER2+3);
  325. Format_DateTime(DateTime[0],LCD_DSY_BUFFER2+6);
  326. //显示年月日,星期,时分秒
  327. Display_LCD_String(0x00,LCD_DSY_BUFFER1);
  328. Set_LCD_POS(0x40);
  329. for(i=0;i<14;i++)
  330. {
  331. Write_LCD_Data(LCD_DSY_BUFFER2[i]);
  332. DelayMS(1);
  333. }
  334. }

  335. //手动按键控制灯函数
  336. void key_LED()
  337. {
  338.   R_LED=1;
  339.   Y_LED=1;
  340.   G_LED=1;
  341.   B_LED=1;
  342.   if(key_2==0)
  343.    {
  344.        R_LED=0;
  345.        Y_LED=0;
  346.        G_LED=0;
  347.        B_LED=0;  
  348.    }
  349.   if(key_3==0)
  350.    {
  351.        R_LED=1;
  352.        Y_LED=1;
  353.        G_LED=1;
  354.        B_LED=1;  
  355.    }

  356. }


  357. //主程序
  358. void main()
  359. {
  360. Init_LCD();//液晶初始化
  361. IE=0X87;//允许INT0,T0中断
  362. IP=0X05;
  363. IT0=0X01;
  364. IT1=0X01;
  365. TMOD=0X01;
  366. TH0=-50000/256;
  367. TL0=-50000%256;
  368. TR0=1;
  369. while(1)
  370. {  
  371.   R_LED=Y_LED=G_LED=B_LED=1;
  372.   if(key_1==0)        //手动模式
  373.    {
  374.             key_LED();         //按键控制灯函数
  375.    }
  376.   else
  377.    {
  378.             if(DateTime[2]>18||DateTime[2]<6||guangmin==0)
  379.       {
  380.             if(ren1==0)
  381.                  {
  382.                    R_LED=0;
  383.                Y_LED=1;
  384.                G_LED=1;
  385.                B_LED=1;
  386.                  }
  387.                 if(ren2==0)
  388.                  {
  389.                    R_LED=0;
  390.                Y_LED=0;
  391.                G_LED=1;
  392.                B_LED=1;
  393.                  }
  394.             if(ren3==0)
  395.                  {
  396.                    R_LED=0;
  397.                Y_LED=0;
  398.                G_LED=0;
  399.                B_LED=1;
  400.                  }
  401.                 if(ren4==0)
  402.                  {
  403.                    R_LED=0;
  404.                Y_LED=0;
  405.                G_LED=0;
  406.                B_LED=0;         
  407.                  }
  408.           }

  409.    }
  410. switch(Adjust_flag)
  411. {
  412. case 0:GetTime(); break;
  413. case 1:
  414. if(K3==0&&DateTime[6]<99)
  415. {
  416. while(K3==0);
  417. DateTime[6]++;}

  418. if(K4==0&&DateTime[6]>0)
  419. {
  420. while(K4==0);
  421. DateTime[6]--;
  422. }
  423. MonthsDays[2]=isLeapYear(2000+DateTime[6])?29:28;
  424. //如果年份变化后当前月份的天数大于上限则设为上限
  425. if(DateTime[3]>MonthsDays[DateTime[4]])
  426. DateTime[3]=MonthsDays[DateTime[4]];
  427. RefreshWeekDay();//刷新星期
  428. break;

  429. case 2:
  430. if(K3==0&&DateTime[4]<12)
  431. {
  432. while(K3==0);
  433. DateTime[4]++;}

  434. if(K4==0&&DateTime[4]>1)
  435. {
  436. while(K4==0);
  437. DateTime[4]--;
  438. }
  439. //获取2月份天数
  440. MonthsDays[2]=isLeapYear(2000+DateTime[6])?29:28;
  441. //如果变化后当前月份的天数大于上限则设为上限
  442. if(DateTime[3]>MonthsDays[DateTime[4]])
  443. DateTime[3]=MonthsDays[DateTime[4]];
  444. RefreshWeekDay();//刷新星期
  445. break;

  446. case 3://日00-28/29/30/31,调节之前首先根据年份得出该年中2月的天数
  447. MonthsDays[2]=isLeapYear(2000+DateTime[6])?29:28;
  448. //根据当前的月份决定调节日期的上限
  449. if(K3==0&&DateTime[3]<MonthsDays[DateTime[4]])
  450. {
  451. while(K3==0);
  452. DateTime[3]++;}
  453. if(K4==0&&DateTime[3]>0)
  454. {
  455. while(K4==0);
  456. DateTime[3]--;
  457. }
  458. break;

  459. case 4://时
  460. if(K3==0&&DateTime[2]<23)
  461. {
  462. while(K3==0);
  463. DateTime[2]++;}
  464. if(K4==0&&DateTime[2]>0)
  465. {
  466. while(K4==0);
  467. DateTime[2]--; }
  468. break;
  469. case 5://分
  470. if(K3==0&&DateTime[1]<59)
  471. { while(K3==0);
  472. DateTime[1]++;   }
  473. if(K4==0&&DateTime[1]>0)
  474. {while(K4==0);
  475. DateTime[1]--;        
  476. }
  477. break;
  478. }
  479. }}
复制代码
全部资料51hei下载地址:
仿真与程序.rar (222.13 KB, 下载次数: 103)

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏6 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:1067911 发表于 2023-4-9 20:20 | 只看该作者
为什么LCD屏幕不能显示
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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