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

共5条 1/1 1 跳转至

第十一届蓝桥杯信息技术人才大赛-单片机竞赛备赛分享八——定时器实战+计数器基础知识讲解

工程师
2020-01-26 21:14:23     打赏

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


上节课呢我们讲了一下定时器的基础知识,在单片机中定时器是特别重要的一个部分,特别是和中断结合起来使用的时候,功能还是很强大的,在编程的时候,定时器总会是一个特别重要的资源,由于51单片机的定时比较少,于是在比赛的时候一定要注意一下这些资源的分配,这节呢主要给大家讲一下上次留下来的小练习以及定时/计数器的另一个功能:计数功能,好的,话不多说,直接开始我们今天的内容:


我们先来看一下第一个练习:

使用定时器控制数码管任意一位显示数值0~9(每隔1秒数值加1)


#include "stc15f2k60s2.h"
#define u8 unsigned char
#define u16 unsigned int
u8 code smg_du[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
u8 numb = 0;
u16 time = 0;
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 init_P0(void)        
{  
 HC138(4);            
 P0 = 0xff;
 HC138(5);        
 P0 = 0x00;       
 HC138(6);          
 P0 = 0xff;    
 HC138(7);             
 P0 = 0xff;           
}
void time0_init()
{
 EA = 1; ET0 = 1;
 TH0 = 0xf8; TL0 = 0xcc;
 TMOD = 0x01; TR0 = 1;
} 
void main()
{
 init_P0();
 time0_init();
 HC138(6);
 P0 = 0x80;
 while(1);
}
void time0() interrupt 1
{
 TH0 = 0xf8; TL0 = 0xcc;
 time++;
 HC138(7);
 P0 = smg_du[numb];
 if(time == 500)
 {
  time = 0;
  numb++;
 }
 if(numb == 10)
  numb = 0;
}


这里使用的是定时器0,我们把关于定时器0的配置写成了一个函数在主函数中调用一下,这里的定时器设置的时间为2ms,所以我们要在开头定义一个变量做标志位来记录时间,每进一次中断数值加1,当它等于500时就代表已经到了1s的时间,再定义另外一个变量用来给数码管显示,每到1s的时候这个变量的数值加1,等于10的时候清零,数码管段选码的程序也放在了定时器中断中,每隔2ms会扫描一次,所以在主函数中只需要调用一下初始化函数就足够了。


这个程序还是比较简单的,我就不说太多啦,我们来看第二个练习:

把我们讲机械按键时写的那个用延时函数实现的秒表程序改为定时器计时


#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 time0_init()
{
 EA = 1; ET0 = 1;
 TH0 = 0xf8; TL0 = 0xcc;
 TMOD = 0x01; TR0 = 1;
} 
 
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;
}
void key_scan()
{
 if(P33 == 0)
 {
  delayms(10);
  if(P33 == 0)
  {
   start = 1;
   while(P33 == 0);
  }
 }
 if(P32 == 0)
 {
  delayms(10);
  if(P32 == 0)
  {
   start = 0;
   while(P32 == 0);
  }
 }
 if(P31 == 0)
 {
  delayms(10);
  if(P31 == 0)
  {
   time_10ms = 0;
   time_sec = 0;
   time_min = 0;
   start = 0;
   while(P31 == 0);
  }
 }
}
void main()
{
 init_P0();
 time0_init();
 while(1)
 {
  key_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;  }
 }
}
void time_0() interrupt 1
{
 TH0 = 0xf8; TL0 = 0xcc;
 smg_scan();
 if(start == 1)
  time++; 
}


这个程序与上次用延时函数写的类似,直接复制上一个程序来修改就可以了,这里我们把计时的方法从延时函数改成了定时器,而且直接把数码管函数放到了定时器中断中来扫描,每隔2ms扫描一次数码管就可以正常显示了,这时候在主函数还有按键的松手检测中就不需要再扫描数码管了,因为即使是程序停在了那个地方,依然可以通过中断来扫描数码管,还是我说的,千万别在这个中断中加延时哦。


好的,现在开始讲解下一节内容,定时/计数器的另外一个功能:计数功能

定时/计数器工作在计数模式时,是接受外部脉冲输入来计数,对应的引脚没接收到一个高低电平,16位的计数寄存器就会加1,在配置的时候与定时器是相同的,只不过计数器不需要打开对应的中断,只需要配置好相应的寄存器就可以了

相同的,TCON寄存器中需要将TR位配置位高电平,TMOD寄存器也是相同的,只需要将C/T位由0改为1,计数模式与定时模式相同的,也配置为模式1,因为计数一般情况下我们都是从0开始计,因此,初值也配置为0,所以说计数器初始化时只需要编写以下程序就好(以定时/计数器0为例): TH0 = 0;  TL0 = 0;  TMOD = 0x05;  TR0 = 1;

还记得我将中断的时候说的P3端口的第二功能吧?对的,P3^4引脚就是定时/计数器0的外部输入引脚,只要引脚接收到脉冲,计数器就可以正常计数了。


好的,我在举个栗子:通过LED(假设为P1端口)灯亮灭来产生脉冲输入到P3^4引脚来计数。

#include “reg52.h”

void count0_init()
{
    TH0 = 0;  TL0 = 0;
    TMOD = 0x05;  TR0 = 1;
}    

void delay(unsigned int n)
{
    unsigned char j;
    while(n--)
        for(j=113;j>0;j--);
}

void main()
{
    count0_init();
    while(1)
    {
        P1 = 0x00;
        delay(500);
        P1 = 0xff;
        delay(500);
    }
}


这时候要把P1端口的任意一个引脚用杜邦线跟P3^4连接在一起。


这里其实不难发现,单片机中,脉冲计数与时间之间的关系十分密切,每输入一个脉冲,计数器的值就会自动累加1,只要相邻两个计数脉冲之间的时间间隔是一样的,那么计数值就代表了时间,因此,单片机中的定时器和计数器其实是同一个物理的电子元件,只不过计数器记录的是单片机外部脉冲,而定时器是由单片机自身提供的一个计数器,这个计数器就是单片机上连接的晶振部件;单片机的晶振经过分频之后提供给单片机稳定的脉冲;晶振的频率是非常准确的,因此,单片机的计数脉冲之间的时间间隔也会非常准确。


好的,今天的内容到这里就结束啦,在留下一个小练习给大家:

通过按键使任意一位引脚产生脉冲输入到P3^4引脚来计数,数码管显示按键按下的次数。要求:按下一次按键只能稳定的计一位数。



好的,今天的内容就先到这里啦,预祝大家能取得好成绩,还有就是,在座的各位,都要好好的,不许被感染!!!






工程师
2020-01-26 21:17:53     打赏
2楼

啊!!为什么又是这样,字有大有小的


院士
2020-01-27 15:33:09     打赏
3楼

定时器的应用还是相当有趣的,可简单,可复杂。

简单的就如楼主帖子里面所说,可以用来脉冲计数;

复杂的可以用来做任务调度,实现多线程处理。


楼主很棒,学而时习之



高工
2020-01-31 20:08:37     打赏
4楼

小飞小飞加油哇~~~


菜鸟
2020-02-02 19:35:43     打赏
5楼

学习一下


共5条 1/1 1 跳转至

回复

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