笔者最近刚学到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; //又开始换向换回到原先的方向
}
}
}
}
}
这个程序除了延时不同之外,还有其他不同的地方我用红字标出来了。
可以看出两个程序用了不同的语句while和if语句来控制定时器的延时。
在提供一种数码管倒计时的写法是宋老师的程序
结语:所以单片机的定时器到底是什么东西?学到这突然觉得所谓定时器,就是这些相关控制寄存器一起作用时的组成的功能叫做定时器,就好比PCB由板材和元器件构成,定时器是由各个不同的特殊功能寄存器组合一起的统一称谓。
写了好多,又好像没写啥东西。宋老师的单片机视频资料都可以上B站自行下载。