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

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

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

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

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

(3)源代码讲解如下:

#include "REG52.H"

/* 注释一:
* 如何知道1秒钟需要多少个定时中断?
* 这个需要编写一段小程序测试,得到测试的结果后再按比例修正。
* 步骤:
* 第一步:在程序代码上先写入1秒钟大概需要200个定时中断。
* 第二步:基于以上1秒钟的基准,编写一个60秒的简单测试程序(如果编写超过
* 60秒的时间,这个精度还会更高)。比如,编写一个用蜂鸣器的声音来识别计时的
* 起始和终止的测试程序。
* 第三步:把程序烧录进单片机后,上电开始测试,手上同步打开手机里的秒表。
*         如果单片机仅仅跑了27秒。
* 第四步:那么最终得出1秒钟需要的定时中断次数是:const_time_1s=(200*60)/27=444
*/
#define const_time_05s 222   //0.5秒钟的时间需要的定时中断次数
#define const_time_1s 444   //1秒钟的时间需要的定时中断次数
#define const_time_3s 1332   //3秒钟的时间需要的定时中断次数
#define const_time_6s 2664   //6秒钟的时间需要的定时中断次数

#define const_voice_short  40   //蜂鸣器短叫的持续时间
#define const_voice_long   200  //蜂鸣器长叫的持续时间

void initial_myself();    
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void led_flicker();
void alarm_run();   
void T0_time();  //定时中断函数

sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
sbit led_dr=P3^5;  //LED灯的驱动IO口

unsigned char ucLedStep=0; //LED灯的步骤变量
unsigned int  uiTimeLedCnt=0; //LED灯统计定时中断次数的延时计数器

unsigned char ucAlarmStep=0; //报警的步骤变量
unsigned int  uiTimeAlarmCnt=0; //报警统计定时中断次数的延时计数器

unsigned int  uiVoiceCnt=0;  //蜂鸣器鸣叫的持续时间计数器

void main() 
  {
   initial_myself();  
   delay_long(100);   
   initial_peripheral(); 
   while(1)  
   { 
      led_flicker();  //第一个任务LED灯闪烁
          alarm_run();    //第二个任务报警器定时报警
   }

}

void led_flicker() //第三区 LED闪烁应用程序
{
  
  switch(ucLedStep)
  {
     case 0:

if(uiTimeLedCnt>=const_time_05s) //时间到
           {
             uiTimeLedCnt=0; //时间计数器清零
             led_dr=1;    //让LED亮
             ucLedStep=1; //切换到下一个步骤
           }
           break;
     case 1:
           if(uiTimeLedCnt>=const_time_05s) //时间到
           {
              uiTimeLedCnt=0; //时间计数器清零
              led_dr=0;    //让LED灭
              ucLedStep=0; //返回到上一个步骤
           }
           break;
  }

}

void alarm_run() //第三区 报警器的应用程序
{
  
  switch(ucAlarmStep)
  {
     case 0:

if(uiTimeAlarmCnt>=const_time_3s) //时间到
           {
             uiTimeAlarmCnt=0; //时间计数器清零
/* 注释二:
* 只要变量uiVoiceCnt不为0,蜂鸣器就会在定时中断函数里启动鸣叫,并且自减uiVoiceCnt
* 直到uiVoiceCnt为0时才停止鸣叫。因此控制uiVoiceCnt变量的大小就是控制声音的长短。
*/
             uiVoiceCnt=const_voice_short;  //蜂鸣器短叫
             ucAlarmStep=1; //切换到下一个步骤
           }
           break;
     case 1:
           if(uiTimeAlarmCnt>=const_time_6s) //时间到
           {
              uiTimeAlarmCnt=0; //时间计数器清零
              uiVoiceCnt=const_voice_long;  //蜂鸣器长叫
              ucAlarmStep=0; //返回到上一个步骤
           }
           break;
  }

}

void T0_time() interrupt 1
{
  TF0=0;  //清除中断标志
  TR0=0; //关中断

if(uiTimeLedCnt<0xffff)  //设定这个条件,防止uiTimeLedCnt超范围。
  {
      uiTimeLedCnt++;  //LED灯的时间计数器,累加定时中断的次数,
  }

if(uiTimeAlarmCnt<0xffff)  //设定这个条件,防止uiTimeAlarmCnt超范围。
  {
      uiTimeAlarmCnt++;  //报警的时间计数器,累加定时中断的次数,
  }

/* 注释三:
* 为什么不把驱动蜂鸣器这段代码放到main函数的循环里去?
* 因为放在定时中断里,能保证蜂鸣器的声音长度是一致的,
* 如果放在main循环里,声音的长度就有可能受到某些必须
* 一气呵成的任务干扰,得不到及时响应,影响声音长度的一致性。
*/

if(uiVoiceCnt!=0)
  {
     uiVoiceCnt--; //每次进入定时中断都自减1,直到等于零为止。才停止鸣叫
         beep_dr=0;  //蜂鸣器是PNP三极管控制,低电平就开始鸣叫。
  }
  else
  {
     ; //此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。
           beep_dr=1;  //蜂鸣器是PNP三极管控制,高电平就停止鸣叫。
  }

TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f
  TL0=0x2f;
  TR0=1;  //开中断
}

void delay_long(unsigned int uiDelayLong)
{
   unsigned int i;
   unsigned int j;
   for(i=0;i<uiDelayLong;i++)
   {
      for(j=0;j<500;j++)  //内嵌循环的空指令数量
          {
             ; //一个分号相当于执行一条空语句
          }
   }
}

void initial_myself()  //第一区 初始化单片机
{
  beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。
  led_dr=0;  //LED灭

TMOD=0x01;  //设置定时器0为工作方式1

TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f
  TL0=0x2f;

}
void initial_peripheral() //第二区 初始化外围
{
  EA=1;     //开总中断
  ET0=1;    //允许定时中断
  TR0=1;    //启动定时中断

}

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

(未完待续,下节更精彩,不要走开哦)

第五节:蜂鸣器的驱动程序相关推荐

  1. 第五节 LCD12864显示

    第五节  LCD12864显示 上一节我们成功控制了LED和蜂鸣器,这一节我们马不停蹄接着LCD12864的控制. 为了系统能够稳定的工作,首先我们将系统时钟切换到32M的外部晶振,为了自由配置所需要 ...

  2. 第五节 RabbitMQ在C#端的应用-消息收发

    原文:第五节 RabbitMQ在C#端的应用-消息收发 版权声明:未经本人同意,不得转载该文章,谢谢 https://blog.csdn.net/phocus1/article/details/873 ...

  3. 【java】兴唐课程第五节到第九节知识点总结

    第九节 1. 代码:void readBook(String- bookNames) 表示不确定参数的个数,此时变量为一个数组. 2.当方法中的参数名称(如stuname)和属性名称相同时. this ...

  4. 第12课第3节 字符设备驱动程序之查询方式的按键驱动程序

    第12课第3节 字符设备驱动程序之查询方式的按键驱动程序 cat /proc/devices //查询主设备号 insmod ./second_drv.ko ls /dev/button -l pos ...

  5. Simulink仿真 第五节 复用器和分路器

    第五节 复用器和分路器 1.复用器(将多个输入信号合并为矢量) Mux 模块可将其输入合并为单个矢量输出.输入可以是标量或矢量信号.所有输入都必须具有相同的数据类型和数值类型.矢量输出信号元素采用输入 ...

  6. Python编程基础:第五十五节 map函数Map

    第五十五节 map函数Map 前言 实践 前言 map函数的作用是将指定函数作用于一个可迭代对象内部的每一个元素,其表达方式为map(function, iterable),第一个位置指定作用函数,第 ...

  7. Python编程基础:第四十五节 方法链Method Chaining

    第四十五节 方法链Method Chaining 前言 实践 前言 方法链是指一个对象一次调用其自身的多个方法,通常写作对象.方法1.方法2.由于这种调用方法看起来像一个链条,所以我们将其称作方法链. ...

  8. Python编程基础:第三十五节 文件删除Delete a File

    第三十五节 文件删除Delete a File 前言 实践 前言 我们这一节来介绍如何删除一个文件,这里需要用到函数os.remove(path)用于删除指定路径下的文件,os.rmdir(path) ...

  9. Python编程基础:第二十五节 args参数*args

    第二十五节 args参数*args 前言 实践 前言 我们目前学习到的函数的参数个数都是固定的,那么我们是否可以指定任意多个参数呢?其实是可以的,这里就用到了args参数,它可以将用户指定的任意多个参 ...

  10. Python编程基础:第十五节 二维列表2D Lists

    第十五节 二维列表2D Lists 前言 实践 前言 列表中的元素可以是任何形式,整型.浮点型.字符串型,甚至是一个列表.当列表的元素也是列表时,我们将其称为二维列表. 实践 我们先来创建多个一维列表 ...

最新文章

  1. git clone early EOF解决方法
  2. Coursera课程Python for everyone:chapter6
  3. officeopenxml excelpackage 需要安装excel嘛_使用ABAP操作Excel的几种方法
  4. 团队作业4——第一次项目冲刺(Alpha版本)3rd day
  5. Rabbitmq集群高可用部署详细
  6. 基于Bootstrap 3.x的免费高级管理控制面板主题:AdminLTE
  7. IOS学习笔记之十七 (NSDate、NSDateFormatter、NSCalendar、NSDateComponents、NSTimer)
  8. Wing IDE 5.0 破解之寻找注册码
  9. 网页动态加载图片 通过JS和jquery实现。
  10. ubuntu用户目录安装pip(无root权限)
  11. Python使用BoundedSemaphore对象进行线程同步
  12. iOS中的gzip解压
  13. Linux时间戳与日期相互转换
  14. 解决Windows10 14393版本迅雷崩溃问题
  15. linux scl,技术|SCL项目:在Red Hat系列系统上安装同一软件的不同版本
  16. 3D全景图php代码,HTML5 Canvas实现360度全景图的示例代码
  17. nginx本地代理调试微信登录(window版本)
  18. 网易邮箱服务器邮箱协议,怎么用网易闪电邮IMAP协议同步网页邮箱?
  19. 优思学院:ILSSI 的六西格玛黑带大师(MBB)
  20. 报表可视化源码,操作界面可视化,通过简易配置即可自动生成各种类型的报表,为企业节省大量的重复开发工作

热门文章

  1. 极光一键登录集成手册 | Android
  2. 使用java.lang.String报错之Expected 0 arguments but found 1
  3. openwrt-mt7628 wds配置
  4. 根据qq宠物等级计算级别
  5. linux操作系统原理与应用.第2版 陈莉君 pdf
  6. C语言左右对齐、占位
  7. IDEA 设置前进,后退快捷键
  8. linux ubuntu设置中文,ubuntu 中文设置 (LANG设置)
  9. 【scratch案例教学】scratch中秋佳节 scratch创意编程 少儿编程 边玩边学 小朋友这样贺中秋
  10. 再不学点东西我们就老了