初学51单片机定时器数码管及C语言实践

笔者最近刚学到51单片机定时器部分(同样是金沙滩宋老师的板子),先分享下C语言这方面遇到的教训。如图

由于书写不规范,这个数组导致报了一堆的错,这个错误都牵连的别的部分。

可以看到很多变量和特殊寄存器都报错。变量提示redefinition,特殊寄存器提示多次初始化,还有提醒缺少分号的,五花八门。我第一眼看到这个问题以为错了很多地方,最后搞了很久发现是数组少了一个逗号,真是一个逗号引发的惨案。

逗号加上去以后什么问题都没了,Keil这个软件还是不够智能。我用vs2019试过,数组里的值不加逗号,它会报错,相应的值那边会提醒。

接着是第二处错误

这个unsigned char cnt = 0 ;这个定义变量不能放到变量赋值后面否则它会报错,它好像必须放在main()函数最上面。

第三次错误。

分号用了中文输入法的分号,导致报错。

我用的单片机是STC89C52RC,两个定时器 定时器0和定时器1。

定时器的计数值存储在2个字节(由高8位THn和低8位TLn组成)的储存寄存器中,这些储存寄存器可以描述为

控制上述储存寄存器工作需要 TCON定时器控制寄存器的参与。

该控制寄存器大小为一个字节,可赋值控制定时器0和1,看图所知。

第7、6、3、2位控制定时器1                             第5、4、1、0位控制定时器0

它的基础用法:当TR1置1即 赋值    TR1 = 1; 时定时器1就开始计数     

这里TR0 TR1 TF0 TF1是可以直接位寻址的即可以在程序中直接写TR0 = 1; 后面的TMOD是没法直接位寻值的,需要赋值的即要写成TMOD = 0x01(0000 0001)定时器0工作在模式1

TMOD = 0x10;定时器1工作在模式1,TMOD = 0x11;定时器0和1都工作在模式1

同样的                                          当TR0 = 1; 定时器0就开始计数

51单片机标准框架下每12个时钟周期即1个机器周期。每过1个机器周期定时器的值就会自动加1

如上述如果过了一个机器周期则:    TL0 值为 0x01  TH0 值为0x00

                                                           TL1值为0x01   TH1值为0x00 

低8位的存储寄存器最多能表达的值为255 当过了256个机器周期的时候 低8位储存寄存器溢出会置0,高8位存储寄存器会进1。 即此时对于定时器0 的值   

TH0 = 0x01 TL0 =0x00 。如此整个16位寄存器刚好溢出的值为256x256=65536

从十进制的算法256x255高8位充满,低8位充满时需要的值是255,则16位能储存的值为256x255+255=65535

从二进制的角度16位1111 1111 1111 1111=65535再进一位即65536刚好溢出。

当存储寄存器溢出以后TFn会硬件置1,我们可以通过软件置0的方式来控制定时器的延时时间。

定时器工作模式寄存器有4种工作模式。

以下是工作模式1的逻辑图。


0sc是时钟频率,我这个板子时钟频率是11.0592M。则时钟周期为 1/11059200秒 机器周期为

12/11059200。整个存储寄存器最多能储存65535个机器周期的时间即为65535*12/11059200=0.07111S 也就是71ms左右 

   ÷d*这个具体什么意思不清楚这里d值为12,即12个时钟周期为一机器周期。

CT=0的时候 工作模式为定时器                    CT =1时工作模式为计数器

与门输出位值为1的时候时候 控制开关才导通 整个定时器才开始工作

与门输出为1,则需要或门的输出与 TRn都为1。INTn输出1或者模式寄存器的gate(Tn)输出1

总结当我们需要使用定时器的时候需要:

1:设置模式寄存器TMOD的值确定工作模式

2:设置计数存储寄存器的初值THn TLn,设置溢出时间

3:设置控制寄存器TCON的值,通过使TRn为1使寄存器开始工作

4:判断TCON TFn的值监测定时器的溢出情况。

初识数码管

我这个板子数码管是共阳的

com为公共端,如果数码管是共阳,com端要接高电平。

数码管每段管子就是1个LED,若想该段的LED点亮相应该段的另一端就需要接低电平。a,b,c...这些接口都需要接低电平。可以看到数码管上LED的阴极输出通过总线是和单片机P0端口相连的。

以下是该板子数码管的真值表不带点。

如下是数码管带右下角点的真值表。

数码管真值的计算:

数码管的倒计时显示是课后习题   看程序

TMOD = 0x01;  //计时器0工作在模式1    
       TH0 = 0xB8;   //计时器0高8位赋值       
       TL0 = 0x00;   //计时器0低8位赋值
       TR0 = 1;      //置1计时器0开始计时

 while(1)
       {
               if(TF0 == 1)  //溢出时才进入该语句
              {
                  TF0 = 0;      // 软件置0
                  TH0 = 0xB8;   // 定时器高8位置0xB8 即 1011 1000 
                   TL0 = 0x00;   // 定时器低8位置0x00 即 0000 0000 高8位低8位合成16位  1011 1000                                             //   0000 0000 为0xb800 47104
                                         // 定时器设置的溢出时间为 (65535+1)-47104=18432                                                                         // 18432*12/11059200=0.02秒  
                    cnt++;           // 每经过1次软件置0 cnt加1
                if (cnt >= 50)
                {
                    cnt = 0;                 // 经过50次延迟 即过了1秒 cnt置1重新延迟
                P0 = LedChar[sec];        // 当前秒数对应的真值表中的值送到P0口
                    sec--;                     //   LedChar 数组里倒序依次显示
                
                       if(sec == 0)   
                      {
                          continue;  // 当sec为0时已经到了数组的第1个值返回while开头显示数组第
                      }                        1个值
                       if(sec == -1)   数组第一个值已经显示完毕
                        {
                           sec = 15;   sec 回到数组最后一位
                        }
                 }
              }
       }
}    
    结果视频            数码管倒计时_哔哩哔哩_bilibili    

上篇博客我用for循环延时了LED流水灯,这次我用定时器更精确的延时 看程序

#include<reg52.h>

sbit ADDR0 = P1^0;          
sbit ADDR1 = P1^1;           
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;

main()
{
  unsigned int i = 0;                   
  unsigned int shift1 = 0x01; //(LED1)shift1在最左边端口1那么一开始会左移
  unsigned int shift2 = 0x80;// (LED2)shift2在最右边端口8那么一开始会右移
  unsigned char dir1 = 0;   //    dir1是0的时候shift1左移,shift2右移,两者是镜像移动的       dir1若为1则相反
    unsigned char j = 0 ;     //    设置一个变量j
    unsigned char cnt = 0;   //    定时器0设置的溢出次数计数
    
  ENLED = 0;           //38译码器使能
  ADDR3 = 1;           //38译码器使能
  ADDR2 = 1;           //使LEDS6从高电平变低电平   38译码器真值表Y6输出低电平
  ADDR1 = 1;           //使LEDS6从高电平变低电平
  ADDR0 = 0;           //使LEDS6从高电平变低电平
    
   TMOD = 0x01;     //定时器0工作在1模式 0000 0001
   TL0 = 0x00;        //定时器0 低8为赋值为0x00
   TH0 = 0xB8;      //定时器0 高8位赋值为0xB8  计时器0 16位 1111 1111 1111 1111
                            // 它的值是0-65535也就是说当65536计时器溢出    
     TR0 = 1;

    
  while(1)
  {                             
        P0 = ~(shift1+shift2); //P0等于循环变量取反     

           while(TF0 == 0); //当TF0为0 时程序一直执行空循环直到TF0=1
           TF0 = 0;             //执行到行说明while()循环已跳出TF0=1了如此T0溢出TF0需要重新置0
           TH0 = 0xB8;      // 重新赋值
           TL0 = 0x00;
           cnt++;
             if(cnt >= 10)     // cnt加10也就是延时0.02s*10 =0.2s
            {
             j = cnt;           //延时10次后赋值给j
             cnt = 0;         //cnt置0从新开始
            }
                       
        while(j == 10)     //只有j为10的时候才进入该while()函数
            {
        
            j = 0;         // 进入函数后j值要立刻置0防止进入循环

                
         if(shift1 == 0x08 && dir1 == 0)    //判断是否 shit1运动到端口4并且是向左移动
        {
          shift1 = shift1<<1;    //shift1在端口4 0x08的位置往左移一位
          shift2 = shift2>>1;    //shift2在端口5    0x10的位置往右移一位,
          }                        //  因为是相对运动的因此判断了shift1的方向shift2 的方向就确认了
        if(shift1 == 0x10 && dir1 == 1)    //判断是否 shift1运动到端口5并且是向右运动
            {
          shift1 = shift1>>1;    // shift1在端口5 0x10的位置并且向右移动一位
          shift2 = shift2<<1;    // shift2在端口4 0x08的位置往左移一位
        }    
       if(dir1 == 0 )             / /判断是否 shift1往左移动
            {
              {shift1 = shift1<<1; //循环变量1左移1位
               shift2 = shift2>>1; //循环变量2右移1位
              }                                   
               if(shift1 ==0x80 && shift2 == 0x01)    //循环变量都移到最左端和最右端
                {
                   dir1 = 1;         //换向
                   break ; 跳出该while循环回到上一级的while循环,上篇博客这里是continue
                  }
              } 

        if(dir1 == 1)  // 判断是否 shift1往右移动
          {
              {
               shift1 = shift1>>1 ;    //换向后循环变量1右移
               shift2 = shift2<<1 ;    //换向后循环变量2左移
            }
             if(shift1 == 0x01 && shift2 == 0x80)  //判断循环变量1到最右端且循环变量2到最 左端
              {
              dir1 = 0;       //又开始换向换回到原先的方向
               }
             }
           }
          }
        }
   这个程序除了延时不同之外,还有其他不同的地方我用红字标出来了。
   
   
  

视频结果定时器延时的流水灯_哔哩哔哩_bilibili

可以看出两个程序用了不同的语句while和if语句来控制定时器的延时。

在提供一种数码管倒计时的写法是宋老师的程序

 

结语:所以单片机的定时器到底是什么东西?学到这突然觉得所谓定时器,就是这些相关控制寄存器一起作用时的组成的功能叫做定时器,就好比PCB由板材和元器件构成,定时器是由各个不同的特殊功能寄存器组合一起的统一称谓。

写了好多,又好像没写啥东西。宋老师的单片机视频资料都可以上B站自行下载。

  • 29
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值