第五节:蜂鸣器的驱动程序
第五节:蜂鸣器的驱动程序。
开场白:
上一节讲了利用累计定时中断次数实现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; //启动定时中断
}
总结陈词:
本节程序已经展示了一个多任务处理的基本思路,假如要实现一个独立按键检测,能不能也按照这种思路来处理呢?欲知详情,请听下回分解-----在主函数中利用累计主循环次数来实现独立按键的检测。
(未完待续,下节更精彩,不要走开哦)
第五节:蜂鸣器的驱动程序相关推荐
- 第五节 LCD12864显示
第五节 LCD12864显示 上一节我们成功控制了LED和蜂鸣器,这一节我们马不停蹄接着LCD12864的控制. 为了系统能够稳定的工作,首先我们将系统时钟切换到32M的外部晶振,为了自由配置所需要 ...
- 第五节 RabbitMQ在C#端的应用-消息收发
原文:第五节 RabbitMQ在C#端的应用-消息收发 版权声明:未经本人同意,不得转载该文章,谢谢 https://blog.csdn.net/phocus1/article/details/873 ...
- 【java】兴唐课程第五节到第九节知识点总结
第九节 1. 代码:void readBook(String- bookNames) 表示不确定参数的个数,此时变量为一个数组. 2.当方法中的参数名称(如stuname)和属性名称相同时. this ...
- 第12课第3节 字符设备驱动程序之查询方式的按键驱动程序
第12课第3节 字符设备驱动程序之查询方式的按键驱动程序 cat /proc/devices //查询主设备号 insmod ./second_drv.ko ls /dev/button -l pos ...
- Simulink仿真 第五节 复用器和分路器
第五节 复用器和分路器 1.复用器(将多个输入信号合并为矢量) Mux 模块可将其输入合并为单个矢量输出.输入可以是标量或矢量信号.所有输入都必须具有相同的数据类型和数值类型.矢量输出信号元素采用输入 ...
- Python编程基础:第五十五节 map函数Map
第五十五节 map函数Map 前言 实践 前言 map函数的作用是将指定函数作用于一个可迭代对象内部的每一个元素,其表达方式为map(function, iterable),第一个位置指定作用函数,第 ...
- Python编程基础:第四十五节 方法链Method Chaining
第四十五节 方法链Method Chaining 前言 实践 前言 方法链是指一个对象一次调用其自身的多个方法,通常写作对象.方法1.方法2.由于这种调用方法看起来像一个链条,所以我们将其称作方法链. ...
- Python编程基础:第三十五节 文件删除Delete a File
第三十五节 文件删除Delete a File 前言 实践 前言 我们这一节来介绍如何删除一个文件,这里需要用到函数os.remove(path)用于删除指定路径下的文件,os.rmdir(path) ...
- Python编程基础:第二十五节 args参数*args
第二十五节 args参数*args 前言 实践 前言 我们目前学习到的函数的参数个数都是固定的,那么我们是否可以指定任意多个参数呢?其实是可以的,这里就用到了args参数,它可以将用户指定的任意多个参 ...
- Python编程基础:第十五节 二维列表2D Lists
第十五节 二维列表2D Lists 前言 实践 前言 列表中的元素可以是任何形式,整型.浮点型.字符串型,甚至是一个列表.当列表的元素也是列表时,我们将其称为二维列表. 实践 我们先来创建多个一维列表 ...
最新文章
- git clone early EOF解决方法
- Coursera课程Python for everyone:chapter6
- officeopenxml excelpackage 需要安装excel嘛_使用ABAP操作Excel的几种方法
- 团队作业4——第一次项目冲刺(Alpha版本)3rd day
- Rabbitmq集群高可用部署详细
- 基于Bootstrap 3.x的免费高级管理控制面板主题:AdminLTE
- IOS学习笔记之十七 (NSDate、NSDateFormatter、NSCalendar、NSDateComponents、NSTimer)
- Wing IDE 5.0 破解之寻找注册码
- 网页动态加载图片 通过JS和jquery实现。
- ubuntu用户目录安装pip(无root权限)
- Python使用BoundedSemaphore对象进行线程同步
- iOS中的gzip解压
- Linux时间戳与日期相互转换
- 解决Windows10 14393版本迅雷崩溃问题
- linux scl,技术|SCL项目:在Red Hat系列系统上安装同一软件的不同版本
- 3D全景图php代码,HTML5 Canvas实现360度全景图的示例代码
- nginx本地代理调试微信登录(window版本)
- 网易邮箱服务器邮箱协议,怎么用网易闪电邮IMAP协议同步网页邮箱?
- 优思学院:ILSSI 的六西格玛黑带大师(MBB)
- 报表可视化源码,操作界面可视化,通过简易配置即可自动生成各种类型的报表,为企业节省大量的重复开发工作
热门文章
- 极光一键登录集成手册 | Android
- 使用java.lang.String报错之Expected 0 arguments but found 1
- openwrt-mt7628 wds配置
- 根据qq宠物等级计算级别
- linux操作系统原理与应用.第2版 陈莉君 pdf
- C语言左右对齐、占位
- IDEA 设置前进,后退快捷键
- linux ubuntu设置中文,ubuntu 中文设置 (LANG设置)
- 【scratch案例教学】scratch中秋佳节 scratch创意编程 少儿编程 边玩边学 小朋友这样贺中秋
- 再不学点东西我们就老了