这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界» 论坛首页» 高校专区» 坤创E-Geek/天科大新电社» 05. 单片机I/O口的操作3-机械按键篇

共18条 1/2 1 2 跳转至

05. 单片机I/O口的操作3-机械按键篇

高工
2019-01-24 20:26:20 打赏

各位同学们大家好~!又到了学习时间了。上一帖中有小可耐回帖,说希望在蓝桥板的代码里多看到一些注释,今天所发的数码管训练题目的帖子,在蓝桥板的代码中必要的地方都重新添加上了注释。让大家能够看得更明白。

今天的题目,主要针对按键进行讲解。有同学觉得按键很简单,但实际上,要想写出一个响应灵敏,并能够灵活触发的各种按键其实并不容易,尤其是当题目中要求我们实现双击、长按等这种不同动作种类的按键时,就更需要大家动一动自己的水灵的小脑袋了。

而且,从历年真题来看,按键和数码管是一样的,也是必考题。题目中通过按键结合数码管,设计的各种逻辑,做起来真的还是蛮有一定的挑战性的。下面咱们就来先针对按键和数码管进行综合练习。本次的练习题目和代码发出来,供大家参考学习。

题1、按下按键S4-S7,数码管分别显示键值1-4

题2、数码管初始显示0,单击S4,显示值加1,单击S5数值减1,长按S6数值连续加1,长按S7数值连续减1

下面依旧给出两个题目的代码,并在文末给出所有代码压缩包的下载链接,欢迎大家下载查看。如有任何问题,请大家在下方回帖告知~不胜感激的同时,还可能有U盘赠送的哦~

-----------------------蓝桥板代码---------------------

蓝桥板题1代码:

/********************************************* 1、按下按键S4-S7,数码管分别显示键值1-4 **********************************************/ #include #include"intrins.h" #define uchar unsigned char #define uint unsigned int uchar code smgtab[]={0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,0X90,0XBF,0XFF}; //共阳数码值 uchar yi,er,san,si,wu,liu,qi,ba; void display1(uchar yi,uchar er); //第1第2两个数码管的显示函数 void display2(uchar san,uchar si); //第3第4两个数码管的显示函数 void display3(uchar wu,uchar liu ); //第5第6两个数码管的显示函数 void display4(uchar qi,uchar ba); //第7第8两个数码管的显示函数 void keyscan(); //按键扫描函数 void delayms(uint z); //毫秒级别延时函数 void init(); //单片机初始化函数 void main() { init(); yi=11;er=11;san=11;si=11;wu=11;liu=11;qi=11;ba=11; while(1) { keyscan(); display1(yi,er); display2(san,si); display3(wu,liu ); display4(qi,ba); } } void delayms(uint z) //延时函数 { uint x,y; for(x=z;x>0;x--) for(y=845;y>0;y--); } void init() { P2=0xa0; P0=0x00; //关闭继电器蜂鸣器 P2=0x80; P0=0xff; //关闭led灯 P2=0xc0; P0=0xff; //选择所有数码管 P2=0xff; P0=0xff; //关闭所有数码管 } void keyscan() //独立按键 { if(P30==0) //s7 { delayms(5); //消抖 if(P30==0) { si=4; //键值4 } while(!P30); } else if(P31==0) //s6 { delayms(5); if(P31==0) { san=3; //键值3 } while(!P31); } else if(P32==0) //s5 { delayms(5); if(P32==0) { er=2; //键值2 } while(!P32); } else if(P33==0) //s4 { delayms(5); if(P33==0) { yi=1; //键值1 } while(!P33); } } void display1(uchar yi,uchar er) { P2=0xc0; P0=0x01; //选择第一个数码管 P2=0xff; P0=smgtab[yi]; //段选 delayms(1); P2=0xc0; P0=0x02; //选择第二个数码管 P2=0xff; P0=smgtab[er]; //段选 delayms(1); } void display2(uchar san,uchar si) { P2=0xc0; P0=0x04; //选择第三个数码管 P2=0xff; P0=smgtab[san]; //段选 delayms(1); P2=0xc0; P0=0x08; //选择第四个数码管 P2=0xff; P0=smgtab[si]; //段选 delayms(1); } void display3(uchar wu,uchar liu ) { P2=0xc0; P0=0x10; //选择第五个数码管 P2=0xff; P0=smgtab[wu]; //段选 delayms(1); P2=0xc0; P0=0x20; //选择第六个数码管 P2=0xff; P0=smgtab[liu]; //段选 delayms(1); } void display4(uchar qi,uchar ba) { P2=0xc0; P0=0x40; //选择第七个数码管 P2=0xff; P0=smgtab[qi]; //段选 delayms(1); P2=0xc0; P0=0x80; //选择第八个数码管 P2=0xff; P0=smgtab[ba]; //段选 delayms(1); }

蓝桥板题2代码:

/************************************************************************************ 2、数码管初始显示0,单击S4,显示值加1,单击S5数值减1,长按S6数值连续加1,长按S7数值连续减1 (评论区有同学觉得长按的设计有点小BUG,已经在代码中重新更正,请大家验证) *************************************************************************************/ #include #include"intrins.h" #define uchar unsigned char #define uint unsigned int #define TT 5 uchar code smgtab[]={0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,0X90,0XBF,0XFF}; uchar s4,s5,num; uint count; uchar One,Two,Three,Four,Five,Six,Seven,Eight; void init();//蓝桥杯板初始化程序 void keyScan();//键盘扫描 void delayms(uint z);//ms级延时函数 void displayOne(uchar One,uchar Two);//第一二个数码管显示函数 void displayTwo(uchar Three,uchar Four);//第三四个数码管显示函数 void displayThree(uchar Five,uchar Six );//第五六个数码管显示函数 void displayFour(uchar Seven,uchar Eight);//第七八个数码管显示函数 /********************主函数****************************/ /*void main() { init(); while(1) { One=11;Two=11;Three=11;Four=11;Five=11;Six=num/100;Seven=num/10%10;Eight=num%10; if(s4==1) //显示值加1 { s4=0; if((num>=0)&&(num<=255)) {num=num+1;} //uchar计数范围为0-255; else if(num>255){num=255;} } else if(s5==1) //显示值减1 { s5=0; if((num>0)&&(num<=255)) {num=num-1;} else if(num==0){num=0;} } else if(P31==0) //s6长按连续加1 { delayms(5); if(P31==0) { count=0; while(P31==0) { delayms(40); count++; } if(count>TT) { if((num>=0)&&(num<255))num=num+1; else if(num==255)num=255; } } } else if(P30==0) //s7长按连续减1 { delayms(5); if(P30==0) { count=0; while(P30==0) { delayms(40); count++; } if(count>TT) { if((num>0)&&(num<=255))num=num-1; else if(num==0)num=0; } } } keyScan(); displayOne(One,Two); displayTwo(Three,Four); displayThree(Five,Six); displayFour(Seven,Eight); } } void delayms(uint z) //延时函数 { uint x,y; for(x=z;x>0;x--) for(y=845;y>0;y--); } void init() { P2=0xa0; P0=0x00; //关闭继电器蜂鸣器 P2=0x80; P0=0xff; //关闭led灯 P2=0xc0; P0=0xff; //选择所有数码管 P2=0xff; P0=0xff; //关闭所有数码管 } void keyscan() { if(P32==0) //s5 { delayms(5); if(P32==0) { s5=1; //按键s5的标志位 } while(!P32); } else if(P33==0) //s4 { delayms(5); if(P33==0) { s4=1; //按键s4的标志位 } while(!P33); } } void displayOne(uchar One,uchar Two) { P2=0xc0; P0=0x01; //选择第一个数码管 P2=0xff; P0=smgtab[One]; //段选 delayms(1); P2=0xc0; P0=0x02; //选择第二个数码管 P2=0xff; P0=smgtab[Two]; //段选 delayms(1); } void displayTwo(uchar Three,uchar Four) { P2=0xc0; P0=0x04; //选择第三个数码管 P2=0xff; P0=smgtab[Three]; //段选 delayms(1); P2=0xc0; P0=0x08; //选择第四个数码管 P2=0xff; P0=smgtab[Four]; //段选 delayms(1); } void displayThree(uchar Five,uchar Six ) { P2=0xc0; P0=0x10; //选择第五个数码管 P2=0xff; P0=smgtab[Five]; //段选 delayms(1); P2=0xc0; P0=0x20; //选择第六个数码管 P2=0xff; P0=smgtab[Six]; //段选 delayms(1); } void displayFour(uchar Seven,uchar Eight) { P2=0xc0; P0=0x40; //选择第七个数码管 P2=0xff; P0=smgtab[Seven]; //段选 delayms(1); P2=0xc0; P0=0x80; //选择第八个数码管 P2=0xff; P0=smgtab[Eight]; //段选 delayms(1); }


-----------------------小红板代码----------------------

小红板题1代码:

/************************************************* 1、按下独立按键s5,s10,s15,s20,数码管分别显示键值1-4 **************************************************/ #include"reg52.h" #define uchar unsigned char #define uint unsigned int sbit P_HC595_SER = P2^7; //pin 14 SER data input sbit P_HC595_RCLK = P2^5; //pin 12 RCLk store (latch) clock sbit P_HC595_SRCLK = P2^6; //pin 11 SRCLK Shift data clock sbit s5=P3^0; sbit s10=P3^1; sbit s15=P3^2; sbit s20=P3^3; uchar code smgtab[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00}; uchar yi,er,san,si,wu,liu,qi,ba; void display1(uchar yi,uchar er); void display2(uchar san,uchar si); void display3(uchar wu,uchar liu); void display4(uchar qi,uchar ba); void keyscan(); void SMG_Init(); void Send_595(unsigned char dat); void delayms(uint z); void main() { SMG_Init(); yi=16;er=16;san=16;si=16;wu=16;liu=16;qi=16;ba=16; while(1) { keyscan(); display1(yi,er); display2(san,si); display3(wu,liu); display4(qi,ba); } } void delayms(uint z) //ms级别延时函数 { uint x,y; for(x=z;x>0;x--) for(y=110;y>0;y--); } void keyscan() { if(s5==0) //s5 { delayms(5); //延时消抖5-10ms if(s5==0) { yi=1; } while(!s5); // 松手检测 } else if(s10==0) //s10 { delayms(5); if(s10==0) { er=2; } while(!s10); } else if(s15==0) //s15 { delayms(5); if(s15==0) { san=3; } while(!s15); } else if(s20==0) //s20 { delayms(5); if(s20==0) { si=4; } while(!s20); } } void SMG_Init() { Send_595(0x00); //同时选择8个数码管 Send_595(0x00); //让8个数码管同时显示消隐字符 P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 } void Send_595(unsigned char dat) //发送一个字节 { unsigned char i; for(i=0; i<8; i++) { if(dat & 0x80) //判断dat的最高位是否为1 P_HC595_SER = 1; //最高位如果为1,则令P2.7 = 1 else P_HC595_SER = 0; //最高位如果为0,则令P2.7 = 0 P_HC595_SRCLK = 0; P_HC595_SRCLK = 1; P_HC595_SRCLK = 0; //使用P2.6制造一个上升沿将这一位数据送到74HC595的QA输出端 dat = dat << 1; //将dat左移一位,取次高位 } } void display1(uchar yi,uchar er) { Send_595(0xfe); //选择最右边的一个数码管 Send_595(smgtab[yi]); P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 delayms(1); Send_595(0xfd); //选择最右边的一个数码管 Send_595(smgtab[er]); P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 delayms(1); } void display2(uchar san,uchar si) { Send_595(0xfb); //选择最右边的一个数码管 Send_595(smgtab[san]); P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 delayms(1); Send_595(0xf7); //选择最右边的一个数码管 Send_595(smgtab[si]); P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 delayms(1); } void display3(uchar wu,uchar liu) { Send_595(0xef); //选择最右边的一个数码管 Send_595(smgtab[wu]); P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 delayms(1); Send_595(0xdf); //选择最右边的一个数码管 Send_595(smgtab[liu]); P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 delayms(1); } void display4(uchar qi,uchar ba) { Send_595(0xbf); //选择最右边的一个数码管 Send_595(smgtab[qi]); P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 delayms(1); Send_595(0x7f); //选择最右边的一个数码管 Send_595(smgtab[ba]); P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 delayms(1); }


小红板题2代码:

/***************************************************************************************** 2、数码管初始显示0,单击S5,显示值加1,单击S10数值减1,长按S15数值连续加1,长按S20数值连续减1。 ******************************************************************************************/ #include"reg52.h" #define uchar unsigned char #define uint unsigned int #define TT 5 sbit P_HC595_SER = P2^7; //pin 14 SER data input sbit P_HC595_RCLK = P2^5; //pin 12 RCLk store (latch) clock sbit P_HC595_SRCLK = P2^6; //pin 11 SRCLK Shift data clock sbit s5=P3^0; sbit s10=P3^1; sbit s15=P3^2; sbit s20=P3^3; uchar code smgtab[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x40,0x00}; uint count; uchar ss5,ss10,num; uchar One,Two,Three,Four,Five,Six,Seven,Eight; void keyScan(); //键盘扫描 void SMG_Init();//数码管初始化函数 void delayms(uint z);//ms级延时函数 void Send_595(unsigned char dat); void displayOne(uchar One,uchar Two);//第一二个数码管显示函数 void displayTwo(uchar Three,uchar Four);//第三四个数码管显示函数 void displayThree(uchar Five,uchar Six );//第五六个数码管显示函数 void displayFour(uchar Seven,uchar Eight);//第七八个数码管显示函数 /****************************主函数*******************************/ void main() { SMG_Init(); while(1) { One=11;Two=11;Three=11;Four=11;Five=11;Six=num/100;Seven=num/10%10;Eight=num%10; if(ss5==1) { ss5=0; if((num>0)&&(num<=255)){num=num-1;} else if(num==0){num=0;} } else if(ss10==1) { ss10=0; if((num>=0)&&(num<=255)){num=num+1;} else if(num>255){num=255;} } else if(s15==0) //s15长按连续加1 { delayms(5); if(s15==0) { count=0; while(s15==0) { delayms(40); count++; } if(count>TT) { if((num>=0)&&(num<255))num=num+1; else if(num==255)num=255; } } } else if(s20==0) //s20长按连续减1 { delayms(5); if(s20==0) { count=0; while(s20==0) { delayms(40); count++; } if(count>TT) { if((num>0)&&(num<=255))num=num-1; else if(num==0)num=0; } } } keyScan(); displayOne(One,Two); displayTwo(Three,Four); displayThree(Five,Six); displayFour(Seven,Eight); } } void delayms(uint z) { uint x,y; for(x=z;x>0;x--) for(y=110;y>0;y--); } void keyscan() { if(s5==0) //s5 { delayms(5); if(s5==0) { ss5=1; //s5的标志位 } while(!s5); } else if(s10==0) //s10 { delayms(5); if(s10==0) { ss10=1; //s10的标志位 } while(!s10); } } void SMG_Init() { Send_595(0x00); //同时选择8个数码管 Send_595(0x00); //让8个数码管同时显示消隐字符 P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 } void Send_595(unsigned char dat) //发送一个字节 { unsigned char i; for(i=0; i<8; i++) { if(dat & 0x80) //判断dat的最高位是否为1 P_HC595_SER = 1; //最高位如果为1,则令P2.7 = 1 else P_HC595_SER = 0; //最高位如果为0,则令P2.7 = 0 P_HC595_SRCLK = 0; P_HC595_SRCLK = 1; P_HC595_SRCLK = 0; //使用P2.6制造一个上升沿将这一位数据送到74HC595的QA输出端 dat = dat << 1; //将dat左移一位,取次高位 } } void displayOne(uchar One,uchar Two) { Send_595(0xfe); //选择最右边的一个数码管 Send_595(smgtab[One]); P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 delayms(1); Send_595(0xfd); //选择最右边的一个数码管 Send_595(smgtab[Two]); P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 delayms(1); } void displayTwo(uchar Three,uchar Four) { Send_595(0xfb); //选择最右边的一个数码管 Send_595(smgtab[Three]); P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 delayms(1); Send_595(0xf7); //选择最右边的一个数码管 Send_595(smgtab[Four]); P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 delayms(1); } void displayThree(uchar Five,uchar Six ) { Send_595(0xef); //选择最右边的一个数码管 Send_595(smgtab[Five]); P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 delayms(1); Send_595(0xdf); //选择最右边的一个数码管 Send_595(smgtab[Six]); P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 delayms(1); } void displayFour(uchar Seven,uchar Eight) { Send_595(0xbf); //选择最右边的一个数码管 Send_595(smgtab[Seven]); P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 delayms(1); Send_595(0x7f); //选择最右边的一个数码管 Send_595(smgtab[Eight]); P_HC595_RCLK = 0; P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选 delayms(1); }

----------------所有代码打包下载链接------------------

05单片机IO口的操作3-机械按键篇_全部代码.zip

同学们,可以接着往下进行06. 单片机 外部中断的使用的题目练习~如对代码有任何问题,欢迎回帖指出来~

欢迎大家对本系列的支持,这是我们的动力!




菜鸟
2019-01-24 20:39:59 打赏
2楼

按键这个东西虽然看起来简单,但是使用非常灵活,往往是一个程序逻辑中最容易出错的地方,感谢楼主分享 mark了!


工程师
2019-01-25 00:22:35 打赏
3楼

写矩阵键盘的时候经常会出错,特别是搭配着LCD1602的时候,容易出现乱码,有点头疼… 以前还以为蓝桥板跟我们平时练习用的板子差不多,现在从代码上看起来,差别不少呀


院士
2019-01-25 10:47:39 打赏
4楼

可以将按键程序进行抽象分解,如,实现IO输出,IO输入,定时扫描,记录键位,返回键码等操作。


菜鸟
2019-01-25 13:39:36 打赏
5楼

感谢楼主的分享,接下来的帖子期待中


高工
2019-01-26 09:38:43 打赏
6楼

接触按键的小伙伴可以了解一下按键的状态机编程,以后写程序的时候会方便很多

简单来说可以给按键设定3种状态:状态0:无按键,状态1:已经按下,状态2:已经释放。按键可以一直处于状态0,也可以由状态0转为状态1,也可以由状态1转为 状态2,然后恢复到状态0。如此顺序循环。通过定时(例如定时器中断)每隔一段时间(例如10毫秒)检查一下按键 状态,根据上次检查的状态和当前的状态比较,来确定应该做什么。在上述时间间隔内,单片机就可以执行其他任务。一旦确认按键成立,就可以立即找出键码并进 行随后的键码处理程序而无需等待按键释放,加上适当的处置也可以对比较复杂的按键进行处理


菜鸟
2019-01-26 12:43:38 打赏
7楼

按键的逻辑编程是蓝桥杯的核心,都是围绕它来的



菜鸟
2019-01-29 09:55:34 打赏
8楼

我想问一下 小红版1的代码 后面显示yi er san si wu liu qi ba 的目的 及结果 是什么? 很迷


菜鸟
2019-01-31 13:52:56 打赏
9楼

我本来是在按键内部直接输入数码管位选段选,以至于没有达到本帖子小红版1的实验效果,然后我就用了老师的方法调整了按键内部繁琐的程序,然后就达到效果了,最后 输入验证码好麻烦- -


院士
2019-02-14 09:07:10 打赏
10楼

学习学习,谢谢分享!!


共18条 1/2 1 2 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册]