按键扫描处理程序流程图代码
- 格式:doc
- 大小:20.00 KB
- 文档页数:3
一、实验目的1. 理解按键扫描的基本原理,掌握按键扫描电路的设计方法。
2. 学习并运用单片机编程技术,实现按键的识别与处理。
3. 掌握按键防抖技术,提高按键识别的准确性。
4. 熟悉数码管显示电路的连接与编程,实现按键值的实时显示。
二、实验原理按键扫描是单片机应用中常见的一种输入方式,通过扫描电路检测按键状态,并转换为单片机可识别的信号。
本实验采用行列扫描法,通过单片机的I/O口输出低电平,逐行扫描按键,同时读取列线状态,判断是否有按键被按下。
三、实验设备1. 单片机实验板(如51单片机实验板)2. 按键(如按钮、触摸按键等)3. 数码管(如7段数码管)4. 电阻、电容等电子元件5. 编程软件(如Keil、IAR等)四、实验步骤1. 电路连接(1)将按键的行线连接到单片机的I/O口,列线连接到数码管的输入端。
(2)数码管的共阳极或共阴极连接到单片机的I/O口。
(3)在按键和数码管之间接入电阻和电容,实现防抖功能。
2. 编程实现(1)初始化单片机的I/O口,将行线设置为输出模式,列线设置为输入模式。
(2)编写按键扫描函数,逐行扫描按键,读取列线状态,判断是否有按键被按下。
(3)编写数码管显示函数,根据按键值显示对应的数字或字符。
(4)编写防抖函数,消除按键抖动干扰。
3. 实验测试(1)上电后,观察数码管显示是否正常。
(2)按下按键,观察数码管是否显示对应的数字或字符。
(3)多次按下按键,观察数码管显示是否稳定。
五、实验结果与分析1. 按键扫描结果实验结果表明,按键扫描电路能够正确识别按键状态,并转换为单片机可识别的信号。
按键按下时,数码管显示对应的数字或字符,按键释放时,数码管显示前一个数字或字符。
2. 防抖效果通过实验发现,防抖函数能够有效消除按键抖动干扰,提高按键识别的准确性。
在按键按下和释放过程中,数码管显示的数字或字符稳定,没有出现跳动现象。
3. 数码管显示实验结果表明,数码管显示电路能够正确显示按键值。
4.3.2 按键扫描处理程序流程图(1)按键扫描处理代码/* 功能实现参数,参数mode为Key_Menu按键选择的功能模块*/void Display(unsigned char mode){switch (mode)//显示模式,0为显示实时温度,1为显示温度上限,2为显示温度下限{case 0: if (temperature < 0)//温度小于0{temperature = -temperature;//换为正温度DisplaySeg(0x40, temperature % 1000); //0x40为负号}else DisplaySeg(codeSeg[temperature % 10000 / 1000], temperature % 1000);break;case 1: DisplaySeg(0x76, alarm_temp_H * 10); break;//显示温度上限,0x76为H字符case 2: DisplaySeg(0x38, alarm_temp_L * 10); break;//显示温度下限,0x38为L字符default:break;}}/* 按键扫描和处理函数*/void KeyScan(void){if (Key_Menu == 0)//判断按键是否被按下{DelayMs(10);//延时10毫秒,去抖动干扰if (Key_Menu == 0)//再次确认按键是否被按下{while(Key_Menu == 0)Display(menu);//等待按键释放,器件扫描数码管menu++;//功能键,功能切换if (menu == 3)menu = 0;//三个功能切换完}}if (Key_Add == 0){DelayMs(10);if (Key_Add == 0){while(Key_Add == 0)Display(menu);switch (menu){case 1: if (alarm_temp_H < 50)alarm_temp_H++;break;//加温度上限case 2: if (alarm_temp_L < 27)alarm_temp_L++;break;//加温度下限default:break;}}}if (Key_Dec == 0){DelayMs(10);if (Key_Dec == 0){while(Key_Dec == 0)Display(menu);switch (menu){case 1: if (alarm_temp_H > 30)alarm_temp_H--;break;//减温度上限case 2: if (alarm_temp_L > 7)alarm_temp_L--;break;//减温度下限default:break;}}。
题目:开关稳压电源(E题)摘要本设计综合考虑题目基本部分和发挥部分的指标要求,系统采用简单的boost 升压电路作为DC-DC变换器主电路;PWM控制器采用低压型专用集成芯片UC3843; 主开关管采用IRF540;由内置12位A/D、D/A的高性能、低功耗单片机C8051F021组成系统测控与显示单元,采用液晶显示器作为系统的状态和运行数据显示屏。
通过实际测试,作品的性能指标中,输出纹波完全达到了要求;电压调整率,整体效率,负载过流故障排除后自恢复功能,输出电压键控1V步进,电流、电压实时测量及数显功能等几项指标达均到了发挥部分要求;负载调整率也接近发挥部分指标要求。
另外,系统还增加了实时输出功率数据显示和负载过流状态下的声、光报警等实用功能。
一、引言为了满足题目发挥部分规定的电压调整率、负载调整率以及效率等几项指标要求,我们在设计中主要是尽量减少辅助控制电路的损耗。
通过单片机和脉宽调制电路来稳定输出电压,并通过单片机的控制实现对整个电路的过流保护功能,排除过流故障后,电源能自动恢复为正常工作状态。
同时,当输出电压与设定电压误差较大时,单片机能对输出电压进行一定调节,以提高负载调整率;通过单片机实现了输出电压的键盘设定和步进调整(步进为1V)。
系统具有测量和数字显示输出电压、电流的功能。
此外,还增加了实时输出功率测量与显示、在输出过流的时候系统发出声、光报警信号等功能。
二、方案论证与比较1.DC-DC主回路拓扑方案论证方案一:采用变压器升压的隔离型PWM直流-直流变换器电路,此电路效率较低,开关辐射/纹波较大,电路较复杂。
方案二:采用非隔离型BOOST升压电路,控制电路用专用集成芯片UC3843A,这种电路使用的外部原件最少、调试容易、成本低、效率高。
因此,采用此种方案。
2. 控制方法及实现方案方案一:采用电压型脉宽调制技术,产生频率固定,脉冲宽度可调整的方波脉冲,采用电压反馈环控制系统,它的反馈信息取自输出电压,用反馈电压调整控制器的输出脉冲宽度,改变脉冲占空比,实现开关电源的稳定。
4.3.2 按键扫描处理程序流程图(1)按键扫描处理代码/* 功能实现参数,参数mode为Key_Menu按键选择的功能模块*/void Displa y(unsign ed char mode){switch (mode)//显示模式,0为显示实时温度,1为显示温度上限,2为显示温度下限{case 0: if (temper ature < 0)//温度小于0{temper ature = -temper ature;//换为正温度Displa ySeg(0x40, temper ature % 1000); //0x40为负号}else Displa ySeg(codeSe g[temper ature% 10000/ 1000], temper ature % 1000);break;case 1: Displa ySeg(0x76, alarm_temp_H * 10); break;//显示温度上限,0x76为H字符case 2: Displa ySeg(0x38, alarm_temp_L * 10); break;//显示温度下限,0x38为L字符defaul t:break;}}/* 按键扫描和处理函数*/void KeyScan(void){if (Key_Menu == 0)//判断按键是否被按下{DelayM s(10);//延时10毫秒,去抖动干扰if (Key_Menu == 0)//再次确认按键是否被按下{while(Key_Menu == 0)Displa y(menu);//等待按键释放,器件扫描数码管menu++;//功能键,功能切换if (menu == 3)menu = 0;//三个功能切换完}}if (Key_Ad d == 0){DelayM s(10);if (Key_Ad d == 0){while(Key_Ad d == 0)Displa y(menu);switch (menu){case 1: if (alarm_temp_H < 50)alarm_temp_H++;break;//加温度上限case 2: if (alarm_temp_L< 27)alarm_temp_L++;break;//加温度下限defaul t:break;}}}if (Key_De c == 0){DelayM s(10);if (Key_De c == 0){while(Key_De c == 0)Displa y(menu);switch (menu){case 1: if (alarm_temp_H > 30)alarm_temp_H--;break;//减温度上限case 2: if (alarm_temp_L > 7)alarm_temp_L--;break;//减温度下限defaul t:break;}}。
基于单片机的LCD1602电子时钟设计一、设计任务和目的1.1、设计任务(1):用单片机设计基于LCD1602的电子时钟,显示时间和日期;(2):误差精度控制在1s/天;(3):具有时间和日期的校准功能;(4):能区分某年是闰年或平年,并对应显示2月份的天数;(5):根据月份的不同显示不同的最大日数;(6):搭建仿真电路图,模拟单片机要实现的功能;(7):焊接单片机开发板;(8):编写程序,下载并调试,实现要求的功能。
1.2、设计目的(1):熟练掌握KEIL软件的使用方法;(2):熟练掌握PROTEUS软件的使用方法;(3):掌握单片机I/O接口的工作原理;(4):掌握LCD显示器的工作原理及编程方法;(5):掌握独立式键盘的工作原理及编程使用方法;(6):掌握单片机的下载使用方法。
二、设计思路和方案论证2.1、设计思路电路总体上分为控制和显示部分。
以单片机最小系统作为核心控制电路,控制LCD显示,具体显示内容及方式由软件来完成;由于有时钟和日期的调节功能需要校准电路和基本的复位电路,复位电路采用按键复位,调节键、加1键、减1键三个按键完成,共需四个按键;计时功能由固定频率的晶振完成(采用11.0592MHz);显示部分主要采用LCD1602作为显示。
2.2、方案论证(1):时钟芯片的选择和论证方案一:采用DS1302时钟芯片实现时钟,DS1302芯片是一种高性能的时钟芯片,可自动对秒、分、时、日、月、年以及闰年补偿的年进行计数,精度也较高,工作电压2.5V~5.5V范围内,功耗也较低,但价格比较贵。
方案二:直接采用单片机定时计数器提供秒信号,使用程序实现秒、分、时、日、月、年计数。
采用此方案实现虽然有一定的时间误差,但可减少芯片的使用,节约成本,易于实现,符合现实选用,所以采用此种作为时钟信号发生器。
(2):显示模块选择方案和论证:方案一:采用点阵式图形LCD12864液晶显示屏,液晶显示屏的显示功能强大,可显示文字,图形,显示多样,清晰可见,但是价格昂贵,需要的接口线多,所以在此设计中不采用点阵式图形LCD12864液晶显示屏。
新型按键程序思想:unsigned char Trg;unsigned char Cont;void KeyRead( void ){unsigned char ReadData = P3^0xff; // 1Trg = ReadData & (ReadData ^ Cont); // 2Cont = ReadData; // 3}(1) 没有按键的时候ReadData =0x00;Trg =0x00;Cont =0x00;(2) 第一次按下按键的情况ReadData =0x01;Trg =0x01;Cont =0x01;(3) 按键按着不松(长按键)的情况ReadData =0x01;Trg =0x00;Cont =0x01;(4) 按键松开的情况ReadData =0x00;Trg =0x00;Cont =0x00;应用一:一次触发的按键处理——假设为蜂鸣器按键,按一下,蜂鸣器beep的响一声。
#define KEY_BEEP 0x01void KeyProc(void){if (Trg & KEY_BEEP) // 如果按下的是KEY_BEEP{Beep(); // 执行蜂鸣器处理函数}}应用2:长按键的处理#define KEY_MODE 0x01 // 模式按键#define KEY_PLUS 0x02 // 加void KeyProc(void){if (Trg & KEY_MODE) // 如果按下的是KEY_MODE,而且你常按这按键也没有用,{ //它是不会执行第二次的哦,必须先松开再按下Mode++; // 模式寄存器加1}if (Cont & KEY_PLUS) // 如果“加”按键被按着不放{cnt_plus++; // 计时if (cnt_plus > 100) // 20ms*100 = 2S 如果时间到{Func(); // 你需要的执行的程序}}}延时消抖问题?真正的单片机入门,是从学会处理多任务开始的。
极其简单好用的按键扫描程序(C语言)不过我在网上游逛了很久,也看过不少源程序了,没有发现这种按键处理办法的踪迹,所以,我将他共享出来,和广大同僚们共勉。
我非常坚信这种按键处理办法的便捷和高效,你可以移植到任何一种嵌入式处理器上面,因为C语言强大的可移植性。
同时,这里面用到了一些分层的思想,在单片机当中也是相当有用的,也是本文的另外一个重点。
对于老鸟,我建议直接看那两个表达式,然后自己想想就会懂的了,也不需要听我后面的自吹自擂了,我可没有班门弄斧的意思,hoho~~但是对于新手,我建议将全文看完。
因为这是实际项目中总结出来的经验,学校里面学不到的东西。
以下假设你懂C语言,因为纯粹的C语言描述,所以和处理器平台无关,你可以在MCS-51,AVR,PIC,甚至是ARM平台上面测试这个程序性能。
当然,我自己也是在多个项目用过,效果非常好的。
好了,工程人员的习惯,废话就应该少说,开始吧。
以下我以AVR的MEGA8作为平台讲解,没有其它原因,因为我手头上只有AVR的板子而已没有51的。
用51也可以,只是芯片初始化部分不同,还有寄存器名字不同而已。
核心算法:unsigned char Trg;unsigned char Cont;void KeyRead( void ){unsigned char ReadData = PINB^0xff; // 1Trg = ReadData & (ReadData ^ Cont); // 2Cont = ReadData; // 3}完了。
有没有一种不可思议的感觉?当然,没有想懂之前会那样,想懂之后就会惊叹于这算法的精妙!!下面是程序解释:Trg(triger)代表的是触发,Cont(continue)代表的是连续按下。
1:读PORTB的端口数据,取反,然后送到ReadData 临时变量里面保存起来。
2:算法1,用来计算触发变量的。
一个位与操作,一个异或操作,我想学过C语言都应该懂吧?Trg为全局变量,其它程序可以直接引用。
一个51单片机的键盘扫描程序,算法简单有效/****************************************键盘_不采用定时器_不延时特点:按键在松手后有效,灵敏度高,消耗资源少,运行效率高独立键盘为:K01=P2^4;K02=P2^5;K03=P2^6;K04=P2^7;矩阵键盘为:行(上到下)_P2.3_P2.2_P2.1_P2.0列(左到右)_P2.7_P2.6_P2.5_P2.4提供的操作函数://独立键盘.无按键动作时其返回值num_key=0,否则返回按键号num_keyextern unsigned char keyboard_self();//矩阵键盘.无按键动作时其返回值num_key=0,否则返回按键号num_key****检测高四位extern unsigned char keyboard_matrix();****************************************/先看独立键盘(和矩阵键盘的算法一样)-----------------------------------------------------------------------#include<reg52.h>#include<intrins.h>//独立键盘.无按键动作时其返回值num_key=0,否则返回按键号num_keyextern unsigned char keyboard_self(){unsigned char num_key=0;//按键号unsigned char temp=0;//用于读取P2线上按键值static unsigned char temp_code=0;//保存按键值static unsigned char num_check=0;//低电平有效次数static unsigned char key_flag=0;//按键有效标识temp=P2&0xF0;//读取P2线数据if(temp!=0xF0)//低电平判断{num_check++;if(num_check==10)//连续10次(10ms)低电平有效,则认为按键有效{key_flag=1;//使能按键有效标识temp_code=temp;//保存按键值}}else//松手时判断{num_check=0;if(key_flag==1)//按键有效{key_flag=0;switch(temp_code)//读取按键号{case 0xE0: num_key=1;break;case 0xD0: num_key=2;break;case 0xB0: num_key=3;break;case 0x70: num_key=4;break;}}}return(num_key);}现在是矩阵键盘的-----------------------------------------------------------------------#include<reg52.h>#include<intrins.h>//矩阵键盘.无按键动作时其返回值num_key=0,否则返回按键号num_key****检测高四位extern unsigned char keyboard_matrix(){unsigned char num_key=0;//按键号unsigned char temp=0;//读取P2口线数据static unsigned char temp_code=0;//用于保存按键值static unsigned char temp_circle=0xFE;//保存P2线上的循环扫描值static unsigned char num_check=0;//低电平计数static unsigned char key_flag=0;//按键有效标识P2=temp_circle;//0xFXtemp=P2;//读取P2口线数据if(temp!=temp_circle)//有按键动作{num_check++;//低电平计数|逢低电平加1if(num_check==10)//连续10次(10ms)低电平有效{key_flag=1;//按键有效标识置1temp_code=temp;//保存按键值}}else//松手OR无按键动作,此时应该改变扫描线{num_check=0;if(key_flag==1)//按键有效判断{key_flag=0;switch(temp_code)//读取按键号{//P2^0线case 0xEE: num_key=1;break;case 0xDE: num_key=2;break;case 0xBE: num_key=3;break;case 0x7E: num_key=4;break;//P2^1线case 0xED: num_key=5;break;case 0xDD: num_key=6;break;case 0xBD: num_key=7;break;case 0x7D: num_key=8;break;//P2^2线case 0xEB: num_key=9;break;case 0xDB: num_key=10;break;case 0xBB: num_key=11;break;case 0x7B: num_key=12;break;//P2^3线case 0xE7: num_key=13;break;case 0xD7: num_key=14;break;case 0xB7: num_key=15;break;case 0x77: num_key=16;break;}}temp_circle=_crol_(temp_circle,1);//改变扫描线if(temp_circle==0xEF){temp_circle=0xFE;}}return(num_key);//返回按键号}/************************************************************************* 未按键时,扫描线一直变化。
一、概述TM1650是一种带键盘扫描接口的LED(发光二极管显示器)驱动控制专用IC,内部集成有MCU 数字接口、数据锁存器、LED驱动、键盘扫描等电路。
本产品质量可靠、稳定性好、抗干扰能力强。
主要适用于机顶盒、家电设备(智能热水器、微波炉、洗衣机、空调、电磁炉)、电子称、智能电表等数码管,可适用于24小时长期连续工作的应用场合。
二、特性说明•两种显示模式(8段×4 位和 7 段×4 位)•支持单个按键7x4bit(28个按键)和组合按键(4个)• 8••高速2••••支持••三、四、管脚功能定义:1结束信号:保持CLK为“1”电平,DAT从“0”跳“1”,认为是结束信号,如(图1)E段;2、ACK信号如果本次通讯正常,芯片在串行通讯的第8个时钟下降沿后,TM1650主动把DAT拉低。
直到检测到CLK来了上升沿,DAT释放为输入状态(对芯片而言),如(图1)D段。
3、写“1”和写“0”写“1”:保持DAT为“1”电平,CLK从“0”跳到“1”,再从“1”跳到“0”,则认为是写入“1”如(图1)B段。
写“0”:保持DAT为“0”电平,CLK从“0”跳到“1”,再从“1”跳到“0”,则认为是写入“0”如(图1) C段。
4、 一个字节(8位)数据传输格式12345678B7B6B5B4B3B2B1B0DATCLK图2一个字节数据的传输格式如图2,数据发送时MSB 在前,LSB 在后,即高位先进。
微处理器的数据通过2线串行接口和TM1650通信,当CLK 是高电平时,DAT 上的信号必须保持不变;只有CLK 上的时钟信号为低电平时,DAT 上的信号才能改变。
数据输入的开始条件是CLK 为高电平时,DAT 由高变低;结束条件是CLK 为高时,DAT 由低电平变为高电平。
5、 写显示操作6SCL 7图5 读按键时序command :读按键命令4FH ;key_data :读按键数据(一个字节)。
单片机按键扫描数码管显示C语言程序按键扫描数码管显示程序共定义了6个键的功能:K1、K2、K3、K4以及K5、K8组成的一对复合键,其中K2,K3为连击键,K5为上档键。
在正常工作模式下按K1则切换至状态,在设定模式下按K1键循环选择4个数码管中的某个,被选中的数码管闪烁,此时单按K2键显示数值加1;常按K2显示数值以一定速度递增,同时数码管停止闪烁,当K2松开,数码管恢复闪烁,显示数值停留在K2松开前的值上。
K3完成的功能和K2类似。
其完成减操作。
这2个键只有在设定状态才有效,可以有效防止误操作。
K4为确认键,按下该键回到正常显示状态,所有指示灯熄灭,数码管显示刚刚设定的数值。
K5+K8这对复合键执行复位操作,任何情况下同时按下K5和K8或先按下K5再按下K8,所有数码管的显示全为0,指示灯全灭,进入正常显示状态。
同时程序还对如下几个异常操作进行了处理:1. 2个或多个功能键同时按下2. 一个功能键按下未释放,又按另一个功能键,然后再松开其中一个功能键3. 先按下功能键再按下上档键4. 多个上档键和一个功能键同时按下,此时不做处理。
等到松开其他上档键,只剩下一个上5. 档键和一个功能键时才执行这对复合键;或松开所有上档键,处理单一功能键。
/****************************************************************************** */#include <iom8v.h>#include <macros.h>#define uchar unsigned char#define uint unsigned int#define RCtrl 0x20 //定义上挡键第5键#define RConti 0xfe //定义连击键第6键#define N 2 //去抖年龄下限#define MaxRate 50 //重复前的延迟值 600ms#define MinRate 20 //重复速度 240ms#define leddark 83 //闪烁时灭时间1s#define ledshow 83 //闪烁时亮时间1s#define decimal 0x80 //小数点的段数#define KEY_DDR DDRC#define KEY_PORTO PORTC#define KEY_PORTI PINC#define OUT 0x3f#define IN 0xc0#define KeyValue 0x3f#define LEDD_DDR DDRB#define LEDD_PORTO PORTB#define LEDS_DDR DDRD#define LEDS_PORTO PORTD#define LEDS_MASK 0xfc#define LEDS_NUM 0x06#define TRUE 1#define FALSE 0/*定义键盘扫描程序返回数据类型*/typedef struct{uchar shiftcnt; //上档键的个数值uchar funcnt; //功能键的个数值uchar shiftval; //最后扫描到的上档键的值uchar funval; //最后扫描到的功能键的值} keyret;/*定义显示字符段码*/uchar const led_stroke[19] ={//0,1,2,3,4,5,6,7,8,90x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,//a,b,C,d,e,F,P,0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x73,//all on all off0xff, 0x00};/*定义位选码*/uchar const led_cs[LEDS_NUM] ={0xfb, //111110110xf7, //111101110xef, //111011110xdf, //110111110xbf, //101111110x7f //01111111};uchar led_buf[LEDS_NUM] ={0x73, 0x81, 0x82, 0x83, 0x84,0x85};uchar *pb = &led_buf[1]; //定义指向数码管数据缓冲区的指针/*定义全局变量*/uchar task, state; //task:按键状态,0:去抖 1,重复的延迟 2,重复//state:显示位置变量uchar keydone, keyprocess; //keydone: 按键任务完成标志,为1表示已完成//keyprocess: 按键有效标志,为1时表示对按键执行uchar keypre[2] ={0x00, 0x00}; //存放上次功能键和上档键的键值//keypre0存放功能键uchar blink, ledtime; //blink:闪烁控制寄存器,某位为1时闪烁//d7d6d5d4d3d2d1d0//xxxx1111//ledtime:累计闪烁时已点亮和已熄灭的时间uchar ledtask; //ledtask: 当前的闪烁状态,0代表亮uchar keymark; //keymark:只是当前工作状态,为1时处于设定状态,为0时正常工作uchar enflash; //enflash:闪烁使能标志,1闪烁#define shut_dis() LEDS_PORTO|=LEDS_MASK; //shut display/****************************************************************************** **函数原型: uchar _crol_(uchar data,uchar shiftbit);*功能:字节左移shiftbit*参数:*说明:****************************************************************************** */uchar _crol_(uchar data,uchar shiftbit){data &=0xff;if(shiftbit>8)return 0;return ((~data)<<shiftbit);}/****************************************************************************** **函数原型: uchar _cror_(uchar data,uchar shiftbit);*功能:字节右移shiftbit*参数:*说明:****************************************************************************** */uchar _cror_(uchar data,uchar shiftbit){data &=0xff;if(shiftbit>8)return 0;return ((~data)>>shiftbit);}/****************************************************************************** **函数原型: void send_shift(uchar d);*功能: 将显示数据由B口送出****************************************************************************** */void send_shift(uchar data){LEDD_PORTO = data;}/****************************************************************************** **函数原型: void lflash();*功能:闪烁处理。
以下假设你懂C语言,因为纯粹的C语言描述,所以和处理器平台无关,你可以在MCS-51,AVR,PIC,甚至是ARM平台上面测试这个程马加爵序性能。
当然,我自己也是在多个项目用过,效果非常好的。
好了,工程人员的习惯,废话就应该少说,开始吧。
核心算法:unsigned char Trg;unsigned char Cont;void KeyRead( void ){unsigned char ReadData = PINB^0xff; // 1Trg = ReadData & (ReadData ^ Cont); // 2 Cont = ReadData; // 3}完了。
有没有一种不可思议的感觉?当然,没有想懂之前会那样,想懂之后就会惊叹于这算法的精妙!!下面是程序解释:Trg(triger)代表的是触发,Cont(continue)代表的是连续按下。
1:读PORTB的端口数据,取反,然后送到ReadData 临时变量里面保存起来。
2:算法1,用来计算触发变量的。
一个位与操作,一个异或操作,我想学过C语言都应该懂吧?Trg为全局变量,其它程序可以直接引用。
3:算法2,用来计算连续变量。
看到这里,有种“知其然,不知其所以然”的感觉吧?代码很简单,但是它到底是怎么样实现我们的目的的呢?好,下面就让我们绕开云雾看青天吧。
我们最常用的按键接法如下:AVR是有内部上拉功能的,但是为了说明问题,我是特意用外部上拉电阻。
那么,按键没有按下的时候,读端口数据为1,如果按键按下,那么端口读到0。
下面就看看具体几种情况之下,这算法是怎么一回事。
(1)没有按键的时候端口为0xff,ReadData读端口并且取反,很显然,就是0x00 了。
Trg = ReadData & (ReadData ^ Cont); (初始状态下,Cont也是为0的)很简单的数学计算,因为ReadD ata为0,则它和任何数“相与”,结果也是为0的。
经典按键扫描程序核心算法:unsigned char Trg;unsigned char Cont;void KeyRead( void ){unsigned char ReadData = PINB^0xff; // 1Trg = ReadData & (ReadData ^ Cont); // 2Cont = ReadData; // 3}完了。
有没有一种不可思议的感觉?当然,没有想懂之前会那样,想懂之后就会惊叹于这算法的精妙!!下面是程序解释:Trg(triger)代表的是触发,Cont(continue)代表的是连续按下。
1:读PORTB的端口数据,取反,然后送到ReadData临时变量里面保存起来。
2:算法1,用来计算触发变量的。
一个位与操作,一个异或操作,我想学过C语言都应该懂吧?Trg为全局变量,其它程序可以直接引用。
3:算法2,用来计算连续变量。
看到这里,有种“知其然,不知其所以然”的感觉吧?代码很简单,但是它到底是怎么样实现我们的目的的呢?好,下面就让我们绕开云雾看青天吧。
我们最常用的按键接法如下:AVR是有内部上拉功能的,但是为了说明问题,我是特意用外部上拉电阻。
那么,按键没有按下的时候,读端口数据为1,如果按键按下,那么端口读到0。
下面就看看具体几种情况之下,这算法是怎么一回事。
(1)没有按键的时候端口为0xff,ReadData读端口并且取反,很显然,就是0x00了。
Trg = ReadData & (ReadData ^ Cont); (初始状态下,Cont也是为0的)很简单的数学计算,因为ReadData为0,则它和任何数“相与”,结果也是为0的。
Cont = ReadData;保存Cont其实就是等于ReadData,为0;结果就是:ReadData=0;Trg=0;Cont=0;(2)第一次PB0按下的情况端口数据为0xfe,ReadData读端口并且取反,很显然,就是0x01了。
单片机4*4键盘扫描程序时如何开启的?按照行顺序,一行一行的开启,如下图:4*4共16键,假设P0.0-P0.3为H0-H3,P0.4-P0.7为L0-L3(列) L0 L1 L2 L3(行) H0 0 1 2 3H1 4 5 6 7H2 8 9 A BH3 C D E F首先让H0 = 0,然后依次检测L0-L3,看那个键按下了,则对应的L0-L3为0,这样第一行检测结束。
比如扫描H0行时第一个键按下了,则L0=0,获得的P0=0xee,你也可以返回一个值,比如就是0,来代表第一个键(0)被按下,这样依次检测就扫描满16个键就行了。
4*4键盘扫描程序#include <reg52.h>//包含头文件#define uchar unsigned char#define uint unsigned intunsigned char const dofly[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//0-Fuchar keyscan(void);void delay(uint i);void main(){uchar key;P2=0x00;//1数码管亮按相应的按键,会显示按键上的字符while(1){key=keyscan();//调用键盘扫描,switch(key){case 0x7e:P0=dofly[0];break;//0 按下相应的键显示相对应的码值case 0x7d:P0=dofly[1];break;//1case 0x7b:P0=dofly[2];break;//2case 0x77:P0=dofly[3];break;//3case 0xbe:P0=dofly[4];break;//4case 0xbd:P0=dofly[5];break;//5case 0xbb:P0=dofly[6];break;//6case 0xb7:P0=dofly[7];break;//7case 0xde:P0=dofly[8];break;//8case 0xdd:P0=dofly[9];break;//9case 0xdb:P0=dofly[10];break;//acase 0xd7:P0=dofly[11];break;//bcase 0xee:P0=dofly[12];break;//ccase 0xed:P0=dofly[13];break;//dcase 0xeb:P0=dofly[14];break;//ecase 0xe7:P0=dofly[15];break;//f}}}uchar keyscan(void)//键盘扫描函数,使用行列反转扫描法{uchar cord_h,cord_l;//行列值P3=0x0f; //行线输出全为0cord_h=P3&0x0f; //读入列线值if(cord_h!=0x0f) //先检测有无按键按下{delay(100); //去抖if(cord_h!=0x0f){cord_h=P3&0x0f; //读入列线值P3=cord_h|0xf0; //输出当前列线值cord_l=P3&0xf0; //读入行线值return(cord_h+cord_l);//键盘最后组合码值}}return(0xff); //返回该值}void delay(uint i)//延时函数{while(i--);}以下为详细解释:假设按下的是S1键进行如下检测(4*4键盘)先在P3口输出p3 00001111低四位行会有变化cord_h =00001111&00001110 =00001110if !=00001111延时0.1uscord_h=00001110&00001111=00001110if !=00001111P3再输出11111110P3 =00001110|11110000=11111110输出高四位cord_l=P3&0xf0 //此时P3口就是输入值01111110 而不是上面的11111110cord_l=01111110&11110000=01110000cord_h+cord_l=00001110+01110000=01111110=0x7e //此编码即为S1的编码#include <reg52.h>//包含头文件#define uchar unsigned char#define uint unsigned intunsigned char const table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//0-Fuchar keyscan(void);void delay(uint i);void main(){uchar key;P2=0x00;//1数码管亮按相应的按键,会显示按键上的字符while(1){key=keyscan();//调用键盘扫描,switch(key){case 0x7e:P0=table[0];break;//0 按下相应的键显示相对应的码值case 0x7d:P0=table[1];break;//1case 0x7b:P0=table[2];break;//2case 0x77:P0=table[3];break;//3case 0xbe:P0=table[4];break;//4case 0xbd:P0=table[5];break;//5case 0xbb:P0=table[6];break;//6case 0xb7:P0=table[7];break;//7case 0xde:P0=table[8];break;//8case 0xdd:P0=table[9];break;//9case 0xdb:P0=table[10];break;//acase 0xd7:P0=table[11];break;//bcase 0xee:P0=table[12];break;//ccase 0xed:P0=table[13];break;//dcase 0xeb:P0=table[14];break;//ecase 0xe7:P0=table[15];break;//f}}}uchar keyscan(void)//键盘扫描函数,使用行列反转扫描法{uchar cord_h,cord_l;//行列值P3=0x0f; //行线输出全为0cord_h=P3&0x0f; //读入列线值if(cord_h!=0x0f) //先检测有无按键按下{delay(100); //去抖cord_h=P3&0x0f; //读入列线值if(cord_h!=0x0f){P3=cord_h|0xf0; //输出当前列线值cord_l=P3&0xf0; //读入行线值return(cord_h+cord_l);//键盘最后组合码值}}return(0xff); //返回该值}void delay(uint i)//延时函数{while(i--);}在P3口做的键盘你的去抖检测没有做好通过电平输入来引发中断,必须是由P3.2或P3.3引脚输入,这样才能触发中断。
4.3.2 按键扫描处理程序流程图
(1)按键扫描处理代码
/* 功能实现参数,参数mode为Key_Menu按键选择的功能模块*/
void Display(unsigned char mode)
{
switch (mode)//显示模式,0为显示实时温度,1为显示温度上限,2为显示温度下限
{
case 0: if (temperature < 0)//温度小于0
{
temperature = -temperature;//换为正温度
DisplaySeg(0x40, temperature % 1000); //0x40为负号
}
else DisplaySeg(codeSeg[temperature % 10000 / 1000], temperature % 1000);
break;
case 1: DisplaySeg(0x76, alarm_temp_H * 10); break;//显示温度上限,0x76为H字符
case 2: DisplaySeg(0x38, alarm_temp_L * 10); break;//显示温度下限,0x38为L字符
default:break;
}
}
/* 按键扫描和处理函数*/
void KeyScan(void)
{
if (Key_Menu == 0)//判断按键是否被按下
{
DelayMs(10);//延时10毫秒,去抖动干扰
if (Key_Menu == 0)//再次确认按键是否被按下
{
while(Key_Menu == 0)Display(menu);//等待按键释放,器件扫描数码管
menu++;//功能键,功能切换
if (menu == 3)menu = 0;//三个功能切换完
}
}
if (Key_Add == 0)
{
DelayMs(10);
if (Key_Add == 0)
{
while(Key_Add == 0)Display(menu);
switch (menu)
{
case 1: if (alarm_temp_H < 50)alarm_temp_H++;break;//加温度上限
case 2: if (alarm_temp_L < 27)alarm_temp_L++;break;//加温度下限
default:break;
}
}
}
if (Key_Dec == 0)
{
DelayMs(10);
if (Key_Dec == 0)
{
while(Key_Dec == 0)Display(menu);
switch (menu)
{
case 1: if (alarm_temp_H > 30)alarm_temp_H--;break;//减温度上限
case 2: if (alarm_temp_L > 7)alarm_temp_L--;break;//减温度下限
default:break;
}
}。