×
嵌入式 > 技术百科 > 详情

第49节:利用DS18B20做一个温控器

发布时间:2020-06-20 发布时间:
|
开场白:
      DS18B20是一款常用的温度传感器芯片,它只占用单片机一根IO口,使用起来也特别方便。需要特别注意的是,正因为它只用一根IO口跟单片机通讯,因此读取一次温度值的通讯时间比较长,而且时序要求严格,在通讯期间不允许被单片机其它的中断干扰,因此在实际项目中,系统一旦选用了这款传感器芯片,就千万不要选用动态扫描数码管的显示方式。否则在关闭中断读取温度的时候,数码管的显示会有略微的“闪烁”现象。
      DS18B20的测温范围是-55度至125度。在-10度至85度的温度范围内误差是+-0.5度,能满足大部分常用的测温要求。
这一节要教会大家三个知识点:
第一个:大概了解一下DS18B20的驱动程序。
第二个:做温控设备的时候,为了避免继电器在临界温度附近频繁跳动切换,应该设置一个缓冲温差。本程序的缓冲温差是2度。
第三个:继续加深了解按键,显示,传感器它们三者是如何紧密关联起来的程序框架。

具体内容,请看源代码讲解。

(1)        硬件平台.
基于朱兆祺51单片机学习板。

(2)实现功能:
     本程序只有1个窗口。这个窗口有2个局部显示。
第1个局部是第7,6,5位数码管,显示设定的温度。
第2个局部是第4,3,2,1位数码管,显示实际环境温度。其中第4位数码管显示正负符号位。
S1按键是加键,S5按键是减键。通过它们可以直接设置“设定温度”。
一个LED灯用来模拟工控的继电器。
当实际温度低于或者等于设定温度2度以下时,模拟继电器的LED灯亮。
当实际温度等于或者大于设定温度时,模拟继电器的LED灯灭。
当实际温度处于设定温度和设定温度减去2度的范围内,模拟继电器的LED维持现状,这个2度范围用来做缓冲温差,避免继电器在临界温度附近频繁跳动切换。

(3)源代码讲解如下:
  1. #include "REG52.H"
  2.  
  3.  
  4. #define const_voice_short  40   //蜂鸣器短叫的持续时间
  5. #define const_key_time1  20    //按键去抖动延时的时间
  6. #define const_key_time2  20    //按键去抖动延时的时间
  7.  
  8. #define const_ds18b20_sampling_time    180   //累计主循环次数的时间,每次刷新采样时钟芯片的时间
  9.  
  10.  
  11. void initial_myself(void);    
  12. void initial_peripheral(void);
  13. void delay_short(unsigned int uiDelayShort); 
  14. void delay_long(unsigned int uiDelaylong);
  15.  
  16.  
  17. //驱动数码管的74HC595
  18. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);  
  19. void display_drive(void); //显示数码管字模的驱动函数
  20. void display_service(void); //显示的窗口菜单服务程序
  21. //驱动LED的74HC595
  22. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
  23.  
  24. void T0_time(void);  //定时中断函数
  25.  
  26. void key_service(void); //按键服务的应用程序
  27. void key_scan(void);//按键扫描函数 放在定时中断里
  28.  
  29. void temper_control_service(void); //温控程序
  30. void ds18b20_sampling(void); //ds18b20采样程序
  31.  
  32. void ds18b20_reset(); //复位ds18b20的时序
  33. unsigned char ds_read_byte(void ); //读一字节
  34. void ds_write_byte(unsigned char dat); //写一个字节
  35. unsigned int get_temper();  //读取一次没有经过换算的温度数值
  36.  
  37. sbit dq_dr_sr=P2^6; //ds18b20的数据驱动线
  38.  
  39. sbit key_sr1=P0^0; //对应朱兆祺学习板的S1键
  40. sbit key_sr2=P0^1; //对应朱兆祺学习板的S5键
  41.  
  42. sbit led_dr=P3^5;  //LED灯,模拟工控中的继电器
  43.  
  44. sbit key_gnd_dr=P0^4; //模拟独立按键的地GND,因此必须一直输出低电平
  45.  
  46. sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
  47.  
  48.  
  49.  
  50. sbit dig_hc595_sh_dr=P2^0;     //数码管的74HC595程序
  51. sbit dig_hc595_st_dr=P2^1;  
  52. sbit dig_hc595_ds_dr=P2^2;  
  53. sbit hc595_sh_dr=P2^3;    //LED灯的74HC595程序
  54. sbit hc595_st_dr=P2^4;  
  55. sbit hc595_ds_dr=P2^5;  
  56.  
  57.  
  58. unsigned int uiSampingCnt=0;   //采集Ds1302的计时器,每秒钟更新采集一次
  59. unsigned char ucSignFlag=0; //正负符号。0代表正数,1代表负数,表示零下多少度。
  60. unsigned long ulCurrentTemper=33; //实际温度
  61. unsigned long ulSetTemper=26; //设定温度
  62.  
  63. unsigned int uiTemperTemp=0; //中间变量
  64.  
  65. unsigned char ucKeySec=0;   //被触发的按键编号
  66.  
  67. unsigned int  uiKeyTimeCnt1=0; //按键去抖动延时计数器
  68. unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志
  69. unsigned int  uiKeyTimeCnt2=0; //按键去抖动延时计数器
  70. unsigned char ucKeyLock2=0; //按键触发后自锁的变量标志
  71.  
  72. unsigned int  uiVoiceCnt=0;  //蜂鸣器鸣叫的持续时间计数器
  73. unsigned char  ucVoiceLock=0;  //蜂鸣器鸣叫的原子锁
  74.  
  75. unsigned char ucDigShow8;  //第8位数码管要显示的内容
  76. unsigned char ucDigShow7;  //第7位数码管要显示的内容
  77. unsigned char ucDigShow6;  //第6位数码管要显示的内容
  78. unsigned char ucDigShow5;  //第5位数码管要显示的内容
  79. unsigned char ucDigShow4;  //第4位数码管要显示的内容
  80. unsigned char ucDigShow3;  //第3位数码管要显示的内容
  81. unsigned char ucDigShow2;  //第2位数码管要显示的内容
  82. unsigned char ucDigShow1;  //第1位数码管要显示的内容
  83.  
  84. unsigned char ucDigDot8;  //数码管8的小数点是否显示的标志
  85. unsigned char ucDigDot7;  //数码管7的小数点是否显示的标志
  86. unsigned char ucDigDot6;  //数码管6的小数点是否显示的标志
  87. unsigned char ucDigDot5;  //数码管5的小数点是否显示的标志
  88. unsigned char ucDigDot4;  //数码管4的小数点是否显示的标志
  89. unsigned char ucDigDot3;  //数码管3的小数点是否显示的标志
  90. unsigned char ucDigDot2;  //数码管2的小数点是否显示的标志
  91. unsigned char ucDigDot1;  //数码管1的小数点是否显示的标志
  92. unsigned char ucDigShowTemp=0; //临时中间变量
  93. unsigned char ucDisplayDriveStep=1;  //动态扫描数码管的步骤变量
  94.  
  95.  
  96. unsigned char ucWd=1;  //因为本程序只有1个窗口,在实际项目中,此处的ucWd也可以省略不要
  97.  
  98. unsigned char ucWd1Part1Update=1;  //在窗口1中,局部1的更新显示标志
  99. unsigned char ucWd1Part2Update=1; //在窗口1中,局部2的更新显示标志
  100.  
  101.  
  102. unsigned char ucTemp1=0;  //中间过渡变量
  103. unsigned char ucTemp2=0;  //中间过渡变量
  104. unsigned char ucTemp3=0;  //中间过渡变量
  105. unsigned char ucTemp4=0;  //中间过渡变量
  106. unsigned char ucTemp5=0;  //中间过渡变量
  107. unsigned char ucTemp6=0;  //中间过渡变量
  108. unsigned char ucTemp7=0;  //中间过渡变量
  109. unsigned char ucTemp8=0;  //中间过渡变量
  110.  
  111.  
  112. //根据原理图得出的共阴数码管字模表
  113. code unsigned char dig_table[]=
  114. {
  115. 0x3f,  //0       序号0
  116. 0x06,  //1       序号1
  117. 0x5b,  //2       序号2
  118. 0x4f,  //3       序号3
  119. 0x66,  //4       序号4
  120. 0x6d,  //5       序号5
  121. 0x7d,  //6       序号6
  122. 0x07,  //7       序号7
  123. 0x7f,  //8       序号8
  124. 0x6f,  //9       序号9
  125. 0x00,  //无      序号10
  126. 0x40,  //-       序号11
  127. 0x73,  //P       序号12
  128. };
  129. void main() 
  130.   {
  131.    initial_myself();  
  132.    delay_long(100);   
  133.    initial_peripheral(); 
  134.    while(1)  
  135.    { 
  136.       key_service(); //按键服务的应用程序
  137.       ds18b20_sampling(); //ds18b20采样程序
  138.       temper_control_service(); //温控程序
  139.       display_service(); //显示的窗口菜单服务程序
  140.    }
  141. }
  142.  
  143. /* 注释一:
  144.   * 做温控设备的时候,为了避免继电器在临界温度附近频繁跳动切换,应该设置一个
  145.   * 缓冲温差。本程序的缓冲温差是2度。
  146.   */
  147. void temper_control_service(void) //温控程序
  148. {
  149.    if(ucSignFlag==0) //是正数的前提下
  150.    {
  151.       if(ulCurrentTemper>=ulSetTemper)  //当实际温度大于等于设定温度时
  152.       {
  153.         led_dr=0; //模拟继电器的LED灯熄灭
  154.       }
  155.       else if(ulCurrentTemper<=(ulSetTemper-2))  //当实际温度小于等于设定温度2读以下时,这里的2是缓冲温差2度
  156.       {
  157.         led_dr=1; //模拟继电器的LED灯点亮
  158.       }
  159.    }
  160.    else  //是负数,说明是零下多少度的情况下
  161.    {
  162.       led_dr=1; //模拟继电器的LED灯点亮
  163.    }
  164.  
  165. }
  166.  
  167.  
  168. void ds18b20_sampling(void) //ds18b20采样程序
  169. {
  170.  
  171.       ++uiSampingCnt;  //累计主循环次数的时间
  172.       if(uiSampingCnt>const_ds18b20_sampling_time)  //每隔一段时间就更新采集一次Ds18b20数据
  173.           {
  174.           uiSampingCnt=0;
  175.  
  176.           ET0=0;  //禁止定时中断
  177.           uiTemperTemp=get_temper();  //读取一次没有经过换算的温度数值
  178.           ET0=1; //开启定时中断
  179.  
  180.           if((uiTemperTemp&0xf800)==0xf800) //是负号
  181.           {
  182.                          ucSignFlag=1;
  183.  
  184.              uiTemperTemp=~uiTemperTemp;  //求补码
  185.              uiTemperTemp=uiTemperTemp+1;
  186.  
  187.           }
  188.           else //是正号
  189.           {
  190.                          ucSignFlag=0;
  191.  
  192.           }
  193.  
  194.  
  195.  
  196.           ulCurrentTemper=0; //把int数据类型赋给long类型之前要先清零
  197.           ulCurrentTemper=uiTemperTemp; 
  198.  
  199.           ulCurrentTemper=ulCurrentTemper*10; //为了先保留一位小数点,所以放大10倍,
  200.           ulCurrentTemper=ulCurrentTemper>>4;  //往右边移动4位,相当于乘以0.0625. 此时保留了1位小数点,
  201.  
  202.           ulCurrentTemper=ulCurrentTemper+5;  //四舍五入
  203.           ulCurrentTemper=ulCurrentTemper/10; //四舍五入后,去掉小数点
  204.  
  205.           ucWd1Part2Update=1; //局部2更新显示实时温度
  206.           }
  207. }
  208.  
  209.  
  210. //ds18b20驱动程序
  211. unsigned int get_temper()  //读取一次没有经过换算的温度数值
  212. {
  213. unsigned char temper_H;
  214. unsigned char temper_L;
  215. unsigned int ds18b20_data=0;
  216.  
  217. ds18b20_reset(); //复位ds18b20的时序
  218. ds_write_byte(0xCC);
  219. ds_write_byte(0x44);
  220.  
  221. ds18b20_reset(); //复位ds18b20的时序
  222. ds_write_byte(0xCC);
  223. ds_write_byte(0xBE);
  224. temper_L=ds_read_byte();
  225. temper_H=ds_read_byte();
  226.  
  227. ds18b20_data=temper_H;     //把两个字节合并成一个int数据类型
  228. ds18b20_data=ds18b20_data<<8;
  229. ds18b20_data=ds18b20_data|temper_L;
  230. return ds18b20_data;
  231. }
  232.  
  233.  
  234.  
  235. void ds18b20_reset() //复位ds18b20的时序
  236. {
  237.   unsigned char x;
  238.   dq_dr_sr=1;
  239.   delay_short(8);
  240.   dq_dr_sr=0;
  241.   delay_short(80);
  242.   dq_dr_sr=1;
  243.   delay_short(14);
  244.   x=dq_dr_sr;
  245.   delay_short(20);
  246.  
  247. }
  248.  
  249. void ds_write_byte(unsigned char date) //写一个字节
  250. {
  251. unsigned char  i;
  252.  
  253. for(i=0;i<8;i++)
  254. {
  255.   dq_dr_sr=0;
  256.   dq_dr_sr=date&0x01;
  257.   delay_short(5);
  258.   dq_dr_sr=1;
  259.   date=date>>1;
  260. }
  261. }
  262.  
  263. unsigned char ds_read_byte(void ) //读一字节
  264. {
  265. unsigned char i;
  266. unsigned char date=0;
  267. for(i=0;i<8;i++)
  268. {
  269.   dq_dr_sr=0;
  270.   date=date>>1;
  271.   dq_dr_sr=1;
  272.   if(dq_dr_sr)
  273.   {
  274.      date=date|0x80;
  275.   }
  276.   delay_short(5);
  277. }
  278. return (date);
  279. }
  280.  
  281.  
  282.  
  283. void display_service(void) //显示的窗口菜单服务程序
  284. {
  285.  
  286.    switch(ucWd)  //因为本程序只有1个窗口,在实际项目中,此处的ucWd也可以省略不要
  287.    {
  288.        case 1:  
  289.  
  290.                         if(ucWd1Part1Update==1)//局部设定温度更新显示
  291.                         {
  292.                            ucWd1Part1Update=0;
  293.  
  294.                ucTemp8=10; //显示空
  295.  
  296.                            if(ulSetTemper>=100)
  297.                            {
  298.                   ucTemp7=ulSetTemper%1000/100; //显示设定温度的百位
  299.                            }
  300.                            else
  301.                            {
  302.                               ucTemp7=10; //显示空
  303.                            }
  304.  
  305.                            if(ulSetTemper>=10)
  306.                            {
  307.                   ucTemp6=ulSetTemper%100/10; //显示设定温度的十位
  308.                            }
  309.                            else
  310.                            {
  311.                               ucTemp6=10; //显示空
  312.                            }
  313.  
  314.                ucTemp5=ulSetTemper%10; //显示设定温度的个位
  315.  
  316.  
  317.                ucDigShow8=ucTemp8; //数码管显示实际内容
  318.                ucDigShow7=ucTemp7; 
  319.                ucDigShow6=ucTemp6; 
  320.                ucDigShow5=ucTemp5; 
  321.                         }
  322.  
  323.  
  324.                         if(ucWd1Part2Update==1)//局部实际温度更新显示
  325.                         {
  326.                            if(ucSignFlag==0)  //正数
  327.                            {
  328.                   ucTemp4=10; //显示空
  329.                            }
  330.                            else  //负数,说明是零下多少度的情况下
  331.                            {
  332.                   ucTemp4=11; //显示负号-
  333.                            }
  334.  
  335.                            if(ulCurrentTemper>=100)
  336.                            {
  337.                   ucTemp3=ulCurrentTemper%100/100; //显示实际温度的百位
  338.                            }
  339.                            else
  340.                            { 
  341.                           ucTemp3=10; //显示空
  342.                            }
  343.  
  344.  
  345.                            if(ulCurrentTemper>=10)
  346.                            {
  347.                   ucTemp2=ulCurrentTemper%100/10;  //显示实际温度的十位
  348.                            }
  349.                            else
  350.                            {
  351.                   ucTemp2=10;  //显示空
  352.                            }
  353.  
  354.                ucTemp1=ulCurrentTemper%10; //显示实际温度的个数位
  355.  
  356.                ucDigShow4=ucTemp4; //数码管显示实际内容
  357.                ucDigShow3=ucTemp3; 
  358.                ucDigShow2=ucTemp2; 
  359.                ucDigShow1=ucTemp1; 
  360.                         }
  361.  
  362.             break;
  363.  
  364.       }
  365.    
  366.  
  367. }
  368.  
  369. void key_scan(void)//按键扫描函数 放在定时中断里
  370. {  
  371.   if(key_sr1==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
  372.   {
  373.      ucKeyLock1=0; //按键自锁标志清零
  374.      uiKeyTimeCnt1=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。      
  375.   }
  376.   else if(ucKeyLock1==0)//有按键按下,且是第一次被按下
  377.   {
  378.      uiKeyTimeCnt1++; //累加定时中断次数
  379.      if(uiKeyTimeCnt1>const_key_time1)
  380.      {
  381.         uiKeyTimeCnt1=0; 
  382.         ucKeyLock1=1;  //自锁按键置位,避免一直触发
  383.         ucKeySec=1;    //触发1号键
  384.      }
  385.   }
  386.  
  387.   if(key_sr2==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
  388.   {
  389.      ucKeyLock2=0; //按键自锁标志清零
  390.      uiKeyTimeCnt2=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。      
  391.   }
  392.   else if(ucKeyLock2==0)//有按键按下,且是第一次被按下
  393.   {
  394.      uiKeyTimeCnt2++; //累加定时中断次数
  395.      if(uiKeyTimeCnt2>const_key_time2)
  396.      {
  397.         uiKeyTimeCnt2=0; 
  398.         ucKeyLock2=1;  //自锁按键置位,避免一直触发
  399.         ucKeySec=2;    //触发2号键
  400.      }
  401.   }
  402.  
  403.  
  404.  
  405.  
  406.  
  407. }
  408.  
  409. void key_service(void) //按键服务的应用程序
  410. {
  411.  
  412.   switch(ucKeySec) //按键服务状态切换
  413.   {
  414.     case 1:// 加按键 对应朱兆祺学习板的S1键 
  415.           switch(ucWd) //因为本程序只有1个窗口,在实际项目中,此处的ucWd也可以省略不要
  416.           {
  417.               case 1: //在窗口1下设置设定温度
  418.                    ulSetTemper++;
  419.                                    if(ulSetTemper>125)
  420.                                    {
  421.                                      ulSetTemper=125;
  422.                                    }
  423.  
  424.                                ucWd1Part1Update=1; //更新显示设定温度
  425.                    break;
  426.           }
  427.  
  428.           ucVoiceLock=1;  //原子锁加锁,保护主函数与中断函数的共享变量uiVoiceCnt
  429.           uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
  430.           ucVoiceLock=0;  //原子锁解锁,保护主函数与中断函数的共享变量uiVoiceCnt
  431.  
  432.           ucKeySec=0;  //响应按键服务处理程序后,按键编号清零,避免一致触发
  433.           break;    
  434.     
  435.     case 2:// 减按键 对应朱兆祺学习板的S5键 
  436.           switch(ucWd) //因为本程序只有1个窗口,在实际项目中,此处的ucWd也可以省略不要
  437.           {
  438.                case 1: //在窗口1下设置设定温度
  439.                     if(ulSetTemper>2)  //由于缓冲温差是2度,所以我人为规定最小允许设定的温度不能低于2度
  440.                                         {
  441.                                            ulSetTemper--;
  442.                                         }
  443.  
  444.                           ucWd1Part1Update=1; //更新显示设定温度
  445.                     break;
  446.          
  447.           }
  448.  
  449.           ucVoiceLock=1;  //原子锁加锁,保护主函数与中断函数的共享变量uiVoiceCnt
  450.           uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
  451.           ucVoiceLock=0;  //原子锁解锁,保护主函数与中断函数的共享变量uiVoiceCnt
  452.  
  453.           ucKeySec=0;  //响应按键服务处理程序后,按键编号清零,避免一致触发
  454.           break;  
  455.  
  456.  
  457.          
  458.   }         
  459.   
  460.  
  461. }
  462.  
  463. void display_drive(void)  
  464. {
  465.    //以下程序,如果加一些数组和移位的元素,还可以压缩容量。但是鸿哥追求的不是容量,而是清晰的讲解思路
  466.    switch(ucDisplayDriveStep)
  467.    { 
  468.       case 1:  //显示第1位
  469.            ucDigShowTemp=dig_table[ucDigShow1];
  470.                    if(ucDigDot1==1)
  471.                    {
  472.                       ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点
  473.                    }
  474.            dig_hc595_drive(ucDigShowTemp,0xfe);
  475.                break;
  476.       case 2:  //显示第2位
  477.            ucDigShowTemp=dig_table[ucDigShow2];
  478.                    if(ucDigDot2==1)
  479.                    {
  480.                       ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点
  481.                    }
  482.            dig_hc595_drive(ucDigShowTemp,0xfd);
  483.                break;
  484.       case 3:  //显示第3位
  485.            ucDigShowTemp=dig_table[ucDigShow3];
  486.                    if(ucDigDot3==1)
  487.                    {
  488.                       ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点
  489.                    }
  490.            dig_hc595_drive(ucDigShowTemp,0xfb);
  491.                break;
  492.       case 4:  //显示第4位
  493.            ucDigShowTemp=dig_table[ucDigShow4];
  494.                    if(ucDigDot4==1)
  495.                    {
  496.                       ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点
  497.                    }
  498.            dig_hc595_drive(ucDigShowTemp,0xf7);
  499.                break;
  500.       case 5:  //显示第5位
  501.            ucDigShowTemp=dig_table[ucDigShow5];
  502.                    if(ucDigDot5==1)
  503.                    {
  504.                       ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点
  505.                    }
  506.            dig_hc595_drive(ucDigShowTemp,0xef);
  507.                break;
  508.       case 6:  //显示第6位
  509.            ucDigShowTemp=dig_table[ucDigShow6];
  510.                    if(ucDigDot6==1)
  511.                    {
  512.                       ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点
  513.                    }
  514.            dig_hc595_drive(ucDigShowTemp,0xdf);
  515.                break;
  516.       case 7:  //显示第7位
  517.            ucDigShowTemp=dig_table[ucDigShow7];
  518.                    if(ucDigDot7==1)
  519.                    {
  520.                       ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点
  521.            }
  522.            dig_hc595_drive(ucDigShowTemp,0xbf);
  523.                break;
  524.       case 8:  //显示第8位
  525.            ucDigShowTemp=dig_table[ucDigShow8];
  526.                    if(ucDigDot8==1)
  527.                    {
  528.                       ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点
  529.                    }
  530.            dig_hc595_drive(ucDigShowTemp,0x7f);
  531.                break;
  532.    }
  533.    ucDisplayDriveStep++;
  534.    if(ucDisplayDriveStep>8)  //扫描完8个数码管后,重新从第一个开始扫描
  535.    {
  536.      ucDisplayDriveStep=1;
  537.    }
  538.  
  539. }
  540.  
  541. //数码管的74HC595驱动函数
  542. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)
  543. {
  544.    unsigned char i;
  545.    unsigned char ucTempData;
  546.    dig_hc595_sh_dr=0;
  547.    dig_hc595_st_dr=0;
  548.    ucTempData=ucDigStatusTemp16_09;  //先送高8位
  549.    for(i=0;i<8;i++)
  550.    { 
  551.          if(ucTempData>=0x80)dig_hc595_ds_dr=1;
  552.          else dig_hc595_ds_dr=0;
  553.          dig_hc595_sh_dr=0;     //SH引脚的上升沿把数据送入寄存器
  554.          delay_short(1); 
  555.          dig_hc595_sh_dr=1;
  556.          delay_short(1);
  557.          ucTempData=ucTempData<<1;
  558.    }
  559.    ucTempData=ucDigStatusTemp08_01;  //再先送低8位
  560.    for(i=0;i<8;i++)
  561.    { 
  562.          if(ucTempData>=0x80)dig_hc595_ds_dr=1;
  563.          else dig_hc595_ds_dr=0;
  564.          dig_hc595_sh_dr=0;     //SH引脚的上升沿把数据送入寄存器
  565.          delay_short(1); 
  566.          dig_hc595_sh_dr=1;
  567.          delay_short(1);
  568.          ucTempData=ucTempData<<1;
  569.    }
  570.    dig_hc595_st_dr=0;  //ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上并且锁存起来
  571.    delay_short(1); 
  572.    dig_hc595_st_dr=1;
  573.    delay_short(1);
  574.    dig_hc595_sh_dr=0;    //拉低,抗干扰就增强
  575.    dig_hc595_st_dr=0;
  576.    dig_hc595_ds_dr=0;
  577. }
  578.  
  579. //LED灯的74HC595驱动函数
  580. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
  581. {
  582.    unsigned char i;
  583.    unsigned char ucTempData;
  584.    hc595_sh_dr=0;
  585.    hc595_st_dr=0;
  586.    ucTempData=ucLedStatusTemp16_09;  //先送高8位
  587.    for(i=0;i<8;i++)
  588.    { 
  589.          if(ucTempData>=0x80)hc595_ds_dr=1;
  590.          else hc595_ds_dr=0;
  591.          hc595_sh_dr=0;     //SH引脚的上升沿把数据送入寄存器
  592.          delay_short(1); 
  593.          hc595_sh_dr=1;
  594.          delay_short(1);
  595.          ucTempData=ucTempData<<1;
  596.    }
  597.    ucTempData=ucLedStatusTemp08_01;  //再先送低8位
  598.    for(i=0;i<8;i++)
  599.    { 
  600.          if(ucTempData>=0x80)hc595_ds_dr=1;
  601.          else hc595_ds_dr=0;
  602.          hc595_sh_dr=0;     //SH引脚的上升沿把数据送入寄存器
  603.          delay_short(1); 
  604.          hc595_sh_dr=1;
  605.          delay_short(1);
  606.          ucTempData=ucTempData<<1;
  607.    }
  608.    hc595_st_dr=0;  //ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上并且锁存起来
  609.    delay_short(1); 
  610.    hc595_st_dr=1;
  611.    delay_short(1);
  612.    hc595_sh_dr=0;    //拉低,抗干扰就增强
  613.    hc595_st_dr=0;
  614.    hc595_ds_dr=0;
  615. }
  616.  
  617.  
  618. void T0_time(void) interrupt 1   //定时中断
  619. {
  620.   TF0=0;  //清除中断标志
  621.   TR0=0; //关中断
  622.  
  623.  
  624.   if(ucVoiceLock==0) //原子锁判断
  625.   {
  626.      if(uiVoiceCnt!=0)
  627.      {
  628.  
  629.         uiVoiceCnt--; //每次进入定时中断都自减1,直到等于零为止。才停止鸣叫
  630.         beep_dr=0;  //蜂鸣器是PNP三极管控制,低电平就开始鸣叫。
  631.      
  632.      }
  633.      else
  634.      {
  635.  
  636.         ; //此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。
  637.         beep_dr=1;  //蜂鸣器是PNP三极管控制,高电平就停止鸣叫。
  638.         
  639.      }
  640.   }
  641.  
  642.  
  643.   key_scan(); //按键扫描函数
  644.   display_drive();  //数码管字模的驱动函数
  645.  
  646.   TH0=0xfe;   //重装初始值(65535-500)=65035=0xfe0b
  647.   TL0=0x0b;
  648.   TR0=1;  //开中断
  649. }
  650.  
  651. void delay_short(unsigned int uiDelayShort) 
  652. {
  653.    unsigned int i;  
  654.    for(i=0;i
  655.    {
  656.      ;   //一个分号相当于执行一条空语句
  657.    }
  658. }
  659.  
  660. void delay_long(unsigned int uiDelayLong)
  661. {
  662.    unsigned int i;
  663.    unsigned int j;
  664.    for(i=0;i
  665.    {
  666.       for(j=0;j<500;j++)  //内嵌循环的空指令数量
  667.           {
  668.              ; //一个分号相当于执行一条空语句
  669.           }
  670.    }
  671. }
  672.  
  673.  
  674. void initial_myself(void)  //第一区 初始化单片机
  675. {
  676.   led_dr=0;//此处的LED灯模拟工控中的继电器
  677.   key_gnd_dr=0; //模拟独立按键的地GND,因此必须一直输出低电平
  678.   beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。
  679.   hc595_drive(0x00,0x00);  //关闭所有经过另外两个74HC595驱动的LED灯
  680.   TMOD=0x01;  //设置定时器0为工作方式1
  681.   TH0=0xfe;   //重装初始值(65535-500)=65035=0xfe0b
  682.   TL0=0x0b;
  683.  
  684. }
  685. void initial_peripheral(void) //第二区 初始化外围
  686. {
  687.  
  688.    ucDigDot8=0;   //小数点全部不显示
  689.    ucDigDot7=0;  
  690.    ucDigDot6=0; 
  691.    ucDigDot5=0;  
  692.    ucDigDot4=0; 
  693.    ucDigDot3=0;  
  694.    ucDigDot2=0;
  695.    ucDigDot1=0;
  696.  
  697.    EA=1;     //开总中断
  698.    ET0=1;    //允许定时中断
  699.    TR0=1;    //启动定时中断
  700.  
  701. }
  702.  

总结陈词:
下一节开始讲单片机采集模拟信号的内容,欲知详情,请听下回分解-----利用ADC0832采集电压的模拟信号。

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
实时控制.安全.如何加速实现未来工厂落地?