这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 高校专区 » 坤创E-Geek/天科大新电社 » 第十一届蓝桥杯信息技术人才大赛-单片机竞赛备赛分享四——基础部分综合实战

共4条 1/1 1 跳转至

第十一届蓝桥杯信息技术人才大赛-单片机竞赛备赛分享四——基础部分综合实战

工程师
2020-01-15 16:00:08     打赏

哈喽哈喽大家好,我是阿飞的小蝴蝶,大家可以叫我阿飞或者小飞,我又回来啦。。。


通过学习前两天的内容,大家应该对板子上这些基础的外设已经能够掌握好了吧,特别是昨天的内容,机械按键对于我们来说还是非常重要的,所以说我们只有多加练习才能比较好的掌握按键与其他模块的配合和设计逻辑。好的,我们先来看一下昨天留下的两个小练习,我将通过这两个练习题来带大家深入的了解一下机械按键的使用


好的,我们直接来看一下昨天练习的代码:

第一题:独立按键与数码管配合使用,数码管显示秒表显示格式为“00-00-00”(分-秒-十毫秒位),按键S4控制秒表开始计时、S5暂停、S6重置


#include "stc15f2k60s2.h"
#define u8 unsigned char 
#define u16 unsigned int
u8 time_10ms = 0,time_sec = 0,time_min = 0,time = 0; //变量分别用于 10毫秒位、秒位、分位、计时标志 的存放
bit start = 1;  //开始、暂停 的标志位
u8 code smg_we[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
u8 code smg_du[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
void HC138(u8 i)  
{  
 switch(i)  
 {  
  case 4: P2 = (P2 & 0x1f) | 0x80; break;  
  case 5: P2 = (P2 & 0x1f) | 0xa0; break;  
  case 6: P2 = (P2 & 0x1f) | 0xc0; break;  
  case 7: P2 = (P2 & 0x1f) | 0xe0; break;  
 }  
}  
  
void delayms(u16 z)  
{    
 u16 x,y;    
 for(x=z;x>0;x--)    
  for(y=845;y>0;y--);    
}   
  
void init_P0(void)        
{  
 HC138(4);            
 P0 = 0xff;
 HC138(5);        
 P0 = 0x00;       
 HC138(6);          
 P0 = 0xff;    
 HC138(7);             
 P0 = 0xff;           
}
void smg_scan()
{
 static u8 wei;
 P0 = 0x00;
 HC138(6);
 P0 = smg_we[wei];
 switch(wei)
 {
  case 0: HC138(7); P0 = smg_du[time_min/10]; break;
  case 1: HC138(7); P0 = smg_du[time_min%10]; break;
  case 2: HC138(7); P0 = 0xbf; break;
  case 3: HC138(7); P0 = smg_du[time_sec/10]; break;
  case 4: HC138(7); P0 = smg_du[time_sec%10]; break;
  case 5: HC138(7); P0 = 0xbf; break;
  case 6: HC138(7); P0 = smg_du[time_10ms/10]; break;
  case 7: HC138(7); P0 = smg_du[time_10ms%10]; break;
 }
 wei++;
 if(wei == 8) wei = 0;
 delayms(2);
 if(start == 1)
  time++; 
}
void key_scan()
{
 if(P33 == 0)
 {
  delayms(10);
  if(P33 == 0)
  {
   start = 1;
   while(P33 == 0)
    smg_scan();
  }
 }
 if(P32 == 0)
 {
  delayms(10);
  if(P32 == 0)
  {
   start = 0;
   while(P32 == 0)
    smg_scan();
  }
 }
 if(P31 == 0)
 {
  delayms(10);
  if(P31 == 0)
  {
   time_10ms = 0;
   time_sec = 0;
   time_min = 0;
   start = 0;
   while(P31 == 0)
    smg_scan();
  }
 }
}
void main()
{
 init_P0();
 while(1)
 {
  key_scan();
  smg_scan();
  if(time == 5) { time_10ms++; time = 0; }
  if(time_10ms == 100) { time_sec++; time_10ms = 0; }
  if(time_sec == 60) { time_min++; time_sec = 0;  }
 }
}


现在来分析一下这段程序:

time_10ms,time_sec,time_min,这三个变量分别用来存放数码管需要显示的数值,首先是time_10ms,从0开始每隔10ms加一次,当数值等于100时就要进位,这时候让time_sec加一次并把time_10ms清零,同样的,当time_sec等于60时就要进位,time_min加一次并把time_sec清零。让这三个数值分别显示到对应的位置就行啦,好的,秒表最基础的计时部分分析完了,接下来就是按键啦。

我们看程序中60~95行,这是独立按键的程序,其中变量 “start” 是计时开始与暂停的标志位,当 “start == 1” 时,开始计时,“start == 0” 是暂停计时,我们通过按键S4、S5(P33与P32)来改变这个标志位的值就可以通过这两个按键来控制开始与暂停啦,当按下按键S6(P31)时,把time_10mstime_sectime_min全部清零并暂停计时,这样按键逻辑也就写好了,接下来是数码管的部分。

程序中37~59行是数码管的程序,因为是动态数码管,我们就需要让它在程序中不停地扫描,这样写的好处是每执行一次函数只会扫描一位数码管,这样的话8个数码管扫描的时间都是相同的,不管是在主函数中循环扫描还是在定时器中断中扫描,都不会出现数字亮度不相同的问题了。关键字 “static” 是静态的意思,使用了这个关键字定义的函数局部变量即使程序执行完这个函数,存放的数值也不会清零,第40行 “P0 = 0x00;” 的作用是清除段选码,56行延时2ms的作用是消除数码管余辉,在这里还起到了计时的作用。


好的现在把以上内容整合到一起就变成了我们现在的程序,值得注意的是:按键程序中松手检测的部分写成了 “while(P32 == 0)  smg_scan();” ,而不单单是 while(P32 == 0) ,因为数码管是需要程序不断扫描的,如果只是用了一个空循环来等待按键释放的话,数码管在这时将不能被扫描,也就不能正常显示。程序57、58行,当 “start == 1” 时,每隔2ms让“time”变量加一,当等于5时就代表计时满了10ms。


好的,现在来看下一个练习:

2.使用矩阵按键,当按下不同按键的时候数码管任意一位显示按下的键值(S4~S19 分别用 0~f 表示) 


#include "stc15f2k60s2.h"
#define u8 unsigned char
#define u16 unsigned int
u8 KeyValue = 0;
u8 code smgdu[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};
void HC138(u8 i)
{
 switch(i)
 {
  case 4: P2 = (P2 & 0x1f) | 0x80; break;
  case 5: P2 = (P2 & 0x1f) | 0xa0; break;
  case 6: P2 = (P2 & 0x1f) | 0xc0; break;
  case 7: P2 = (P2 & 0x1f) | 0xe0; break;
 }
}
void delayms(u16 z)  
{  
 u16 x,y;  
 for(x=z;x>0;x--)  
  for(y=845;y>0;y--);  
}  
void init_P0(void)
{
 HC138(4);
 P0 = 0xff;
 HC138(5);
 P0 = 0x00;
 HC138(6);
 P0 = 0xff;
 HC138(7);
 P0 = 0xff;
}
void key_scan()
{
 P44 = 0;
 P42 = P35 = P34 = 1;
 P33 = P32 = P31 = P30 = 1;
 if(P33 == 0)
 {
  KeyValue = 0;
  HC138(7); P0 = smgdu[KeyValue];
 }
 else if(P32 == 0)
 {
  KeyValue = 1;
  HC138(7); P0 = smgdu[KeyValue];
 }
 else if(P31 == 0)
 {
  KeyValue = 2;
  HC138(7); P0 = smgdu[KeyValue];
 }
 else if(P30 == 0)
 {
  KeyValue = 3;
  HC138(7); P0 = smgdu[KeyValue];
 }
 P42 = 0;
 P44 = P35 = P34 = 1;
 P33 = P32 = P31 = P30 = 1;
 if(P33 == 0)
 {
  while(P33 == 0);
  KeyValue = 4;
  HC138(7); P0 = smgdu[KeyValue];
 }
 else if(P32 == 0)
 {
  while(P32 == 0);
  KeyValue = 5;
  HC138(7); P0 = smgdu[KeyValue];
 }
 else if(P31 == 0)
 {
  while(P31 == 0);
  KeyValue = 6;
  HC138(7); P0 = smgdu[KeyValue];
 }
 else if(P30 == 0)
 {
  while(P30 == 0);
  KeyValue = 7;
  HC138(7); P0 = smgdu[KeyValue];
 }
 
 P35 = 0;
 P42 = P44 = P34 = 1;
 P33 = P32 = P31 = P30 = 1;
 if(P33 == 0)
 {
  while(P33 == 0);
  KeyValue = 8;
  HC138(7); P0 = smgdu[KeyValue];
 }
 else if(P32 == 0)
 {
  while(P32 == 0);
  KeyValue = 9;
  HC138(7); P0 = smgdu[KeyValue];
 }
 else if(P31 == 0)
 {
  while(P31 == 0);
  KeyValue = 10;
  HC138(7); P0 = smgdu[KeyValue];
 }
 else if(P30 == 0)
 {
  while(P30 == 0);
  KeyValue = 11;
  HC138(7); P0 = smgdu[KeyValue];
 }
 P34 = 0;
 P44 = P35 = P42 = 1;
 P33 = P32 = P31 = P30 = 1;
 if(P33 == 0)
 {
  while(P33 == 0);
  KeyValue = 12;
  HC138(7); P0 = smgdu[KeyValue];
 }
 else if(P32 == 0)
 {
  while(P32 == 0);
  KeyValue = 13;
  HC138(7); P0 = smgdu[KeyValue];
 }
 else if(P31 == 0)
 {
  while(P31 == 0);
  KeyValue = 14;
  HC138(7); P0 = smgdu[KeyValue];
 }
 else if(P30 == 0)
 {
  while(P30 == 0);
  KeyValue = 15;
  HC138(7); P0 = smgdu[KeyValue];
 }
}
void main()
{
 init_P0();
 HC138(6);
 P0 = 0x80;
 HC138(7);
 P0 = 0xbf;
 while(1)
 {
  key_scan();
 }
}


不难看出来,这里矩阵按键编写的方式跟昨天讲的不太一样,这就是昨天说的另外一种方法:逐列扫描,扫描的时候就直接给对应的按键赋好键值并通过数码管直接显示出来,因为这里使的是静态数码管,我们就不需要在程序中不断的扫描数码管了,只需要在数值改变的时候扫描一下就行了,这题相较于第一题来说是非常简单的,就不做太多分析啦。


对比一下前两个程序不难发现,昨天讲到的按键消抖、松手检测在第一个程序中都使用了,但是在第二个程序中并没有按键消抖的程序,这里就要给大家具体再讲解一下它们的作用了:

按键消抖:大家可以先去再看一下昨天内容中的波形图,不难发现,在实际中,按键按下时电平会抖动一段时间,明明只按了一次按键,但因为这样高低交错的电平就很容易会误判断为我们按下了很多次,但是在我们这两个程序里,一个按键只有一个作用,所以说不管是按一次还是很多次,效果都是一样的,所以就可以忽略不写,当然,第一个程序不写也是可以的。

松手检测:不加松手检测的时候,程序就不会停在这里,这时候即使我们没有松开按键,程序也会不断的执行按键扫描函数,每次执行都会判断到按下了按键,所以,这段程序就会不停的被执行,同样的,在这两个程序里边,一个按键只有一个作用,所以加上与不加都是可以的。

那么。。。问题来了,我们时候是必不可少的呢?在一个按键有不同功能的时候。我举个栗子:S4在正常计时的时候按下会暂停,而在暂停的时候按下又会正常计时。(你品,你细品)这里要提醒大家:如果没把握就把这两个都加上,有的话肯定不会出问题,但是没有就有可能会出问题


好的,通过学习这两个练习,大家应该对之前的内容都能够熟练掌握了,那么但现在为止,我们的基础部分差不多也就讲完了,在接下来的更新我们将进入中级部分(外部中断、定时计数器、串口通信等内容)的学习,有没有一点小激动呢?


今天的内容就到这里啦,如果有什么问题,欢迎回帖提出来,谢谢大家的支持,预祝大家能取得好成绩!




工程师
2020-01-15 16:06:03     打赏
2楼

明明全程用的都是同一大小的字,为什么发出来会有大有小呢?


高工
2020-01-16 13:27:50     打赏
3楼

写的很好


工程师
2020-01-16 13:44:39     打赏
4楼

希望楼主继续更新呀 我三月份就省赛了 哭唧唧


共4条 1/1 1 跳转至

回复

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