第五节:蜂鸣器的驱动程序。

开场白:
上一节讲了利用累计定时中断次数实现LED灯闪烁,这个例子同时也第一次展示了我最完整的实战程序框架:用switch语句实现状态机,外加定时中断。这个框架看似简单,实际上就是那么简单。我做的所有开发项目都是基于这个简单框架,但是非常好用。上一节只有一个单任务的LED灯在闪烁,这节开始,我们多增加一个蜂鸣器报警的任务,要教会大家四个知识点:
第一点:蜂鸣器的驱动程序框架编写。
第二点:多任务处理的程序框架。
第三点:如何控制蜂鸣器声音的长叫和短叫。
第四点:如何知道1秒钟需要多少个定时中断,也就是如何按比例修正时间精度。

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

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

(2)实现功能:同时跑两个任务,第一个任务让一个LED灯1秒钟闪烁一次。第二个任务让蜂鸣器在前面3秒发生一次短叫报警,在后面6秒发生一次长叫报警,反复循环。

(3)源代码讲解如下:

  1. #include "REG52.H"  
  2.   
  3. /* 注释一: 
  4. * 如何知道1秒钟需要多少个定时中断? 
  5. * 这个需要编写一段小程序测试,得到测试的结果后再按比例修正。 
  6. * 步骤: 
  7. * 第一步:在程序代码上先写入1秒钟大概需要200个定时中断。 
  8. * 第二步:基于以上1秒钟的基准,编写一个60秒的简单测试程序(如果编写超过 
  9. * 60秒的时间,这个精度还会更高)。比如,编写一个用蜂鸣器的声音来识别计时的 
  10. * 起始和终止的测试程序。 
  11. * 第三步:把程序烧录进单片机后,上电开始测试,手上同步打开手机里的秒表。 
  12. *         如果单片机仅仅跑了27秒。 
  13. * 第四步:那么最终得出1秒钟需要的定时中断次数是:const_time_1s=(200*60)/27=444 
  14. */  
  15. #define const_time_05s 222   //0.5秒钟的时间需要的定时中断次数  
  16. #define const_time_1s 444   //1秒钟的时间需要的定时中断次数  
  17. #define const_time_3s 1332   //3秒钟的时间需要的定时中断次数  
  18. #define const_time_6s 2664   //6秒钟的时间需要的定时中断次数  
  19.   
  20. #define const_voice_short  40   //蜂鸣器短叫的持续时间  
  21. #define const_voice_long   200  //蜂鸣器长叫的持续时间  
  22.   
  23. void initial_myself();      
  24. void initial_peripheral();  
  25. void delay_long(unsigned int uiDelaylong);  
  26. void led_flicker();  
  27. void alarm_run();     
  28. void T0_time();  //定时中断函数  
  29.   
  30. sbit beep_dr=P2^7; //蜂鸣器的驱动IO口  
  31. sbit led_dr=P3^5;  //LED灯的驱动IO口  
  32.   
  33. unsigned char ucLedStep=0; //LED灯的步骤变量  
  34. unsigned int  uiTimeLedCnt=0; //LED灯统计定时中断次数的延时计数器  
  35.   
  36. unsigned char ucAlarmStep=0; //报警的步骤变量  
  37. unsigned int  uiTimeAlarmCnt=0; //报警统计定时中断次数的延时计数器  
  38.   
  39. unsigned int  uiVoiceCnt=0;  //蜂鸣器鸣叫的持续时间计数器  
  40.   
  41. void main()   
  42.   {  
  43.    initial_myself();    
  44.    delay_long(100);     
  45.    initial_peripheral();   
  46.    while(1)    
  47.    {   
  48.       led_flicker();  //第一个任务LED灯闪烁  
  49.           alarm_run();    //第二个任务报警器定时报警  
  50.    }  
  51.   
  52. }  
  53.   
  54. void led_flicker() //第三区 LED闪烁应用程序  
  55. {  
  56.     
  57.   switch(ucLedStep)  
  58.   {  
  59.      case 0:  
  60.   
  61.            if(uiTimeLedCnt>=const_time_05s) //时间到  
  62.            {  
  63.              uiTimeLedCnt=0; //时间计数器清零  
  64.              led_dr=1;    //让LED亮  
  65.              ucLedStep=1; //切换到下一个步骤  
  66.            }  
  67.            break;  
  68.      case 1:  
  69.            if(uiTimeLedCnt>=const_time_05s) //时间到  
  70.            {  
  71.               uiTimeLedCnt=0; //时间计数器清零  
  72.               led_dr=0;    //让LED灭  
  73.               ucLedStep=0; //返回到上一个步骤  
  74.            }  
  75.            break;  
  76.   }  
  77.   
  78. }  
  79.   
  80. void alarm_run() //第三区 报警器的应用程序  
  81. {  
  82.     
  83.   switch(ucAlarmStep)  
  84.   {  
  85.      case 0:  
  86.   
  87.            if(uiTimeAlarmCnt>=const_time_3s) //时间到  
  88.            {  
  89.              uiTimeAlarmCnt=0; //时间计数器清零  
  90. /* 注释二: 
  91. * 只要变量uiVoiceCnt不为0,蜂鸣器就会在定时中断函数里启动鸣叫,并且自减uiVoiceCnt 
  92. * 直到uiVoiceCnt为0时才停止鸣叫。因此控制uiVoiceCnt变量的大小就是控制声音的长短。 
  93. */  
  94.              uiVoiceCnt=const_voice_short;  //蜂鸣器短叫  
  95.              ucAlarmStep=1; //切换到下一个步骤  
  96.            }  
  97.            break;  
  98.      case 1:  
  99.            if(uiTimeAlarmCnt>=const_time_6s) //时间到  
  100.            {  
  101.               uiTimeAlarmCnt=0; //时间计数器清零  
  102.               uiVoiceCnt=const_voice_long;  //蜂鸣器长叫  
  103.               ucAlarmStep=0; //返回到上一个步骤  
  104.            }  
  105.            break;  
  106.   }  
  107.   
  108. }  
  109.   
  110. void T0_time() interrupt 1  
  111. {  
  112.   TF0=0;  //清除中断标志  
  113.   TR0=0; //关中断  
  114.   
  115.   if(uiTimeLedCnt<0xffff)  //设定这个条件,防止uiTimeLedCnt超范围。  
  116.   {  
  117.       uiTimeLedCnt++;  //LED灯的时间计数器,累加定时中断的次数,  
  118.   }  
  119.   
  120.   if(uiTimeAlarmCnt<0xffff)  //设定这个条件,防止uiTimeAlarmCnt超范围。  
  121.   {  
  122.       uiTimeAlarmCnt++;  //报警的时间计数器,累加定时中断的次数,  
  123.   }  
  124.   
  125.   
  126. /* 注释三: 
  127. * 为什么不把驱动蜂鸣器这段代码放到main函数的循环里去? 
  128. * 因为放在定时中断里,能保证蜂鸣器的声音长度是一致的, 
  129. * 如果放在main循环里,声音的长度就有可能受到某些必须 
  130. * 一气呵成的任务干扰,得不到及时响应,影响声音长度的一致性。 
  131. */  
  132.   
  133.   
  134.   if(uiVoiceCnt!=0)  
  135.   {  
  136.      uiVoiceCnt--; //每次进入定时中断都自减1,直到等于零为止。才停止鸣叫  
  137.          beep_dr=0;  //蜂鸣器是PNP三极管控制,低电平就开始鸣叫。  
  138.   }  
  139.   else  
  140.   {  
  141.      ; //此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。  
  142.            beep_dr=1;  //蜂鸣器是PNP三极管控制,高电平就停止鸣叫。  
  143.   }  
  144.   
  145.   
  146.   TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f  
  147.   TL0=0x2f;  
  148.   TR0=1;  //开中断  
  149. }  
  150.   
  151.   
  152. void delay_long(unsigned int uiDelayLong)  
  153. {  
  154.    unsigned int i;  
  155.    unsigned int j;  
  156.    for(i=0;i<uiDelayLong;i++)  
  157.    {  
  158.       for(j=0;j<500;j++)  //内嵌循环的空指令数量  
  159.           {  
  160.              ; //一个分号相当于执行一条空语句  
  161.           }  
  162.    }  
  163. }  
  164.   
  165.   
  166. void initial_myself()  //第一区 初始化单片机  
  167. {  
  168.   beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。  
  169.   led_dr=0;  //LED灭  
  170.   
  171.   TMOD=0x01;  //设置定时器0为工作方式1  
  172.   
  173.   
  174.   TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f  
  175.   TL0=0x2f;  
  176.   
  177. }  
  178. void initial_peripheral() //第二区 初始化外围  
  179. {  
  180.   EA=1;     //开总中断  
  181.   ET0=1;    //允许定时中断  
  182.   TR0=1;    //启动定时中断  
  183.   
  184. }   

总结陈词:
本节程序已经展示了一个多任务处理的基本思路,假如要实现一个独立按键检测,能不能也按照这种思路来处理呢?欲知详情,请听下回分解-----在主函数中利用累计主循环次数来实现独立按键的检测。

 

永不止步步 发表于11-19 21:28 浏览65535次
分享到:

已有0条评论

暂时还没有回复哟,快来抢沙发吧

添加一条新评论

只有登录用户才能评论,请先登录注册哦!

话题作者

永不止步步
金币:67417个|学分:363741个
立即注册
畅学电子网,带你进入电子开发学习世界
专业电子工程技术学习交流社区,加入畅学一起充电加油吧!

x

畅学电子网订阅号