说明
本人使用的是清翔的51单片机开发板,如果型号相同最方便,但是如果型号不同也可以参考,因为芯片都是一样的,只是外设不同而已,使用时只需要对照自己的开发板原理图稍微修改下引脚即可。
本次笔记将对应视频教程的第28,29,30,31集 定时计数器的定时和计数(理论+实践)
如果笔记之中有任何错误,请在评论区指出,谢谢
目录
4.1 main.c 以16位定时器为例 (delay延时)
一、什么是定时/计数器
这里仅认为我用的STC89C52RC有2个16位定时/计数器(为什么“仅”,见)清翔零基础教你学51单片机_个人学习笔记(9)_中断系统和外部中断四、51中断源 部分。
定时器和计数器从本质上来说都是计数器,如果计数系统内部脉冲,那么就是定时器,如果计数外部脉冲,那么就是计数器。(T0为P3.4引脚,T1为P3.5引脚),每计数一个脉冲,值+1。
定时器作用:定时计数器可以用于精确事件定时,PWM脉宽调制,波形发生,信号时序测量的方面。
二、定时/计数器的使用
2.1 步骤
- 启动定时/计数器(通过TCON寄存器控制,上次笔记截图里面也有)
- 设置定时/计数器工作模式(TMOD寄存器)
- 查询定时/计数器是否溢出(读TCON内TF位)
模式1和模式2用的较多
2.2 定时器/计数器0工作模式
2.2.1 模式0
速率调节
2.2.2 模式1
16位,其余和模式0相同
2.2.3 模式2
TL0是真正用作计数的,TH0用于存放想要重装的值,每次TL0溢出,就会自动把TH0的值放入TL0,并且TH0的值不变。
2.2.4 模式3
2.3 定时器/计数器1工作模式
除模式3下停止计数外,其余和计数器0相同
三、 定时器0的使用示范 定时器
3.1 创建工程
复制上一份工程文件夹,修改名称为“10.定时计数器”,进入项目文件夹,打开工程文件,删除main.c函数的内容。
3.2 main.c 以16位定时器为例
给定时器赋初值,从给的值开始向上计数,从而设定要计数的时间长度。假设要计数50ms,也就是50 000us,机器周期是1.085us。50000/1.085=46,082.9≈46083,也就是说要计数这么多次才能到50ms,16位最大计数值是65535,那么就要从65535 - 46083=19452,十六进制就是4BFC,把4B赋值给TH0,把FC赋值给TL0
void main()
{
uchar temp;
uchar sec;
timer0init(0x01, 1, 0x4b, 0xfc); //设置T0为定时器,模式1(16位) 0000 0001
while(1)
{
if (TF0 == 1)
{
temp++;
if (temp == 20)
{
sec++;
if (sec == 10) //sec是uchar类型,最大值是255
sec = 0;
temp = 0;
}
TH0 = 0x4B; //重新放入初值
TL0 = 0xFC;
TF0 = 0; //软件清零溢出标志位
}
SEG_DIS3(sec);
}
}
void timer0init(uchar tmod, uchar tr0, uchar th0, uchar tl0)
{
TMOD = tmod;
TR0 = tr0; //可位寻址,是否允许T0开始计数
//给定时器赋初值,从给的值开始向上计数,从而设定要计数的时间长度
TH0 = th0;
TL0 = tl0;
}
SEG_DIS3函数之前有写过,是在数码管显示一个3位数,直接把之前的函数复制归来,并且在main.h写好函数声明就行了。
3.3 main.h
写好函数声明
void timer0init(uchar tmod, uchar tr0, uchar th0, uchar tl0);
3.4 现象说明
数码管最左边3个从000变到009,之后变成000,如此循环,每秒数字增加1
四、 定时器0使用示范 计数器
把P34和P10用杜邦线连起来
4.1 main.c 以16位定时器为例 (delay延时)
void main()
{
timer0init(0x05, 1, 0, 0);
while(1)
{
if (TL0 == 10)
TL0 = 0;
SEG_DIS(1,TL0);
LED1 = ~LED1;
delay(300);
}
}
0x05就是 0000 0101,表示设置为计数器模式,模式1
注意在main.h里面写sbit LED1 = P1^0;,否则会报错。
SEG_DIS()函数之前用过直接复制过来,注意写函数声明
不用SEG_DIS3()的原因,是因为要动态显示需要不停地扫描,但是这里用了delay(300),那么芯片就会去计算加减,无法扫描,数码管只会显示最后1位,前面两个只会在更新的时候闪一下
4.2 现象说明
LED1不停地闪烁,可以把LED1上方的跳线帽拔下来。数码管第1位从0~9不停的变化
4.3 用T1来延时,T0计数
main.c
void main(){
uchar temp;
timer1init(0x10, 1, 0x4b, 0xfc); //定时器1初始化为定时模式,设置初值计时50ms
timer0init(0x05, 1, 0, 0);
while (1)
{
if (TF1 == 1)
{
temp++;
if (temp == 2)
{
LED1 = ~LED1;
temp = 0;
}
TH1 = 0x4B; //T1重新放入初值
TL1 = 0xFC;
TF1 = 0; //T1软件清零溢出标志位
}
if(TH0 != 0)
{
TH0 = 0;
TL0 = 0;
}
SEG_DIS3(TL0);
}
}
void timer1init(uchar tmod, uchar tr1, uchar th1, uchar tl1)
{
TMOD |= tmod; //注意是“或等于”,只把想要的位置更改成1而不去动其他位置
TR1 = tr1; //可位寻址,是否允许T1开始计数
TH1 = th1; //设置初值
TL1 = tl1;
}
void timer0init(uchar tmod, uchar tr0, uchar th0, uchar tl0)
{
TMOD |= tmod; //注意是“或等于”,只把想要的位置更改成1而不去动其他位置
TR0 = tr0; //可位寻址,是否允许T0开始计数
//给定时器赋初值,从给的值开始向上计数,从而设定要计数的时间长度
TH0 = th0;
TL0 = tl0;
}
注意,定时/计数器初始化函数我把TMOD改成了或等于。
然后在main.h把函数声明写进去就可以了。
4.4 现象说明
数码管3个都可以显示,从000显示到255,再变成000循环。
五、定时器0使用示范 定时器中断
5.1 main.c
uchar temp;
uchar sec; //由于在中断函数也使用了这两个变量,所以定义为全局变量
void main() {
//定时器中断
//T0定时器模式 中断
timer0init(0x01, 1, 0x4b, 0xfc); //设置T0为定时器,模式1(16位) 0000 0001
while(1)
{
SEG_DIS3(sec);
}
}
void timer0() interrupt 1
{
temp++;
if (temp == 20)
{
sec++;
if (sec == 255) //sec是uchar类型,最大值是255
sec = 0;
temp = 0;
}
TH0 = 0x4B; //重新放入初值
TL0 = 0xFC;
}
void timer1init(uchar tmod, uchar tr1, uchar th1, uchar tl1)
{
EA = 1;
ET1 = 1;
TMOD |= tmod; //注意是“或等于”,只把想要的位置更改成1而不去动其他位置
TR1 = tr1; //可位寻址,是否允许T1开始计数
TH1 = th1; //设置初值
TL1 = tl1;
}
void timer0init(uchar tmod, uchar tr0, uchar th0, uchar tl0)
{
EA = 1; //打开总中断
ET0 = 1; //打开T0中断
TMOD |= tmod; //注意是“或等于”,只把想要的位置更改成1而不去动其他位置
TR0 = tr0; //可位寻址,是否允许T0开始计数
//给定时器赋初值,从给的值开始向上计数,从而设定要计数的时间长度
TH0 = th0;
TL0 = tl0;
}
注意在定时器初始化函数里面我把中断打开了
5.2 现象说明
数码管3位数从0~254循环显示1秒增加1
六、利用定时器中断进行数码管动态扫描
main.c
uchar temp;
void main()
{
timer0init(0x01, 1, 0xfc, 0x65); //设置T0为定时器,模式1(16位) 0000 0001
while(1)
{
key_scan();
if (keyval == 16)
temp++;
else if (keyval == 17 && temp > 0)
temp--;
keyval = 20;
}
}
void timer0() interrupt 1
{
SEG_DIS3(temp);
TH0 = 0xfc;
TL0 = 0x65;
}
这里我设置的定时时长为1ms,也可以自己计算改其他数值
while(1)也可以这样写
while(1)
{
if (key_s2 == 0)
{
delay(20);
if (key_s2 == 0)
temp++;
while(!key_s2);
}
else if (key_s3 == 0 && temp > 0)
{
delay(20);
if (key_s3 == 0)
temp--;
while(!key_s3);
}
}
不使用key_scan函数,直接判断对应按键是否按下,它的好处就是可以在按下之后数码管值立刻改变,而不必等到松手后才改变。
6.1 现象说明
按下s2,数码管3位数+1,按下s3,数码管数值-1,当数码管数值为0,s3无效
本次笔记将对应视频教程的第28,29,30,31集 定时计数器的定时和计数(理论+实践),到此结束,我感觉内容还是比较清晰的,关于定时计数器的模式0和模式3,视频教程中也并未详细说明,感兴趣的朋友可以根据知识点自行研究。至于8位自动重装模式也比较好理解,设置好要重装的数就行,其实如果用模式1,在初始化和中断里面再给他手动重装,差不多相当于一个16位手动重装定时器,可以用来代替模式2。
下一次笔记将对应视频教程的第32,33集 串口通信(理论+实践)
如果笔记之中有任何错误,请在评论区指出,谢谢