我让单片机控制一属于控制区工作台污染走100毫米可以吗

HYX-63B型单片机控制功能实训考核装置
HYX-63B型单片机控制功能实训考核装置
一、概述:
1 、采用烤漆钢板制作的台式结构。实训台的抽屉柜用于存放实训模块,抽屉柜与台面之间布置实训模块安装支架,最上一层是工作台台面。用于技能竞赛时在台面上放置设备安装底板,在底板上安装试题拟定的单片机控制装置需要的模块,连接控制电路,编写的控制程序输入单片机后,按下运行指令开关,单片机控制装置就按程序运行。
放置在单片机实训室中,这就是一张实训台。当讲解理论或实训项目中的相关知识时,可以当做课桌;当需要进行实际操作时,将台面打开,就露出放置在安装支架上的模块,在需要的实训模块间按实训要求连接电路,编写控制程序,就可以按照设计的实训项目进行单片机技术的教学。模块之间联接灵活,加深对单片机原理理解, 能够完成I/O 实训,A/D 实训,D/A 实训,LED LCD 点阵模块等。
二、设备技术指标
1. 输入电源:单相AC 220V±10% (50Hz )
2. 输出电源:DC±12V±5 %/1A
DC±5V±5 %/1A
DC24V±5 %/1.5A
3. 温度:-10 ~40 ℃;环境湿度:≤90% (25 ℃)
4. 外形尺寸:约1225mm×690mm×820mm (L×W×H )
5. 整机功耗:≤0.5kW
6. 安全保护措施:具有接地保护、漏电保护功能,安全性符合相关的国家标准。市电采用高绝缘的安全型插座及带绝缘护套的高强度安全型实验导线。
三、功能特点
1 、总体结构
本设备采用实训桌加实训模块的结构设计、模块采用标准结构,互换性强;按输入,输出,数据转换等功能的原则确定模块内容,使教学或竞赛时可方便的选择需要的模块。
本设备的每个模块都具有两种接线方式,一种为电子连线接法,8 位数据总线时还可以用杜邦线排线连接。整个连接过程方便快捷。对于具有干扰性质的元件,全部采用光电隔离装置隔离,确保系统的安全稳定。
2 、各个功能模块功能特点
(1 )主机模块
单片机采用AT89S52 。该单片机内部设有256Byte 的RAM 和8KByte 的FLASH 、三个16 位定时器,两个数据指针,片内集成了一个看门狗电路。64KRAM 扩展空间、64KROM 扩展空间;32 个IO 口,6 个向量中断源;0~33MHz 的工作频率,三级程序加密功能;工作电压4.0V~5.5V 。使用DIP40 封装便于更换芯片及仿真。并设计有在系统下载设计接口,通过USB 下载器可以方便地进行编程,无需把单片机从电路上取下。
该模块上还放有串行通信接口,该接口已经加入升压电路,可以直接与计算机通信,单片机和单片机也可以通过此口进行程序下载。复位电路上加有手动复位按扭,可以直接复位操作。模块上还设有有源蜂鸣器驱动电路,直接给固定电平就可以发出声响。
(2 )电源模块
电源模块采用线性电源和开关电源两种电源。单片机等芯片采用线性电源供电,可以有效减少因电源干扰引的程序跑飞。电机等大功率器件采用开关电源供电,确保提供足够的功率。电源采用漏电保护开关作为总控制,安全可靠。用船形开关单独控制低压电,市电与低压电分开控制。该模块共有三组相互独立的低压电源,其中有两组是正负双电源输出。面板上放置了一个AC220 输出插板,可以同时挂接三个仪器仪表。其中每组电源都有保险丝作过载保护。
(3 )仿真器模块
配备有高性能单片机仿真器,可以全功能仿真MCS51 系列单片机,也可以软件仿真;软件为中国自主研发,可以很好的支持中文,支持关键字提示,自动完成符号配对等功能。支持64K 程序地址断点、64K 源程序有效行断点和64K 临时断点,单片机内部寄存器状态一目了然。主机与计算机采用流行的USB 接口。
(4 )显示模块
该模块无论从最基本的显示元件LED 发光二极管,还是到最高级的LCD 显示屏都包含在内。具体配置为8 位跑马灯(逻辑电平指示)、8 位数码管动态显示、16×32 点阵LED 屏、1602 字符型液晶显示屏、128×64 绘图液晶显示屏。单片机常用的显示器件全部包含在内,使学习者可以接触所有的显示方案,设计人机界面时可以得心应手。
(5 )继电器模块
继电器是常用的执行元件,也是常用的隔离元件,它能用低压控制高压电路,远程切换强电电路状态优点,同时它的执行元件又是一个线圈,在通电及断电时会产生电磁干扰。为此继电器模块在集成了驱动电路的基础上增加了光电隔离措施。该模块共有6 路继电器,每个继电器的开关触点全部引出,并明确标示;每个继电器还设有工作指示灯,那一路继电器在通电工作使人一目了然。六组继电器中有两组为AC220 控制的继电器,可以控制交流电机的正反转及停止,通过转换头可以连接电子连线。另外四组为电子连线座输出。继电器触点容量250V/6A 、DC28V/12A
(6 )指令模块
指令模块上放置了单片机常用的输入元件,其中有8 个独立键盘接口,8 路8 位开关量输入,4*4 矩形键盘接口,可以满足学生从基础到高级,从简单到复杂的实训要求。如果用户需要更多的按键或需要更为复杂的设计,则可以采用该模块的PS2 键盘鼠标接口进行扩展。
(7 )ADC/DAC 模块
ADC/DAC 模块上设计了两种转换芯片ADC0809 和DAC0832 。为了便于调试程序和硬件,板载了三个功能子模块:0-5V 模拟电压输出、8 等级LED 电平指示器和有源时钟发生器。ADC0809 实验时可以用有源时钟发生器作为芯片的时钟,可以用0-5V 模拟电压输出作为模拟量,用8 等级LED 电平指示用作模拟量大小的指示;DAC0832 实验时,可以将芯片的输出接在8 等级LED 电平指示器上,转换结果是否正常一目了然。
(8 )交直流电机控制模块
该模块可以完成两种电机的控制实验,分别是24V 直流减速电机和220V 交流减速电机。每种电机的转盘下面放有光电开关计数器,可以用单片机感知其位置和转动了多少圈。每个电机控制线路中设有超程保护输入端口,在与水平移动装置组合实验时可以有效保护电机和机构的安全。
(9 )步进电机控制模块
该模块设有一只24V 两相步进电机和大功率步进电机驱动器。模块改变了电机的轴转式运行方式,采用了一个水平移动机构,该机构可以把电机的轴转改为水平直线运动,并用铝直尺的刻度指示。该模块还可以做闭环控制的实验项目,因为在运动机构上装有感知距离的闭环元件。同时设计了一个超程保护电路,当机构走到一个程序不可知状态时,可自动关闭电机电源,避免机构超程损坏。直线运动机构上带了左右限位输出端子,供单片机查询状态。
(10 )传感器配接模块(4 路传感器接口)
传感器配接模块是专为传感器的配接和电气隔离而设计的一块电路单元,该单元共有两组16 路光电隔离IO 口,每个光电隔离IO 口均可仿PLC 与24V 电源供电的传感器连接。并配有四路传感器转换接口用于工业传感器的连接。
(11 )扩展模块
因为82C55 的控制方式仍有学习价值,目前在许多教材中仍有它的身影,扩展模块能满足实验的需求;82C55 的三个端口全部引出,可以用电子连线连接,也可以用排线连接。控制端与数据通信口安排在一起,整体布局美观大方。另设有一片74LS245 芯片,可以用来与82C55 组合起来完成不同的实验功能。
(12 )温度传感器模块
该模块配备有两种芯片型温度传感器:数字量温度传感器18B20 和模拟量温度传感器LM35 。LM35 器件还加有运算放大器,放大的模拟量可以提高测量精度。每种传感器都有透明外壳加固保护,内有大功率加热电阻,加热指示器等。工作状态一清二楚。可以用来做恒温控制,温控器等实验。
(13 )智能物料搬运装置
配置双轴机器人作为装配的执行机构,具有X 轴、Y 轴两个方向的执行能力,可完成智能抓取物料放料等功能;配置有行程开关,光电传感器,光纤传感器等,可确保各执行器的准确定位;超出最大行程时,具有硬件自动保护功能,确保系统安全及人身安全;本装置为通用的模块化的多功能装置,各功能端口完全开放,可以能过不同的接线来完成各种不同的功能,为学生的创新设计提供了广阔的发挥空间。
四、实训项目
用于教学,可按工作过程导向,工学结合的模式规划教学活动,完成以下工作任务
单片机部分
1. 跑马灯实验
2. 独立按键输入实验
3. 矩阵键盘接口实训
4. 光电耦隔离输入输出实验
5. 金属检测实训
6. 继电器隔离控制实验
7. 静态数码管显示实验
8. 动态数码管显示实验
9. 蜂鸣器演奏实验
10.8255 芯片扩展IO 口实验
11. 液晶显示模块控制实训
12. 点阵LED 屏汉字显示实验
13.AD 转换实验
14.DA 转换实验
15.LM35 温度传感器采集实验
16.IIC 总线IC 卡存储实验
17.EEPROM 芯片24 系列存储实验
18. 外部中断光电计数控制电机实训
19. 定时器中断控制电机实训
20. 开环步进电机控制实训
21. 闭环交直流电机系统实验
22. 闭环步进电机系统实验
23. 恒温室控制系统实验
24.PS 键盘通信实验
25.RS232 通信实验
智能物料搬运装置部分
26. 气动电磁阀的控制实验
27. 气爪动作及物料检测实验
28. 电动手臂控制实验
29. 单球循环搬运实训
30. 双球循环搬运实训
31. 智能判断搬运实训
相关产品:
责任编辑:
声明:本文由入驻搜狐号的作者撰写,除搜狐官方账号外,观点仅代表作者本人,不代表搜狐立场。
今日搜狐热点后使用快捷导航没有帐号?
查看: 56319|回复: 187
从业将近十年!手把手教你单片机程序框架(连载)
在线时间24 小时
芯币12328枚
TA的帖子TA的资源
本帖最后由 jianhong_wu 于
02:36 编辑
& && &大家好,我叫吴坚鸿,从事单片机开发行业将近十年,这些年一直在外面接单片机项目开发,现在借电子工程世界这个平台把我这些年的经验都分享出来。我打算每个星期写一两节,直到我江郎才尽为止,目前在其它论坛已经更新到50节的内容了,但是初步估计,我这个帖子后续不会低于100节内容,因为我要整理和分享的技术资料实在是太多了。
& && & 第一次在这里发技术帖,希望各位版主和管理员多多包涵,如果发现我不对的地方请及时告诉我,我会马上改正,也可以直接帮我更改不对的地方。有不同见解的欢迎提出来交流,意见不同的请心平气和地交流,君子和而不同,不要太较真。
<p id="rate_751" onmouseover="showTip(this)" tip="&芯币 + 1 枚
威望 + 2 分
" class="mtn mbn">
<p id="rate_813" onmouseover="showTip(this)" tip="很给力!&芯币 + 1 枚
威望 + 2 分
" class="mtn mbn">
<p id="rate_691" onmouseover="showTip(this)" tip="很给力!&芯币 + 3 枚
威望 + 2 分
" class="mtn mbn">
<p id="rate_848" onmouseover="showTip(this)" tip="很给力!&芯币 + 4 枚
威望 + 5 分
" class="mtn mbn">
<p id="rate_119" onmouseover="showTip(this)" tip="赞一个!&芯币 + 6 枚
" class="mtn mbn">
在线时间24 小时
芯币12328枚
TA的帖子TA的资源
第二节:delay()延时实现LED灯的闪烁。
& & 上一节鸿哥列出了初学者七大误区,到底什么才是初学者关注的核心?那就是裸机奔跑的程序结构。一个好的程序结构,本身就是一个微型的多任务操作系统。鸿哥教给大家的就是如何编写这个简单的操作系统。在main函数循环中用switch语句实现多任务并行处理的任务切换,再外加一个定时器中断,这两者的结合就是鸿哥多年来所有实战项目的核心。鸿哥的程序结构看似简单,实际上就是那么简单。大家不用着急,本篇连载文章现在才正式开始,这一节我要教会大家两个知识点:
第一点:鸿哥首次提出的“三区一线”理论。此理论把程序代码分成三个区,一个延时分割线。
第二点:delay()延时的用途。
(1)硬件平台:基于朱兆祺51单片机学习板。
(2)实现功能:让一个LED闪烁。
(3)源代码讲解如下:
#include &REG52.H&
void initial_myself();& &
void initial_peripheral();
void delay_short(unsigned int uiDelayshort);
void delay_long(unsigned int uiDelaylong);
void led_flicker();
/* 注释一:
* 吴坚鸿个人的命名风格:凡是输出后缀都是_dr,凡是输入后缀都是_sr。
* dr代表drive驱动,sr代表sensor感应器
*/
sbit led_dr=P3^5;&&
void main()&&//学习要点:深刻理解鸿哥首次提出的三区一线理论
&&{
/* 注释二:
* initial_myself()函数属于鸿哥三区一线理论的第一区,
* 专门用来初始化单片机自己的寄存器以及个别外围要求响应速度快的输出设备,
* 防止刚上电之后,由于输出IO口电平状态不确定而导致外围设备误动作,
* 比如继电器的误动作等等。
*/
& &initial_myself();
/* 注释三:
* 此处的delay_long()延时函数属于第一区与第二区的分割线,
* 延时时间一般是0.3秒到2秒之间,等待外围芯片和模块上电稳定。
* 比如液晶模块,AT24C02存储芯片,DS1302时钟芯片,
* 这类芯片有个特点,一般都是跟单片机进行串口或并口通讯的,
* 并且不要求上电立即处理的。
*/
& &delay_long(100);
/* 注释四:
* initial_peripheral()函数属于鸿哥三区一线理论的第二区,
* 专门用来初始化不要求上电立即处理的外围芯片和模块.
* 比如液晶模块,AT24C02存储芯片,DS1302时钟芯片。
* 本程序基于朱兆祺51单片机学习板。
*/
& &initial_peripheral();
/* 注释五:
* while(1){}主函数循环区属于鸿哥三区一线理论的第三区,
* 专门用来编写被循环扫描到的非中断应用程序
*/
& &while(1)
& &{
& && &led_flicker();& &//LED闪烁应用程序
& &}
void led_flicker() //LED闪烁应用程序
{
&&led_dr=1;&&//LED亮
&&delay_short(50000);&&//延时50000个空指令的时间
/* 注释六:
* delay_long(100)延时50000个空指令的时间,因为内嵌了一个500次的for循环
*/
&&led_dr=0;&&//LED灭
&&delay_long(100);& & //延时50000个空指令的时间&&
}
/* 注释七:
* delay_short(unsigned int uiDelayShort)是小延时函数,
* 专门用在时序驱动的小延时,一般uiDelayShort的数值取10左右,
* 最大一般也不超过100.本例为了解释此函数的特点,取值范围超过100。
* 此函数的特点是时间的细分度高,延时时间不宜过长。uiDelayShort数值
* 的大小就代表里面执行了多少条空指令的时间。数值越大,延时越长。
* 时间精度不要刻意去计算,感觉差不多就行。
*/
void delay_short(unsigned int uiDelayShort)
{
& &&&
& &for(i=0;i&uiDelaySi++)
& &{
& &&&;& &//一个分号相当于执行一条空语句
& &}
}
/* 注释八:
* delay_long(unsigned int uiDelayLong)是大延时函数,
* 专门用在上电初始化的大延时,
* 此函数的特点是能实现比较长时间的延时,细分度取决于内嵌for循环的次数,
* uiDelayLong的数值的大小就代表里面执行了多少次500条空指令的时间。
* 数值越大,延时越长。时间精度不要刻意去计算,感觉差不多就行。
*/
void delay_long(unsigned int uiDelayLong)
{
& &
& &
& &for(i=0;i&uiDelayLi++)
& &{
& && &for(j=0;j&500;j++)&&//内嵌循环的空指令数量
& && && & {
& && && && & ; //一个分号相当于执行一条空语句
& && && & }
& &}
}
void initial_myself()&&//初始化单片机
{
&&led_dr=0;&&//LED灭
}
void initial_peripheral() //初始化外围
{
&&;& &//本例为空
}
复制代码
总结陈词:
鸿哥首次提出的“三区一线”理论概况了各种项目程序的基本分区。我后续的程序就按此分区编写。
Delay()函数的长延时适用在上电初始化。
Delay()函数的短延时适用在驱动时序的脉冲延时,此时的时间不能太长,本例中暂时没有列出这方面的例子,在后面的章节中会提到。
在本例源代码中,在led_flicker()闪烁应用程序里用到的两个延时delay,它们的延时时间都太长了,在实战项目中肯定不能用这种延时,因为消耗的时间太长了,其它任务根本没有机会执行。那怎么办呢?我们应该如何改善?欲知详情,请听下回分解-----累计主循环次数使LED灯闪烁。
(未完待续,下节更精彩,不要走开哦)
<p id="rate_145" onmouseover="showTip(this)" tip="好长啊&芯币 -3 枚
威望 -2 分
" class="mtn mbn">
在线时间24 小时
芯币12328枚
TA的帖子TA的资源
第八节:在定时中断函数里执行独立按键的扫描程序。
上一节讲了在主函数中利用累计定时中断的次数来实现独立按键的检测,但是如果在某些项目中,需要在主函数里间歇性地执行一些一气呵成的耗时任务,当主函数正在处理一气呵成的耗时任务时(前提是没有关闭定时器中断),这个时候如果有按键按下来,就有可能没有及时被响应到而遗漏了。在定时中断函数里处理独立按键的扫描程序,可以避免这个问题。要教会大家一个知识点:如何在上一节的基础上,略作修改,就可以在定时中断函数里处理独立按键的扫描程序。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。用矩阵键盘中的S1和S5号键作为独立按键,记得把输出线P0.4一直输出低电平,模拟独立按键的触发地GND。
(2)实现功能:有两个独立按键,每按一个独立按键,蜂鸣器发出“滴”的一声后就停。
(3)源代码讲解如下:
#include &REG52.H&
#define const_voice_short&&40& &//蜂鸣器短叫的持续时间
/* 注释一:
* 调整抖动时间阀值的大小,可以更改按键的触发灵敏度。
* 去抖动的时间本质上等于累计定时中断次数的时间。
*/
#define const_key_time1&&20& & //按键去抖动延时的时间
#define const_key_time2&&20& & //按键去抖动延时的时间
void initial_myself();& &
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();&&//定时中断函数
void key_service(); //按键服务的应用程序
void key_scan(); //按键扫描函数 放在定时中断里
sbit key_sr1=P0^0; //对应朱兆祺学习板的S1键
sbit key_sr2=P0^1; //对应朱兆祺学习板的S5键
sbit key_gnd_dr=P0^4; //模拟独立按键的地GND,因此必须一直输出低电平
sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
unsigned char ucKeySec=0;& &//被触发的按键编号
unsigned int&&uiKeyTimeCnt1=0; //按键去抖动延时计数器
unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志
unsigned int&&uiKeyTimeCnt2=0; //按键去抖动延时计数器
unsigned char ucKeyLock2=0; //按键触发后自锁的变量标志
unsigned int&&uiVoiceCnt=0;&&//蜂鸣器鸣叫的持续时间计数器
void main()
&&{
& &initial_myself();&&
& &delay_long(100);& &
& &initial_peripheral();
& &while(1)&&
& &{
& && & key_service(); //按键服务的应用程序
& &}
void key_scan()//按键扫描函数 放在定时中断里
{&&
/* 注释二:
* 独立按键扫描的详细过程:
* 第一步:平时没有按键被触发时,按键的自锁标志,去抖动延时计数器一直被清零。
* 第二步:一旦有按键被按下,去抖动延时计数器开始在定时中断函数里累加,在还没累加到
*& && && &阀值const_key_time1时,如果在这期间由于受外界干扰或者按键抖动,而使
*& && && &IO口突然瞬间触发成高电平,这个时候马上把延时计数器uiKeyTimeCnt1
*& && && &清零了,这个过程非常巧妙,非常有效地去除瞬间的杂波干扰。这是我实战中摸索出来的。
*& && && &以后凡是用到开关感应器的时候,都可以用类似这样的方法去干扰。
* 第三步:如果按键按下的时间超过了阀值const_key_time1,则触发按键,把编号ucKeySec赋值。
*& && && &同时,马上把自锁标志ucKeyLock1置位,防止按住按键不松手后一直触发。
* 第四步:等按键松开后,自锁标志ucKeyLock1及时清零,为下一次自锁做准备。
* 第五步:以上整个过程,就是识别按键IO口下降沿触发的过程。
*/
&&if(key_sr1==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
&&{
& &&&ucKeyLock1=0; //按键自锁标志清零
& && && &uiKeyTimeCnt1=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。& && &
&&}
&&else if(ucKeyLock1==0)//有按键按下,且是第一次被按下
&&{
& &&&uiKeyTimeCnt1++; //累加定时中断次数
& &&&if(uiKeyTimeCnt1&const_key_time1)
& &&&{
& && &&&uiKeyTimeCnt1=0;
& && &&&ucKeyLock1=1;&&//自锁按键置位,避免一直触发
& && &&&ucKeySec=1;& & //触发1号键
& &&&}
&&}
&&if(key_sr2==1)
&&{
& &&&ucKeyLock2=0;
& && && &uiKeyTimeCnt2=0;
&&}
&&else if(ucKeyLock2==0)
&&{
& &&&uiKeyTimeCnt2++; //累加定时中断次数
& &&&if(uiKeyTimeCnt2&const_key_time2)
& &&&{
& && &&&uiKeyTimeCnt2=0;
& && &&&ucKeyLock2=1;
& && &&&ucKeySec=2;& &&&//触发2号键
& &&&}
&&}
void key_service() //第三区 按键服务的应用程序
{
&&switch(ucKeySec) //按键服务状态切换
&&{
& & case 1:// 1号键 对应朱兆祺学习板的S1键
& && && && &&&uiVoiceCnt=const_voice_ //按键声音触发,滴一声就停。
& && && && &&&ucKeySec=0;&&//响应按键服务处理程序后,按键编号清零,避免一致触发
& && && && && &&&
& & case 2:// 2号键 对应朱兆祺学习板的S5键
& && && && &&&uiVoiceCnt=const_voice_ //按键声音触发,滴一声就停。
& && && && &&&ucKeySec=0;&&//响应按键服务处理程序后,按键编号清零,避免一致触发
& && && && && && && && && &&&
&&}& && && && && &
void T0_time() interrupt 1
{
&&TF0=0;&&//清除中断标志
&&TR0=0; //关中断
&&key_scan(); //按键扫描函数
&&if(uiVoiceCnt!=0)
&&{
& &&&uiVoiceCnt--; //每次进入定时中断都自减1,直到等于零为止。才停止鸣叫
& && && &beep_dr=0;&&//蜂鸣器是PNP三极管控制,低电平就开始鸣叫。
&&}
&&else
&&{
& &&&; //此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。
& && && &&&beep_dr=1;&&//蜂鸣器是PNP三极管控制,高电平就停止鸣叫。
&&}
&&TH0=0xf8;& &//重装初始值()=f
&&TL0=0x2f;
&&TR0=1;&&//开中断
}
void delay_long(unsigned int uiDelayLong)
{
& &
& &
& &for(i=0;i&uiDelayLi++)
& &{
& && &for(j=0;j&500;j++)&&//内嵌循环的空指令数量
& && && & {
& && && && & ; //一个分号相当于执行一条空语句
& && && & }
& &}
}
void initial_myself()&&//第一区 初始化单片机
{
/* 注释三:
* 矩阵键盘也可以做独立按键,前提是把某一根公共输出线输出低电平,
* 模拟独立按键的触发地,本程序中,把key_gnd_dr输出低电平。
* 朱兆祺51学习板的S1和S5两个按键就是本程序中用到的两个独立按键。
*/
&&key_gnd_dr=0; //模拟独立按键的地GND,因此必须一直输出低电平
&&beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。
&&TMOD=0x01;&&//设置定时器0为工作方式1
&&TH0=0xf8;& &//重装初始值()=f
&&TL0=0x2f;
}
void initial_peripheral() //第二区 初始化外围
{
&&EA=1;& &&&//开总中断
&&ET0=1;& & //允许定时中断
&&TR0=1;& & //启动定时中断
}
复制代码
总结陈词:
本节程序已经展示了在定时中断函数里执行独立按键的扫描程序。这节和前面两节所讲的扫描方式,我都在项目上用过,具体跟项目的侧重点不同来选择不同的方式,我本人用得最多的就是当前这种方式。假如要独立按键实现类似鼠标的双击功能,我们改怎么写程序?欲知详情,请听下回分解-----独立按键的双击按键触发。
(未完待续,下节更精彩,不要走开哦)
在程序的69行要不要这么写:else if(ucKeyLock1==0 && key_sr1==0)//有按键按下,且是第一次被按下&
所以要至少20秒左右不能回到主函数中。这时按键程序就不再管用了。放在定时器扫描中就好了完美解决了。&
这种方法超实用。每当进入定时器中断一次,就自动扫描按键一次。而且相当于一个定时单位就扫描一次。我在做单片机与GSM模块通信的时候,当做来电话或者短信息,单片机去处理的时候,因为我要打电话发短信什么的,所 &
在线时间24 小时
芯币12328枚
TA的帖子TA的资源
第六节:在主函数中利用累计主循环次数来实现独立按键的检测。
上一节讲了多任务中蜂鸣器驱动程序的框架,这节继续利用多任务处理的方式,在主函数中利用累计主循环次数来实现独立按键的检测。要教会大家四个知识点:
第一点:独立按键的驱动程序框架。
第二点:用累计主循环次数来实现去抖动的延时。
第三点:灵活运用防止按键不松手后一直触发的按键自锁标志。
第四点:在按键去抖动延时计时中,添加一个抗干扰的软件监控判断。一旦发现瞬间杂波干扰,马上把延时计数器清零。这种方法是我在复杂的工控项目中总结出来的。以后凡是用到开关感应器的地方,都可以用类似的方法实现软件上的抗干扰处理。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。用矩阵键盘中的S1和S5号键作为独立按键,记得把输出线P0.4一直输出低电平,模拟独立按键的触发地GND。
(2)实现功能:有两个独立按键,每按一个独立按键,蜂鸣器发出“滴”的一声后就停。
(3)源代码讲解如下:
#include &REG52.H&
#define const_voice_short&&40& &//蜂鸣器短叫的持续时间
/* 注释一:
* 调整抖动时间阀值的大小,可以更改按键的触发灵敏度。
* 去抖动的时间本质上等于累计主循环次数的时间。
*/
#define const_key_time1&&500& & //按键去抖动延时的时间
#define const_key_time2&&500& & //按键去抖动延时的时间
void initial_myself();& &
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();&&//定时中断函数
void key_service(); //按键服务的应用程序
void key_scan(); //按键扫描函数
sbit key_sr1=P0^0; //对应朱兆祺学习板的S1键
sbit key_sr2=P0^1; //对应朱兆祺学习板的S5键
sbit key_gnd_dr=P0^4; //模拟独立按键的地GND,因此必须一直输出低电平
sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
unsigned char ucKeySec=0;& &//被触发的按键编号
unsigned int&&uiKeyTimeCnt1=0; //按键去抖动延时计数器
unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志
unsigned int&&uiKeyTimeCnt2=0; //按键去抖动延时计数器
unsigned char ucKeyLock2=0; //按键触发后自锁的变量标志
unsigned int&&uiVoiceCnt=0;&&//蜂鸣器鸣叫的持续时间计数器
void main()
&&{
& &initial_myself();&&
& &delay_long(100);& &
& &initial_peripheral();
& &while(1)&&
& &{
& && & key_scan(); //按键扫描函数
& && & key_service(); //按键服务的应用程序
& &}
void key_scan()//按键扫描函数
{&&
/* 注释二:
* 独立按键扫描的详细过程:
* 第一步:平时没有按键被触发时,按键的自锁标志和去抖动延时计数器一直被清零。
* 第二步:一旦有按键被按下,去抖动延时计数器开始累加,在还没累加到
*& && && &阀值const_key_time1时,如果在这期间由于受外界干扰或者按键抖动,而使
*& && && &IO口突然瞬间触发成高电平,这个时候马上又把延时计数器uiKeyTimeCnt1
*& && && &清零了,这个过程非常巧妙,非常有效地去除瞬间的杂波干扰。这是我实战中摸索出来的。
*& && && &以后凡是用到开关感应器的时候,都可以用类似这样的方法去干扰。
* 第三步:如果按键按下的时间超过了阀值const_key_time1,则触发按键,把编号ucKeySec赋值。
*& && && &同时,马上把自锁标志ucKeyLock1置位,防止按住按键不松手后一直触发。
* 第四步:等按键松开后,自锁标志ucKeyLock1及时清零,为下一次自锁做准备。
* 第五步:以上整个过程,就是识别按键IO口下降沿触发的过程。
*/
&&if(key_sr1==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
&&{
& &&&ucKeyLock1=0; //按键自锁标志清零
& && && &uiKeyTimeCnt1=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。& && &
&&}
&&else if(ucKeyLock1==0)//有按键按下,且是第一次被按下
&&{
& &&&++uiKeyTimeCnt1;&&//延时计数器
& &&&if(uiKeyTimeCnt1&const_key_time1)
& &&&{
& && &&&uiKeyTimeCnt1=0;
& && &&&ucKeyLock1=1;&&//自锁按键置位,避免一直触发
& && &&&ucKeySec=1;& & //触发1号键
& &&&}
&&}
&&if(key_sr2==1)
&&{
& &&&ucKeyLock2=0;
& && && &uiKeyTimeCnt2=0;
&&}
&&else if(ucKeyLock2==0)
&&{
& &&&++uiKeyTimeCnt2;
& &&&if(uiKeyTimeCnt2&const_key_time2)
& &&&{
& && &&&uiKeyTimeCnt2=0;
& && &&&ucKeyLock2=1;
& && &&&ucKeySec=2;& &&&//触发2号键
& &&&}
&&}
void key_service() //第三区 按键服务的应用程序
{
&&switch(ucKeySec) //按键服务状态切换
&&{
& & case 1:// 1号键 对应朱兆祺学习板的S1键
& && && && &&&uiVoiceCnt=const_voice_ //按键声音触发,滴一声就停。
& && && && &&&ucKeySec=0;&&//响应按键服务处理程序后,按键编号清零,避免一致触发
& && && && && &&&
& & case 2:// 2号键 对应朱兆祺学习板的S5键
& && && && &&&uiVoiceCnt=const_voice_ //按键声音触发,滴一声就停。
& && && && &&&ucKeySec=0;&&//响应按键服务处理程序后,按键编号清零,避免一致触发
& && && && && && && && && &&&
&&}& && && && && &
void T0_time() interrupt 1
{
&&TF0=0;&&//清除中断标志
&&TR0=0; //关中断
&&if(uiVoiceCnt!=0)
&&{
& &&&uiVoiceCnt--; //每次进入定时中断都自减1,直到等于零为止。才停止鸣叫
& && && &beep_dr=0;&&//蜂鸣器是PNP三极管控制,低电平就开始鸣叫。
&&}
&&else
&&{
& &&&; //此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。
& && && &&&beep_dr=1;&&//蜂鸣器是PNP三极管控制,高电平就停止鸣叫。
&&}
&&TH0=0xf8;& &//重装初始值()=f
&&TL0=0x2f;
&&TR0=1;&&//开中断
}
void delay_long(unsigned int uiDelayLong)
{
& &
& &
& &for(i=0;i&uiDelayLi++)
& &{
& && &for(j=0;j&500;j++)&&//内嵌循环的空指令数量
& && && & {
& && && && & ; //一个分号相当于执行一条空语句
& && && & }
& &}
}
void initial_myself()&&//第一区 初始化单片机
{
/* 注释三:
* 矩阵键盘也可以做独立按键,前提是把某一根公共输出线输出低电平,
* 模拟独立按键的触发地,本程序中,把key_gnd_dr输出低电平。
* 朱兆祺51学习板的S1和S5两个按键就是本程序中用到的两个独立按键。
*/
&&key_gnd_dr=0; //模拟独立按键的地GND,因此必须一直输出低电平
&&beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。
&&TMOD=0x01;&&//设置定时器0为工作方式1
&&TH0=0xf8;& &//重装初始值()=f
&&TL0=0x2f;
}
void initial_peripheral() //第二区 初始化外围
{
&&EA=1;& &&&//开总中断
&&ET0=1;& & //允许定时中断
&&TR0=1;& & //启动定时中断
}
复制代码
总结陈词:
& & 本节程序已经展示了在主函数中,利用累计主循环次数来实现独立按键的检测。这种方法我经常在实战用应用,但是它也有一个小小的不足,随着在主函数循环中任务量的增加,为了保证去抖动延时的时间一致性,要适当调整一下去抖动的阀值const_key_time1。如何解决这个问题呢?欲知详情,请听下回分解-----在主函数中利用累计定时中断的次数来实现独立按键的检测。
(未完待续,下节更精彩,不要走开哦)
感觉吴老师的去抖用的很传神。当检测到按键高电平的时候就直接将计数器清零。很棒,很好的想法。完美去抖动。ucKeyLock1很好。当检测到计时500次以后,就置1,然后程序就再也不进这个循环了。节省了CPU的扫描时间。&
在线时间24 小时
芯币12328枚
TA的帖子TA的资源
第三节:累计主循环次数使LED灯闪烁。
上一节鸿哥提到delay()延时函数消耗的时间太长了,其它任务根本没有机会执行,我们该怎么改善?本节教大家利用累计主循环次数的方法来解决这个问题。这一节要教会大家两个知识点:
第一点:利用累计主循环次数的方法实现时间延时
第二点:switch核心语句之初体验。 鸿哥所有的实战项目都是基于switch语句实现多任务并行处理。
(1)硬件平台:基于朱兆祺51单片机学习板。
(2)实现功能:让一个LED闪烁。
(3)源代码讲解如下:
#include &REG52.H&
/* 注释一:
* const_time_level是统计循环次数的设定上限,数值越大,LED延时的时间越久
*/
#define const_time_level 10000&&
void initial_myself();& &
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void led_flicker();
sbit led_dr=P3^5;&&
/* 注释二:
* 吴坚鸿个人的命名风格:凡是switch语句里面的步骤变量后缀都是Step.
* 前缀带uc,ui,ul分别表示此变量是unsigned char,unsigned int,unsigned long.
*/
unsigned char ucLedStep=0; //步骤变量
unsigned int&&uiTimeCnt=0; //统计循环次数的延时计数器
void main()
&&{
& &initial_myself();&&
& &delay_long(100);& &
& &initial_peripheral();
& &while(1)& &
& &{
& && &led_flicker();& &
& &}
void led_flicker() ////第三区 LED闪烁应用程序
{
&&
&&switch(ucLedStep)
&&{
& &&&case 0:
/* 注释三:
* uiTimeCnt累加循环次数,只有当它的次数大于或等于设定上限const_time_level时,
* 才会去改变LED灯的状态,否则CPU退出led_flicker()任务,继续快速扫描其他的任务,
* 这样的程序结构就可以达到多任务并行处理的目的。
* 本程序基于朱兆祺51单片机学习板
*/
& && && & uiTimeCnt++;&&//累加循环次数,
& && && && && && &if(uiTimeCnt&=const_time_level) //时间到
& && && && && && &{
& && && && && && && &uiTimeCnt=0; //时间计数器清零
& && && && & led_dr=1;& & //让LED亮
& && && && && && && && & ucLedStep=1; //切换到下一个步骤
& && && && && && &}
& && && && &&&
& &&&case 1:
& && && & uiTimeCnt++;&&//累加循环次数,
& && && && && && &if(uiTimeCnt&=const_time_level) //时间到
& && && && && && &{
& && && && && && && &uiTimeCnt=0; //时间计数器清零
& && && && & led_dr=0;& & //让LED灭
& && && && && && && && & ucLedStep=0; //返回到上一个步骤
& && && && && && &}
& && && && &&&
&&
&&}
void delay_long(unsigned int uiDelayLong)
{
& &
& &
& &for(i=0;i&uiDelayLi++)
& &{
& && &for(j=0;j&500;j++)&&//内嵌循环的空指令数量
& && && & {
& && && && & ; //一个分号相当于执行一条空语句
& && && & }
& &}
}
void initial_myself()&&//第一区 初始化单片机
{
&&led_dr=0;&&//LED灭
}
void initial_peripheral() //第二区 初始化外围
{
&&;& &//本例为空
}复制代码
总结陈词:
& & 在实际项目中,用累计主循环次数实现时间延时是一个不错的选择。这种方法能胜任多任务处理的程序框架,但是它本身也有一个小小的不足。随着主函数里任务量的增加,我们为了保证延时时间的准确性,要不断修正设定上限const_time_level 。我们该怎么解决这个问题呢?欲知详情,请听下回分解-----累计定时中断次数使LED灯闪烁。
(未完待续,下节更精彩,不要走开哦)
在线时间24 小时
芯币12328枚
TA的帖子TA的资源
本帖最后由 jianhong_wu 于
21:38 编辑
可能我看得不是太仔细,LZ你第一节说误区的时候,我觉得这不是误区,而是你自己的一些想法。
这不能称为误 ...
每个人理解不一样,有的网友看到我的观点后会如梦初醒,茅舍顿开。也有的网友非常不赞同我的观点,所以针对这个问题我也不想反驳什么。我只想分享一下我去年年底的一个经历。有一个惠州的网友,他非常努力地自学单片机,花了很多钱买了很多学习板和书籍,都不得要领。他看到我这个文章后,他非要我教他学单片机,不惜重金。2014年的元旦假期,他过来找到我,我们第一次见面,他就拿出一本厚厚的自学笔记给我看,里面密密麻麻地记着各种寄存器的名称,以及各种概念名词,C语言的各种高级功能。我看了之后,就直接告诉他,把自学笔记都丢掉吧,学单片机不需要做笔记,就像一个球类运动一样,要的是球感,有了球感后,一切都很简单,而要获得球感,就必须多打球。想学好单片机,必须快速进入到动手编程中,而要想快速进入到编程中,就必须先把一大部分无关紧要的内容和知识暂时抛弃掉,这个无关紧要的知识和内容就是我所说的误区。比如C语言的指针,我毕业工作2年后才慢慢领悟到它有什么用,但是之前不会指针并不会影响我开发项目。再说到那个惠州的网友,他自从受到我点拨之后,目前已经入门了。
其实我表达的意思是说,写教程应该严谨!!用词应该恰当!观点应该客观!!&
在线时间24 小时
芯币12328枚
TA的帖子TA的资源
第三十三节:能设置速度档位的数码管倒计时程序。
& &上一节讲了数码管中的倒计时程序。这节要在此程序上多增加两个按键,用来控制数码管倒计时的速度档位,并且需要在数码管中闪烁显示被设置的速度档位。这一节要教会大家三个知识点:
第一个:把一个按键的短按与长按复合应用在项目中的程序结构。
第二个:通过本程序,继续加深理解按键与数码管的关联方法。
第三个:继续加深熟悉鸿哥首次提出的“一二级菜单显示理论”:凡是人机界面显示,不管是数码管还是液晶屏,都可以把显示的内容分成不同的窗口来显示,每个显示的窗口中又可以分成不同的局部显示。其中窗口就是一级菜单,用ucWd变量表示。局部就是二级菜单,用ucPart来表示。不同的窗口,会有不同的更新显示变量ucWdXUpdate来对应,表示整屏全部更新显示。不同的局部,也会有不同的更新显示变量ucWdXPartYUpdate来对应,表示局部更新显示。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。启动和暂停键对应S1键,复位键对应S5键。加键对应S9键,减键对应S13键。
(2)实现功能:按下启动暂停按键时,倒计时开始工作,再按一次启动暂停按键时,则暂停倒计时。在任何时候,按下复位按键,倒计时将暂停工作,并且恢复倒计时当前默认值99。如果长按复位按键,在数码管会切换到第2个闪烁窗口,用来设置速度档位,修改完速度档位后,再一次按下复位按键,或者直接按启动暂停按键,会切换回窗口1显示倒计时的当前数据。
(3)源代码讲解如下:
#include &REG52.H&
#define const_voice_short&&40& &//蜂鸣器短叫的持续时间
#define const_voice_long& &200& & //蜂鸣器长叫的持续时间
#define const_key_time1&&20& & //按键去抖动延时的时间
#define const_key_time2&&20& & //按键去抖动延时的时间
#define const_key_time3&&20& & //按键去抖动延时的时间
#define const_key_time4&&20& & //按键去抖动延时的时间
#define const_key_long_time 200&&//长按复位按键的时间
#define const_dpy_time_half&&200&&//数码管闪烁时间的半值
#define const_dpy_time_all& &400&&//数码管闪烁时间的全值 一定要比const_dpy_time_half 大
void initial_myself();& &
void initial_peripheral();
void delay_short(unsigned int uiDelayShort);
void delay_long(unsigned int uiDelaylong);
//驱动数码管的74HC595
void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);&&
void display_drive(); //显示数码管字模的驱动函数
void display_service(); //显示的窗口菜单服务程序
//驱动LED的74HC595
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
void T0_time();&&//定时中断函数
void key_service(); //按键服务的应用程序
void key_scan();//按键扫描函数 放在定时中断里
sbit key_sr1=P0^0; //对应朱兆祺学习板的S1键
sbit key_sr2=P0^1; //对应朱兆祺学习板的S5键
sbit key_sr3=P0^2; //对应朱兆祺学习板的S9键
sbit key_sr4=P0^3; //对应朱兆祺学习板的S13键
sbit key_gnd_dr=P0^4; //模拟独立按键的地GND,因此必须一直输出低电平
sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
sbit led_dr=P3^5;&&//作为中途暂停指示灯 亮的时候表示中途暂停
sbit dig_hc595_sh_dr=P2^0;& &&&//数码管的74HC595程序
sbit dig_hc595_st_dr=P2^1;&&
sbit dig_hc595_ds_dr=P2^2;&&
sbit hc595_sh_dr=P2^3;& & //LED灯的74HC595程序
sbit hc595_st_dr=P2^4;&&
sbit hc595_ds_dr=P2^5;&&
unsigned char ucKeySec=0;& &//被触发的按键编号
unsigned int&&uiKeyTimeCnt1=0; //按键去抖动延时计数器
unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志
unsigned int&&uiKeyTimeCnt2=0; //按键去抖动延时计数器
unsigned char ucKeyLock2=0; //按键触发后自锁的变量标志
unsigned int&&uiKeyTimeCnt3=0; //按键去抖动延时计数器
unsigned char ucKeyLock3=0; //按键触发后自锁的变量标志
unsigned int&&uiKeyTimeCnt4=0; //按键去抖动延时计数器
unsigned char ucKeyLock4=0; //按键触发后自锁的变量标志
unsigned int&&uiVoiceCnt=0;&&//蜂鸣器鸣叫的持续时间计数器
unsigned char ucDigShow8;&&//第8位数码管要显示的内容
unsigned char ucDigShow7;&&//第7位数码管要显示的内容
unsigned char ucDigShow6;&&//第6位数码管要显示的内容
unsigned char ucDigShow5;&&//第5位数码管要显示的内容
unsigned char ucDigShow4;&&//第4位数码管要显示的内容
unsigned char ucDigShow3;&&//第3位数码管要显示的内容
unsigned char ucDigShow2;&&//第2位数码管要显示的内容
unsigned char ucDigShow1;&&//第1位数码管要显示的内容
unsigned char ucDigDot8;&&//数码管8的小数点是否显示的标志
unsigned char ucDigDot7;&&//数码管7的小数点是否显示的标志
unsigned char ucDigDot6;&&//数码管6的小数点是否显示的标志
unsigned char ucDigDot5;&&//数码管5的小数点是否显示的标志
unsigned char ucDigDot4;&&//数码管4的小数点是否显示的标志
unsigned char ucDigDot3;&&//数码管3的小数点是否显示的标志
unsigned char ucDigDot2;&&//数码管2的小数点是否显示的标志
unsigned char ucDigDot1;&&//数码管1的小数点是否显示的标志
unsigned char ucDigShowTemp=0; //临时中间变量
unsigned char ucDisplayDriveStep=1;&&//动态扫描数码管的步骤变量
unsigned char ucWd=1;&&//本程序的核心变量,窗口显示变量。类似于一级菜单的变量。代表显示不同的窗口。
unsigned char ucWd1Update=1; //窗口1更新显示标志
unsigned char ucWd2Update=1; //窗口2更新显示标志
unsigned char ucCountDown=99;&&//倒计时的当前值
unsigned char ucStartFlag=0;&&//暂停与启动的标志位
unsigned int&&uiTimeCnt=0;&&//倒计时的时间计时器
unsigned int&&uiDpyTimeCnt=0;&&//数码管的闪烁计时器,放在定时中断里不断累加
unsigned int&&uiSetData1=50;&&//速度档位
unsigned int&&uiSpeedCnt=400;&&//影响速度变量,它跟速度档位uiSetData1有关联
unsigned char ucTemp1=0;&&//中间过渡变量
unsigned char ucTemp2=0;&&//中间过渡变量
unsigned char ucTemp3=0;&&//中间过渡变量
unsigned char ucTemp4=0;&&//中间过渡变量
unsigned char ucTemp5=0;&&//中间过渡变量
unsigned char ucTemp6=0;&&//中间过渡变量
unsigned char ucTemp7=0;&&//中间过渡变量
unsigned char ucTemp8=0;&&//中间过渡变量
//根据原理图得出的共阴数码管字模表
code unsigned char dig_table[]=
{
0x3f,&&//0& && & 序号0
0x06,&&//1& && & 序号1
0x5b,&&//2& && & 序号2
0x4f,&&//3& && & 序号3
0x66,&&//4& && & 序号4
0x6d,&&//5& && & 序号5
0x7d,&&//6& && & 序号6
0x07,&&//7& && & 序号7
0x7f,&&//8& && & 序号8
0x6f,&&//9& && & 序号9
0x00,&&//无& && &序号10
0x40,&&//-& && & 序号11
0x73,&&//P& && & 序号12
};
void main()
&&{
& &initial_myself();&&
& &delay_long(100);& &
& &initial_peripheral();
& &while(1)&&
& &{
& && & key_service(); //按键服务的应用程序
& && & display_service(); //显示的窗口菜单服务程序
& &}
/* 注释一:
*鸿哥首次提出的&一二级菜单显示理论&:
*凡是人机界面显示,不管是数码管还是液晶屏,都可以把显示的内容分成不同的窗口来显示,
*每个显示的窗口中又可以分成不同的局部显示。其中窗口就是一级菜单,用ucWd变量表示。
*局部就是二级菜单,用ucPart来表示。不同的窗口,会有不同的更新显示变量ucWdXUpdate来对应,
*表示整屏全部更新显示。不同的局部,也会有不同的更新显示变量ucWdXPartYUpdate来对应,表示局部更新显示。
void display_service() //显示的窗口菜单服务程序
{
& &switch(ucWd)&&//本程序的核心变量,窗口显示变量。类似于一级菜单的变量。代表显示不同的窗口。
& &{
& && & case 1:& &//显示窗口1的数据
& && && && &if(ucWd1Update==1)&&//窗口1要全部更新显示
& & & & & & & & & & & & {
& && && && && &ucWd1Update=0;&&//及时清零标志,避免一直进来扫描
& && && && && &ucTemp8=10;&&//显示空
& && && && && &ucTemp7=10;&&//显示空
& && && && && &ucTemp6=10;&&//显示空
& && && && && &ucTemp5=10;&&//显示空
& && && && && &ucTemp4=10;&&//显示空
& && && && && &ucTemp3=10;&&//显示空
& && && && && &ucTemp2=ucCountDown/10;&&//倒计时的当前值
& && && && && &ucTemp1=ucCountDown%10;
& && && && && &ucDigShow8=ucTemp8;&&
& && && && && &ucDigShow7=ucTemp7;&&
& && && && && &ucDigShow6=ucTemp6;&&
& && && && && &ucDigShow5=ucTemp5;
& && && && && &ucDigShow4=ucTemp4;&&
& && && && && &ucDigShow3=ucTemp3;
& & & & & & & & & & & && & if(ucCountDown&10)
& & & & & & & & & & & && & {
& & & & & & & & & & & && && & ucDigShow2=10;
& & & & & & & & & & & && & }
& & & & & & & & & & & && & else
& & & & & & & & & & & && & {
& & & & & & & & & & & && && & ucDigShow2=ucTemp2;
& & & & & & & & & & & && & }
& & & & & & & & & & & && & ucDigShow1=ucTemp1;
& & & & & & & & & & & &
& && && && &}
& && && && &
& && & case 2:& &//显示窗口2的数据
& && && && &if(ucWd2Update==1)&&//窗口2要全部更新显示
& && && && &{
& && && && && &ucWd2Update=0;&&//及时清零标志,避免一直进来扫描
& && && && && &ucTemp8=10;&&//显示空
& && && && && &ucTemp7=10;&&//显示空
& && && && && &ucTemp6=10;&&//显示空
& && && && && &ucTemp5=10;&&//显示空
& && && && && &ucTemp4=10;&&//显示空
& && && && && &ucTemp3=10;&&//显示空
& && && && && &ucTemp2=uiSetData1/10;&&//倒计时的速度档位
& && && && && &ucTemp1=uiSetData1%10;
& && && && && &ucDigShow8=ucTemp8;&&
& && && && && &ucDigShow7=ucTemp7;&&
& && && && && &ucDigShow6=ucTemp6;&&
& && && && && &ucDigShow5=ucTemp5;
& && && && && &ucDigShow4=ucTemp4;&&
& && && && && &ucDigShow3=ucTemp3;
& & & & & & & & & & & && & if(uiSetData1&10)
& & & & & & & & & & & && & {
& & & & & & & & & & & && && & ucDigShow2=10;
& & & & & & & & & & & && & }
& & & & & & & & & & & && & else
& & & & & & & & & & & && & {
& & & & & & & & & & & && && & ucDigShow2=ucTemp2;
& & & & & & & & & & & && & }
& & & & & & & & & & & && & ucDigShow1=ucTemp1;
& && && && && && && && &
& && && && &}
& && && && &//数码管闪烁
& && && && &if(uiDpyTimeCnt==const_dpy_time_half)
& && && && &{
& && && && && &if(uiSetData1&10)& && &&&//数码管显示内容
& && && && && &{
& && && && && && &&&ucDigShow2=10;
& && && && && &}
& && && && && &else
& && && && && &{
& && && && && && &&&ucDigShow2=ucTemp2;
& && && && && &}
& && && && && &ucDigShow1=ucTemp1;
& && && && &}
& && && && &else if(uiDpyTimeCnt&const_dpy_time_all) //const_dpy_time_all一定要比const_dpy_time_half 大
& && && && &{& & & & & & & & & & & &
& && && && && &uiDpyTimeCnt=0;& &//及时把闪烁记时器清零& & & & & & & & & & & && &
& && && && && &ucDigShow2=10;& &//数码管显示空,什么都不显示
& && && && && &ucDigShow1=10;
& && && && &}
& && && && &
& &
& &&&}
& &
void key_scan()//按键扫描函数 放在定时中断里
{&&
&&if(key_sr1==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
&&{
& &&&ucKeyLock1=0; //按键自锁标志清零
& &&&uiKeyTimeCnt1=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。& && &
&&}
&&else if(ucKeyLock1==0)//有按键按下,且是第一次被按下
&&{
& &&&uiKeyTimeCnt1++; //累加定时中断次数
& &&&if(uiKeyTimeCnt1&const_key_time1)
& &&&{
& && &&&uiKeyTimeCnt1=0;
& && &&&ucKeyLock1=1;&&//自锁按键置位,避免一直触发
& && &&&ucKeySec=1;& & //触发1号键
& &&&}
&&}
/* 注释二:
* 请注意以下长按复位按键与短按复位按键的写法。在本程序中,每次长按复位按键必然
* 触发一次短按复位按键。
&&if(key_sr2==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
&&{
& &&&ucKeyLock2=0; //按键自锁标志清零
& &&&uiKeyTimeCnt2=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。& && &
&&}
&&else if(ucKeyLock2==0)//有按键按下,且是第一次被按下
&&{
& &&&uiKeyTimeCnt2++; //累加定时中断次数
& &&&if(uiKeyTimeCnt2&const_key_time2)
& &&&{
& && &&&uiKeyTimeCnt2=0;
& && &&&ucKeyLock2=1;&&//自锁按键置位,避免一直触发
& && &&&ucKeySec=2;& & //触发2号键
& &&&}
&&}
&&else if(uiKeyTimeCnt2&const_key_long_time)& &//长按复位按键
&&{
& && &uiKeyTimeCnt2++;
& & & && &if(uiKeyTimeCnt2==const_key_long_time)
& & & && &{
& & & && && &ucKeySec=17;& & //触发17号长按复位键
& & & && &}
&&}
if(key_sr3==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
&&{
& &&&ucKeyLock3=0; //按键自锁标志清零
& &&&uiKeyTimeCnt3=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。& && &
&&}
&&else if(ucKeyLock3==0)//有按键按下,且是第一次被按下
&&{
& &&&uiKeyTimeCnt3++; //累加定时中断次数
& &&&if(uiKeyTimeCnt3&const_key_time3)
& &&&{
& && &&&uiKeyTimeCnt3=0;
& && &&&ucKeyLock3=1;&&//自锁按键置位,避免一直触发
& && &&&ucKeySec=3;& & //触发3号键
& &&&}
&&}
&&if(key_sr4==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
&&{
& &&&ucKeyLock4=0; //按键自锁标志清零
& &&&uiKeyTimeCnt4=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。& && &
&&}
&&else if(ucKeyLock4==0)//有按键按下,且是第一次被按下
&&{
& &&&uiKeyTimeCnt4++; //累加定时中断次数
& &&&if(uiKeyTimeCnt4&const_key_time4)
& &&&{
& && &&&uiKeyTimeCnt4=0;
& && &&&ucKeyLock4=1;&&//自锁按键置位,避免一直触发
& && &&&ucKeySec=4;& & //触发4号键
& &&&}
&&}
void key_service() //按键服务的应用程序
{
&&switch(ucKeySec) //按键服务状态切换
&&{
& & case 1:// 启动和暂停按键 对应朱兆祺学习板的S1键
& && && & switch(ucWd)&&//在不同的窗口下,设置不同的参数
& && && & {
& && && && &&&case 1:
& && && && && && & if(ucStartFlag==0)&&//如果原来处于暂停的状态,则启动
& & & & & & & & & & & & & & & && & {
& && && && && && && & ucStartFlag=1; //启动
& & & & & & & & & & & & & & & && & }
& & & & & & & & & & & & & & & && & else& &&&//如果原来处于启动的状态,则暂停
& & & & & & & & & & & & & & & && & {
& & & & & & & & & & & & & & & && && & ucStartFlag=0;&&//暂停
& & & & & & & & & & & & & & & && & }
& && && && && && &
& && && &&&
& && && & }& &
& && && & ucWd=1;&&//不管在哪个窗口,强行切换回窗口1
& & & & & & & && &ucWd1Update=1; //窗口1更新显示标志
& && && & uiVoiceCnt=const_voice_ //按键声音触发,滴一声就停。
& && && & ucKeySec=0;&&//响应按键服务处理程序后,按键编号清零,避免一致触发
& && && && &
& & case 2:// 复位按键 对应朱兆祺学习板的S5键
& && && & switch(ucWd)&&//在不同的窗口下,设置不同的参数
& && && & {
& && && && &&&case 1:& & //在窗口1中
& & & & & & & & & & & & & & & && & ucStartFlag=0;&&//暂停
& && && && && && & ucCountDown=99;&&//恢复倒计时的默认值99
& && && && && && & uiTimeCnt=0;&&//倒计时的时间计时器清零
& & & & & & & & & & & & & & & && & ucWd1Update=1; //窗口1更新显示标志&&只要ucCountDown变化了,就要更新显示一次
& && && && && && &
& && && && &&&case 2:& & //在窗口2中
& && && && && && & ucWd=1;&&//切换回窗口1
& & & & & & & & & & & & & & & && & ucWd1Update=1; //窗口1更新显示标志
& && && && && && &
& && && & }&&
& && && & uiVoiceCnt=const_voice_ //按键声音触发,滴一声就停。
& && && & ucKeySec=0;&&//响应按键服务处理程序后,按键编号清零,避免一致触发
& && && &&&
& & case 3:// 加按键 对应朱兆祺学习板的S9键
& && && & switch(ucWd)&&//在不同的窗口下,设置不同的参数
& && && & {
& && && && &&&case 2:& &//在窗口2中
& && && && && && & uiSetData1++;& && & //速度档位累加,档位越大,速度越快.
& & & & & & & & & & & & & & & && & if(uiSetData1&99)
& & & & & & & & & & & & & & & && & {
& & & & & & & & & & & & & & & && && & uiSetData1=99;
& & & & & & & & & & & & & & & && & }
& && && && && && & uiSpeedCnt=440-(uiSetData1*2);&&//速度档位越大,累计中断数uiSpeedCnt越小,从而倒计时的时间越快
& & & & & & & & & & & & & & & && & ucWd2Update=1; //窗口2更新显示
& && && && && && &
& && && & }& &&&
& && && & uiVoiceCnt=const_voice_ //按键声音触发,滴一声就停。
& && && & ucKeySec=0;&&//响应按键服务处理程序后,按键编号清零,避免一致触发
& && && && &
& & case 4:// 减按键 对应朱兆祺学习板的S13键
& && && & switch(ucWd)&&//在不同的窗口下,设置不同的参数
& && && & {
& && && && &&&case 2:& &//在窗口2中
& & & & & & & & & & & && && &&&if(uiSetData1&0)&&//加此条件判断,避免0减1
& & & & & & & & & & & & & & & && & {
& && && && && && && & uiSetData1--;& && & //速度档位累减,档位越小,速度越慢.
& & & & & & & & & & & & & & & && & }
& && && && && && & uiSpeedCnt=440-(uiSetData1*2);&&//速度档位越小,累计中断数uiSpeedCnt越大,从而倒计时的时间越慢
& & & & & & & & & & & & & & & && & ucWd2Update=1; //窗口2更新显示
& && && && && && &
& && && & }
& && && & uiVoiceCnt=const_voice_ //按键声音触发,滴一声就停。
& && && & ucKeySec=0;&&//响应按键服务处理程序后,按键编号清零,避免一致触发
& && && &
& & case 17:// 长按复位按键 对应朱兆祺学习板的S5键
& && && & switch(ucWd)&&//在不同的窗口下,设置不同的参数
& && && & {
& && && && &&&case 1:&&//窗口1下
& && && && && && & ucWd=2;&&//切换到闪烁窗口2&&进行设置速度档位显示
& & & & & & & & & & & & & & & && & ucWd2Update=1; //窗口2更新显示标志
& && && && && && &
& && && &
& && && & }&&
& && && & uiVoiceCnt=const_voice_ //按键声音触发,滴一声就停。
& && && & ucKeySec=0;&&//响应按键服务处理程序后,按键编号清零,避免一致触发
& && && &
&&}& && && && && &
void display_drive()&&
{
& &//以下程序,如果加一些数组和移位的元素,还可以压缩容量。但是鸿哥追求的不是容量,而是清晰的讲解思路
& &switch(ucDisplayDriveStep)
& &{
& && &case 1:&&//显示第1位
& && && &&&ucDigShowTemp=dig_table[ucDigShow1];
& && && && && && & if(ucDigDot1==1)
& && && && && && & {
& && && && && && && & ucDigShowTemp=ucDigShowTemp|0x80;&&//显示小数点
& && && && && && & }
& && && &&&dig_hc595_drive(ucDigShowTemp,0xfe);
& && && && && &
& && &case 2:&&//显示第2位
& && && &&&ucDigShowTemp=dig_table[ucDigShow2];
& && && && && && & if(ucDigDot2==1)
& && && && && && & {
& && && && && && && & ucDigShowTemp=ucDigShowTemp|0x80;&&//显示小数点
& && && && && && & }
& && && &&&dig_hc595_drive(ucDigShowTemp,0xfd);
& && && && && &
& && &case 3:&&//显示第3位
& && && &&&ucDigShowTemp=dig_table[ucDigShow3];
& && && && && && & if(ucDigDot3==1)
& && && && && && & {
& && && && && && && & ucDigShowTemp=ucDigShowTemp|0x80;&&//显示小数点
& && && && && && & }
& && && &&&dig_hc595_drive(ucDigShowTemp,0xfb);
& && && && && &
& && &case 4:&&//显示第4位
& && && &&&ucDigShowTemp=dig_table[ucDigShow4];
& && && && && && & if(ucDigDot4==1)
& && && && && && & {
& && && && && && && & ucDigShowTemp=ucDigShowTemp|0x80;&&//显示小数点
& && && && && && & }
& && && &&&dig_hc595_drive(ucDigShowTemp,0xf7);
& && && && && &
& && &case 5:&&//显示第5位
& && && &&&ucDigShowTemp=dig_table[ucDigShow5];
& && && && && && & if(ucDigDot5==1)
& && && && && && & {
& && && && && && && & ucDigShowTemp=ucDigShowTemp|0x80;&&//显示小数点
& && && && && && & }
& && && &&&dig_hc595_drive(ucDigShowTemp,0xef);
& && && && && &
& && &case 6:&&//显示第6位
& && && &&&ucDigShowTemp=dig_table[ucDigShow6];
& && && && && && & if(ucDigDot6==1)
& && && && && && & {
& && && && && && && & ucDigShowTemp=ucDigShowTemp|0x80;&&//显示小数点
& && && && && && & }
& && && &&&dig_hc595_drive(ucDigShowTemp,0xdf);
& && && && && &
& && &case 7:&&//显示第7位
& && && &&&ucDigShowTemp=dig_table[ucDigShow7];
& && && && && && & if(ucDigDot7==1)
& && && && && && & {
& && && && && && && & ucDigShowTemp=ucDigShowTemp|0x80;&&//显示小数点
& && && &&&}
& && && &&&dig_hc595_drive(ucDigShowTemp,0xbf);
& && && && && &
& && &case 8:&&//显示第8位
& && && &&&ucDigShowTemp=dig_table[ucDigShow8];
& && && && && && & if(ucDigDot8==1)
& && && && && && & {
& && && && && && && & ucDigShowTemp=ucDigShowTemp|0x80;&&//显示小数点
& && && && && && & }
& && && &&&dig_hc595_drive(ucDigShowTemp,0x7f);
& && && && && &
& &}
& &ucDisplayDriveStep++;
& &if(ucDisplayDriveStep&8)&&//扫描完8个数码管后,重新从第一个开始扫描
& &{
& &&&ucDisplayDriveStep=1;
& &}
//数码管的74HC595驱动函数
void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)
{
& &
& &unsigned char ucTempD
& &dig_hc595_sh_dr=0;
& &dig_hc595_st_dr=0;
& &ucTempData=ucDigStatusTemp16_09;&&//先送高8位
& &for(i=0;i&8;i++)
& &{
& && && &if(ucTempData&=0x80)dig_hc595_ds_dr=1;
& && && &else dig_hc595_ds_dr=0;
& && && &dig_hc595_sh_dr=0;& &&&//SH引脚的上升沿把数据送入寄存器
& && && &delay_short(1);
& && && &dig_hc595_sh_dr=1;
& && && &delay_short(1);
& && && &ucTempData=ucTempData&&1;
& &}
& &ucTempData=ucDigStatusTemp08_01;&&//再先送低8位
& &for(i=0;i&8;i++)
& &{
& && && &if(ucTempData&=0x80)dig_hc595_ds_dr=1;
& && && &else dig_hc595_ds_dr=0;
& && && &dig_hc595_sh_dr=0;& &&&//SH引脚的上升沿把数据送入寄存器
& && && &delay_short(1);
& && && &dig_hc595_sh_dr=1;
& && && &delay_short(1);
& && && &ucTempData=ucTempData&&1;
& &}
& &dig_hc595_st_dr=0;&&//ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上并且锁存起来
& &delay_short(1);
& &dig_hc595_st_dr=1;
& &delay_short(1);
& &dig_hc595_sh_dr=0;& & //拉低,抗干扰就增强
& &dig_hc595_st_dr=0;
& &dig_hc595_ds_dr=0;
//LED灯的74HC595驱动函数
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
{
& &
& &unsigned char ucTempD
& &hc595_sh_dr=0;
& &hc595_st_dr=0;
& &ucTempData=ucLedStatusTemp16_09;&&//先送高8位
& &for(i=0;i&8;i++)
& &{
& && && &if(ucTempData&=0x80)hc595_ds_dr=1;
& && && &else hc595_ds_dr=0;
& && && &hc595_sh_dr=0;& &&&//SH引脚的上升沿把数据送入寄存器
& && && &delay_short(1);
& && && &hc595_sh_dr=1;
& && && &delay_short(1);
& && && &ucTempData=ucTempData&&1;
& &}
& &ucTempData=ucLedStatusTemp08_01;&&//再先送低8位
& &for(i=0;i&8;i++)
& &{
& && && &if(ucTempData&=0x80)hc595_ds_dr=1;
& && && &else hc595_ds_dr=0;
& && && &hc595_sh_dr=0;& &&&//SH引脚的上升沿把数据送入寄存器
& && && &delay_short(1);
& && && &hc595_sh_dr=1;
& && && &delay_short(1);
& && && &ucTempData=ucTempData&&1;
& &}
& &hc595_st_dr=0;&&//ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上并且锁存起来
& &delay_short(1);
& &hc595_st_dr=1;
& &delay_short(1);
& &hc595_sh_dr=0;& & //拉低,抗干扰就增强
& &hc595_st_dr=0;
& &hc595_ds_dr=0;
void T0_time() interrupt 1
{
&&TF0=0;&&//清除中断标志
&&TR0=0; //关中断
&&key_scan(); //按键扫描函数
&&if(ucStartFlag==1)&&//启动倒计时的计时器
&&{
& &&&uiTimeCnt++;
& &&&if(uiTimeCnt&=uiSpeedCnt)& & //时间到
& &&&{
& & & && &&&if(ucCountDown!=0) //加这个判断,就是避免在0的情况下减1
& & & && &&&{
& & & && && &&&ucCountDown--;&&//倒计时当前显示值减1
& & & && &&&}
& && &&&if(ucCountDown==0)&&//倒计时结束
& & & && &&&{
& & & && && &&&ucStartFlag=0;&&//暂停
& && && &&&uiVoiceCnt=const_voice_ //蜂鸣器触发提醒,滴一声就停。
& & & && &&&}
& && &&&ucWd1Update=1; //窗口1更新显示标志
& && &&&uiTimeCnt=0;& &//计时器清零,准备从新开始计时
& &&&}
&&}
&&uiDpyTimeCnt++;&&//数码管的闪烁计时器
&&if(uiVoiceCnt!=0)
&&{
& &&&uiVoiceCnt--; //每次进入定时中断都自减1,直到等于零为止。才停止鸣叫
& &&&beep_dr=0;&&//蜂鸣器是PNP三极管控制,低电平就开始鸣叫。
//& &&&beep_dr=1;&&//蜂鸣器是PNP三极管控制,低电平就开始鸣叫。
&&}
&&else
&&{
& &&&; //此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。
& &&&beep_dr=1;&&//蜂鸣器是PNP三极管控制,高电平就停止鸣叫。
//& &&&beep_dr=0;&&//蜂鸣器是PNP三极管控制,高电平就停止鸣叫。
&&}
&&display_drive();&&//数码管字模的驱动函数
&&TH0=0& &//重装初始值()=65035=0xfe0b
&&TL0=0x0b;
&&TR0=1;&&//开中断
}
void delay_short(unsigned int uiDelayShort)
{
& &&&
& &for(i=0;i&uiDelaySi++)
& &{
& &&&;& &//一个分号相当于执行一条空语句
& &}
}
void delay_long(unsigned int uiDelayLong)
{
& &
& &
& &for(i=0;i&uiDelayLi++)
& &{
& && &for(j=0;j&500;j++)&&//内嵌循环的空指令数量
& && && & {
& && && && & ; //一个分号相当于执行一条空语句
& && && & }
& &}
}
void initial_myself()&&//第一区 初始化单片机
{
/* 注释三:
* 矩阵键盘也可以做独立按键,前提是把某一根公共输出线输出低电平,
* 模拟独立按键的触发地,本程序中,把key_gnd_dr输出低电平。
* 朱兆祺51学习板的S1就是本程序中用到的一个独立按键。
*/
&&key_gnd_dr=0; //模拟独立按键的地GND,因此必须一直输出低电平
&&led_dr=0;&&//关闭独立LED灯
&&beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。
&&hc595_drive(0x00,0x00);&&//关闭所有经过另外两个74HC595驱动的LED灯
&&TMOD=0x01;&&//设置定时器0为工作方式1
&&TH0=0& &//重装初始值()=65035=0xfe0b
&&TL0=0x0b;
void initial_peripheral() //第二区 初始化外围
{
& &ucDigDot8=0;& &//小数点全部不显示
& &ucDigDot7=0;&&
& &ucDigDot6=0;
& &ucDigDot5=0;&&
& &ucDigDot4=0;
& &ucDigDot3=0;&&
& &ucDigDot2=0;
& &ucDigDot1=0;
& &uiSpeedCnt=440-(uiSetData1*2);&&//速度档位越大,累计中断数uiSpeedCnt越小,从而倒计时的时间越快
& &EA=1;& &&&//开总中断
& &ET0=1;& & //允许定时中断
& &TR0=1;& & //启动定时中断
}
复制代码
总结陈词:
这节讲了能设置速度档位的数码管倒计时程序。现在很多人用iphone4S的手机,这个手机每次开机显示的时候,都要通过4个密码开锁,如果我们要用4位数码管来实现这个密码锁功能,该怎么编写这个程序?欲知详情,请听下回分解-----在数码管中实现iphone4S开机密码锁的程序。
(未完待续,下节更精彩,不要走开哦)
在线时间24 小时
芯币12328枚
TA的帖子TA的资源
第八十四节:实时同步把键盘输入的BCD码数组转换成数值的液晶屏显示程序。
开场白:& & 键盘直接输入的是带小数点的BCD码数组,要把它们转换成具体的数值才可以更好的在程序里运算或者处理。如何把BCD码数组实时同步转换成数值?这一节主要跟大家讲这方面的算法程序。另外,有一个地方值得注意:上一节键盘输入的小数点个数可以限制成最大2位,但是整数部分没有限制。这节为了也能限制整数部分的最大个数为3位,我修改了上一节的void set_data(…)函数。所以这节的void set_data(…)函数跟上一节的void set_data(…)函数有点不一样,需要特别注意。
具体内容,请看源代码讲解。
(1)& &&&硬件平台:
基于朱兆祺51单片机学习板。数字1键对应S1键,数字2键对应S2键,数字3键对应S3键…. 数字9键对应S9键, 数字0键对应S10键。小数键对应S11,清零键对应S16,其它按键不用。
(2)& &&&实现功能:用矩阵键盘输入任意数字或小数点。小数点不能超过2位,一旦超过2位,再按其它按键则输入无效。整数部分不能超过3位,一旦超过3位,再按其它按键则输入无效。想重新输入,必须按S16清零按键才能重新输入。每次键盘输入的第一行BCD码数组会同步更新显示在第二行的数值上。
(, 下载次数: 0)
点击文件名下载附件
(3)源代码讲解如下: #include &REG52.H&
#define const_voice_short&&40& &//蜂鸣器短叫的持续时间
#define const_key_time&&10& & //按键去抖动延时的时间
sbit key_sr1=P0^0; //第一行输入
sbit key_sr2=P0^1; //第二行输入
sbit key_sr3=P0^2; //第三行输入
sbit key_sr4=P0^3; //第四行输入
sbit key_dr1=P0^4; //第一列输出
sbit key_dr2=P0^5; //第二列输出
sbit key_dr3=P0^6; //第三列输出
sbit key_dr4=P0^7; //第四列输出
sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
sbit&&LCDCS_dr&&= P1^6;&&//片选线
sbit&&LCDSID_dr = P1^7;&&//串行数据线
sbit&&LCDCLK_dr = P3^2;&&//串行时钟线
sbit&&LCDRST_dr = P3^4;&&//复位线
void SendByteToLcd(unsigned char ucData);&&//发送一个字节数据到液晶模块
void SPIWrite(unsigned char ucWData, unsigned char ucWRS); //模拟SPI发送一个字节的命令或者数据给液晶模块的底层驱动
void WriteCommand(unsigned char ucCommand); //发送一个字节的命令给液晶模块
void LCDWriteData(unsigned char ucData);& &//发送一个字节的数据给液晶模块
void LCDInit(void);&&//初始化&&函数内部包括液晶模块的复位
void display_clear(unsigned char ucFillDate); // 清屏 全部显示空填充0x00& &全部显示点阵用0xff
void insert_buffer_to_canvas(unsigned int x,unsigned int y,const unsigned char&&*ucArray,unsigned char ucFbFlag,unsigned int x_amount,unsigned int y_amount);//把字模插入画布.
void display_lattice(unsigned int x,unsigned int y,const unsigned char&&*ucArray,unsigned char ucFbFlag,unsigned int x_amount,unsigned int y_amount,unsigned int uiOffSetAddr); //显示任意点阵函数
unsigned char *number_to_matrix(unsigned char&&ucBitNumber); //把一位数字转换成字模首地址的函数
void delay_short(unsigned int uiDelayshort); //延时
void delay_long(unsigned int uiDelayLong);
void key_number_input(unsigned char ucKeyNumber); //输入数字按键
void set_data(unsigned char ucKeyNumberTemp, //设置参数
& && && && &&&unsigned char ucDotBitMax,
& && && && &&&unsigned char ucDataCntMax,
& && && && &&&unsigned char *p_ucDotCnt,
& && && && &&&unsigned char *p_ucDotBitS,
& & & & & & & & & & & && &unsigned char *p_ucWdPartCnt,
& & & & & & & & & & & && &unsigned char *p_ucSetDataBuffer,
& & & & & & & & & & & && &unsigned char ucIntCntMax,
& & & & & & & & & & & && &unsigned char *p_ucIntCnt);
unsigned long buffer_to_data(unsigned char ucConverDataSize,unsigned char ucConverDotCnt,unsigned char *p_ucConverBuffer); //把带小数点的BCD数组转换成long类型的数值。
void key_delete_input(void); //删除按键
void T0_time(); //定时中断函数
void key_service();
void key_scan(); //按键扫描函数 放在定时中断里
void initial_myself();& &
void initial_peripheral();
void lcd_display_service(void); //应用层面的液晶屏显示程序
void clear_all_canvas(void);&&//把画布全部清零
code unsigned char Zf816_0[]=
{
/*--&&文字:&&0&&--*/
/*--&&宋体12;&&此字体下对应的点阵为:宽x高=8x16& &--*/
0x00,0x00,0x00,0x18,0x24,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x24,0x18,0x00,0x00,
};
code unsigned char Zf816_1[]=
{
/*--&&文字:&&1&&--*/
/*--&&宋体12;&&此字体下对应的点阵为:宽x高=8x16& &--*/
0x00,0x00,0x00,0x10,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x7C,0x00,0x00,
};
code unsigned char Zf816_2[]=
{
/*--&&文字:&&2&&--*/
/*--&&宋体12;&&此字体下对应的点阵为:宽x高=8x16& &--*/
0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x04,0x04,0x08,0x10,0x20,0x42,0x7E,0x00,0x00,
};
code unsigned char Zf816_3[]=
{
/*--&&文字:&&3&&--*/
/*--&&宋体12;&&此字体下对应的点阵为:宽x高=8x16& &--*/
0x00,0x00,0x00,0x3C,0x42,0x42,0x04,0x18,0x04,0x02,0x02,0x42,0x44,0x38,0x00,0x00,
};
code unsigned char Zf816_4[]=
{
/*--&&文字:&&4&&--*/
/*--&&宋体12;&&此字体下对应的点阵为:宽x高=8x16& &--*/
0x00,0x00,0x00,0x04,0x0C,0x14,0x24,0x24,0x44,0x44,0x7E,0x04,0x04,0x1E,0x00,0x00,
};
code unsigned char Zf816_5[]=
{
/*--&&文字:&&5&&--*/
/*--&&宋体12;&&此字体下对应的点阵为:宽x高=8x16& &--*/
0x00,0x00,0x00,0x7E,0x40,0x40,0x40,0x58,0x64,0x02,0x02,0x42,0x44,0x38,0x00,0x00,
};
code unsigned char Zf816_6[]=
{
/*--&&文字:&&6&&--*/
/*--&&宋体12;&&此字体下对应的点阵为:宽x高=8x16& &--*/
0x00,0x00,0x00,0x1C,0x24,0x40,0x40,0x58,0x64,0x42,0x42,0x42,0x24,0x18,0x00,0x00,
};
code unsigned char Zf816_7[]=
{
/*--&&文字:&&7&&--*/
/*--&&宋体12;&&此字体下对应的点阵为:宽x高=8x16& &--*/
0x00,0x00,0x00,0x7E,0x44,0x44,0x08,0x08,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,
};
code unsigned char Zf816_8[]=
{
/*--&&文字:&&8&&--*/
/*--&&宋体12;&&此字体下对应的点阵为:宽x高=8x16& &--*/
0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x24,0x18,0x24,0x42,0x42,0x42,0x3C,0x00,0x00,
};
code unsigned char Zf816_9[]=
{
/*--&&文字:&&9&&--*/
/*--&&宋体12;&&此字体下对应的点阵为:宽x高=8x16& &--*/
0x00,0x00,0x00,0x18,0x24,0x42,0x42,0x42,0x26,0x1A,0x02,0x02,0x24,0x38,0x00,0x00,
};
code unsigned char Zf816_nc[]=&&//空字模
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
code unsigned char Zf816_dot[]=&&//小数点
{
/*--&&文字:&&.&&--*/
/*--&&宋体12;&&此字体下对应的点阵为:宽x高=8x16& &--*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00,
};
code unsigned char Zf816_mao_hao[]=&&//冒号
{
/*--&&文字:&&:&&--*/
/*--&&宋体12;&&此字体下对应的点阵为:宽x高=8x16& &--*/
0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,
};
code unsigned char Hz1616_yi[]=
{
/*--&&文字:&&一&&--*/
/*--&&宋体12;&&此字体下对应的点阵为:宽x高=16x16& &--*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x7F,0xFE,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
code unsigned char Hz1616_xiang[]=
{
/*--&&文字:&&项&&--*/
/*--&&宋体12;&&此字体下对应的点阵为:宽x高=16x16& &--*/
0x00,0x00,0x03,0xFE,0xFC,0x20,0x10,0x40,0x11,0xFC,0x11,0x04,0x11,0x24,0x11,0x24,
0x11,0x24,0x11,0x24,0x1D,0x24,0xE1,0x34,0x00,0x48,0x01,0x86,0x06,0x02,0x00,0x00,
};
code unsigned char Hz1616_shu[]=
{
/*--&&文字:&&数&&--*/
/*--&&宋体12;&&此字体下对应的点阵为:宽x高=16x16& &--*/
0x08,0x20,0x49,0x30,0x2A,0x20,0x1C,0x20,0xFF,0x7E,0x1C,0x44,0x2B,0x44,0x48,0xC4,
0x08,0x28,0xFF,0x28,0x12,0x10,0x34,0x10,0x0C,0x28,0x32,0x4E,0xC0,0x84,0x00,0x00,
};
code unsigned char Hz1616_zhu[]=
{
/*--&&文字:&&组&&--*/
/*--&&宋体12;&&此字体下对应的点阵为:宽x高=16x16& &--*/
0x10,0x00,0x19,0xF8,0x11,0x08,0x25,0x08,0x25,0x08,0x79,0xF8,0x09,0x08,0x11,0x08,
0x21,0x08,0x7D,0xF8,0x01,0x08,0x01,0x08,0x0D,0x08,0x73,0xFE,0x00,0x00,0x00,0x00,
};
code unsigned char Hz1616_zhi[]=
{
/*--&&文字:&&值&&--*/
/*--&&宋体12;&&此字体下对应的点阵为:宽x高=16x16& &--*/
0x10,0x40,0x18,0x60,0x17,0xFC,0x10,0x40,0x20,0x80,0x33,0xF8,0x62,0x08,0xA3,0xF8,
0x22,0x08,0x23,0xF8,0x22,0x08,0x23,0xF8,0x22,0x08,0x22,0x08,0x2F,0xFE,0x20,0x00,
};
/* 注释一:
* 以下是画布显示数组。横向是6个字节,纵向16行,可以显示3个16x16的汉字.
*&&注意,这节内容的画布跟前面79章节的画布大小不一样,79节前面的横向是4个字节,这节的横向是6个字节。
*/
unsigned char ucCanvasBuffer[]=
{
0x00,0x00,0x00,0x00,0x00,0x00,&&//上半屏
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
//------------上半屏和下半屏的分割线-----------
0x00,0x00,0x00,0x00,0x00,0x00,&&//下半屏
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
};
/* 注释二:
* 以下5个变量记录一个参数的5种信息,包括小数点的数量,小数点个数,数据的位置,数组具体值,整数个数
*/
unsigned char ucDotCnt_1=0;&&//记录当前输入的小数点数量,如果小数点的数量不为0,说明当前数组已包含小数点,此时再按小数点按键则无效
unsigned char ucDotBitS_1=0; //记录当前输入的小数点个数,如果小数点的个数如果超过规定ucDotBitMax位,此时再按任何输入按键则无效
unsigned char ucWdPartCnt_1=0; //记录当前输入的数据在数组中的位置。
unsigned char ucDataBuffer_1[6]={0,10,10,10,10,10}; //一项的BCD码数组缓冲
unsigned char ucIntCnt_1=0; //记录当前输入的整数个数,如果整数的个数如果超过规定ucIntCntMax位,此时再按任何输入按键则无效
unsigned long ulData_1=0; //用一个long变量表示BCD码的具体数值。
unsigned char ucKeyStep=1;&&//按键扫描步骤变量
unsigned char ucKeySec=0;& &//被触发的按键编号
unsigned int&&uiKeyTimeCnt=0; //按键去抖动延时计数器
unsigned char ucKeyLock=0; //按键触发后自锁的变量标志
unsigned char ucRowRecord=1; //记录当前扫描到第几列了
unsigned int&&uiVoiceCnt=0;&&//蜂鸣器鸣叫的持续时间计数器
unsigned char ucWd=1; //窗口变量
unsigned char ucPart=1; //局部变量 0代表没有选中任何一行,其它数值1到4代表选中某一行
unsigned char ucWd1Update=1; //窗口1的整屏更新显示变量& && &1代表更新显示,响应函数内部会清零
unsigned char ucWd1Part1Update=0; //窗口1的第1行局部更新显示变量&&1代表更新显示,响应函数内部会自动把它清零
unsigned char ucWd1Part2Update=0; //窗口1的第2行局部更新显示变量&&1代表更新显示,响应函数内部会自动把它清零
void main()
&&{
& && &&&initial_myself();& && &//第一区,上电后马上初始化
& && &&&delay_long(100);& && & //一线,延时线。延时一段时间
& && &&&initial_peripheral();&&//第二区,上电后延时一段时间再初始化
& && &&&while(1)& &//第三区
& && &&&{
& && && && && && &&&key_service(); //按键服务程序
& && && && &lcd_display_service(); //应用层面的液晶屏显示程序
& && &&&}
void initial_myself()&&//第一区 上电后马上初始化
{
& &beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。
& &TMOD=0x01;&&//设置定时器0为工作方式1
& &TH0=0xf8;& &//重装初始值()=f
& &TL0=0x2f;
}
void initial_peripheral() //第二区 上电后延时一段时间再初始化
{
& & LCDInit(); //初始化12864 内部包含液晶模块的复位
& & EA=1;& &&&//开总中断
& & ET0=1;& & //允许定时中断
& & TR0=1;& & //启动定时中断
void T0_time() interrupt 1
{
&&TF0=0;&&//清除中断标志
&&TR0=0; //关中断
&&key_scan();//按键扫描函数 放在定时中断里
&&if(uiVoiceCnt!=0)
&&{
& &&&uiVoiceCnt--; //每次进入定时中断都自减1,直到等于零为止。才停止鸣叫
& && && &beep_dr=0;&&//蜂鸣器是PNP三极管控制,低电平就开始鸣叫。
&&}
&&else
&&{
& &&&; //此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。
& && && &&&beep_dr=1;&&//蜂鸣器是PNP三极管控制,高电平就停止鸣叫。
&&}
&&TH0=0xf8;& &//重装初始值()=f
&&TL0=0x2f;
&&TR0=1;&&//开中断
}
void key_scan()//按键扫描函数 放在定时中断里
{&&
&&switch(ucKeyStep)
&&{
& &&&case 1:& &//按键扫描输出第ucRowRecord列低电平
& && && && &&&if(ucRowRecord==1)&&//第一列输出低电平
& && && && && && &{
& && && && & key_dr1=0;& && &
& && && && & key_dr2=1;
& && && && & key_dr3=1;& &
& && && && & key_dr4=1;
& && && && && && &}
& && && && &&&else if(ucRowRecord==2)&&//第二列输出低电平
& && && && && && &{
& && && && & key_dr1=1;& && &
& && && && & key_dr2=0;
& && && && & key_dr3=1;& &
& && && && & key_dr4=1;
& && && && && && &}
& && && && &&&else if(ucRowRecord==3)&&//第三列输出低电平
& && && && && && &{
& && && && & key_dr1=1;& && &
& && && && & key_dr2=1;
& && && && & key_dr3=0;& &
& && && && & key_dr4=1;
& && && && && && &}
& && && && &&&else& &//第四列输出低电平
& && && && && && &{
& && && && & key_dr1=1;& && &
& && && && & key_dr2=1;
& && && && & key_dr3=1;& &
& && && && & key_dr4=0;
& && && && && && &}
& && && & uiKeyTimeCnt=0;&&//延时计数器清零
& && && & ucKeyStep++;& &&&//切换到下一个运行步骤
& && && && &&&
& &&&case 2:& &&&//此处的小延时用来等待刚才列输出信号稳定,再判断输入信号。不是去抖动延时。
& && && & uiKeyTimeCnt++;
& && && && && && &if(uiKeyTimeCnt&1)
& && && && && && &{
& && && && && && && &uiKeyTimeCnt=0;
& && && && & ucKeyStep++;& &&&//切换到下一个运行步骤
& && && && && && &}
& && && && &&&
& &&&case 3:
& && && & if(key_sr1==1&&key_sr2==1&&key_sr3==1&&key_sr4==1)
& && && & {&&
& && && && & ucKeyStep=1;&&//如果没有按键按下,返回到第一个运行步骤重新开始扫描
& && && && & ucKeyLock=0;&&//按键自锁标志清零
& && && && & uiKeyTimeCnt=0; //按键去抖动延时计数器清零,此行非常巧妙& &&&
& &
& && && && && && && && & ucRowRecord++;&&//输出下一列
& && && && && && && && & if(ucRowRecord&4)&&
& && && && && && && && & {
& && && && && && && && && & ucRowRecord=1; //依次输出完四列之后,继续从第一列开始输出低电平
& && && && && && && && & }
& && && & }
& && && && && && &else if(ucKeyLock==0)&&//有按键按下,且是第一次触发
& && && && && && &{
& && && && && && && &if(key_sr1==0&&key_sr2==1&&key_sr3==1&&key_sr4==1)
& && && && && && && && & {
& && && && && && && && && & uiKeyTimeCnt++;&&//去抖动延时计数器
& && && && && && && && && && &&&if(uiKeyTimeCnt&const_key_time)
& && && && && && && && && && &&&{
& && && && && && && && && && && &&&uiKeyTimeCnt=0;
& && && && && && && && && && && &&&ucKeyLock=1;//自锁按键置位,避免一直触发,只有松开按键,此标志位才会被清零
& && && && && && && &&&if(ucRowRecord==1)&&//第一列输出低电平
& && && && && && && && && &{
& && && && && && && && && && && && &&&ucKeySec=1;&&//触发1号键 对应朱兆祺学习板的S1键
& && && && && && && && && &}
& && && && && && && &&&else if(ucRowRecord==2)&&//第二列输出低电平
& && && && && && && && && &{
& && && && && && && && && && && && &&&ucKeySec=2;&&//触发2号键 对应朱兆祺学习板的S2键
& && && && && && && && && &}
& && && && && && && &&&else if(ucRowRecord==3)&&//第三列输出低电平
& && && && && && && && && &{
& }

我要回帖

更多关于 单片机控制步进电机 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信