什么是定时器/计数器? --- 既可以定时,也能计数的一种单片机内部资源,主要用于软件计时和软件延时等场合。但同一时刻只能使用其中一种功能。
定时器的核心功能简单一句话说就是“启动定时器后,每个机器周期到来,初值寄存器自动加1,直到计满溢出”。
定时器的工作流程:
1.启动信号
2.预设机器周期--以12MHZ为例,单片机时钟信号÷12=1MHZ,等于一个机器周期是1us
3.按预设初值寄存器来启动计算; TH0=高八位,TL0=低八位;初值都为00H;
4.满足条件自动加1;TL0低位开始+1,+至1111 1111(255); 从高位继续+1,+至1111 1111 1111 1111;继续+1则溢出;
5.溢出;(65535)再+1变成1 0000 0000 0000 0000; 此时溢出,定时结束
那么如果要定时50ms,该如何实现呢?
首先要进行初值计算:
定时时长:65536us,计算机周期个数:65536个;
定时时长:50000us,计算机周期个数:50000个;
计算方法为:初值X=65536-50000=15536;
15536换算为16进制为3Cb0H;
即初值为:TH0=0x3c; TL0=0xb0;
TH0存放的是256取整;TL0存放的是小于256的余数;
所以可以写成:TH0=(65535-50000)/256;
TL0=(65535-50000)%256;
以上就解决了初值计算的问题,假设需要定时大于65536的时间,比如1秒,则需要再程序内部加循环,1s=50ms×20次;或者10ms×100次;
定时器的编程实现步骤:
1.报备---2.置初值---3.启动---4.等待---5.重置初值---6.清溢出
1.报备
TMOD (89H) | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
工作模式寄存器 | GATE | C/T | M1 | M0 | GATE | C/T | M1 | M0 |
T1字段 | T0字段 |
GATE:门控位,为0时,将TR0=1,则启动定时器。 为1时,TR0=1还需要加上外部中断引脚INT0共同启动。
C/T:0定时;1计数;
M1/M0(工作方式):00:方式0; 01:方式1; 10:方式2; 11:方式3; 最后得到TMOD的值:0000 0001;0x01;
2.置初值
50ms为例: TH0=0x3c; TL0=0xb0;
3.启动
TR0=1; 启动
TR0=0; 停止
TCON(88H) | TF1 | TR1 | TF0 | TR0 | IE1 | IT1 | IE0 | IT0 |
4.等待
TF0溢出位,溢出自动置1;
为了保证下一次溢出时能够正常置1;需要将TF0手动清0;TF0=0;
while(TF0==0); 查询TF0是否为1,来判断溢出;为1就是时间到了,溢出了;
5.重置初值
如果定时1s就需要50ms×20次,在溢出后要重置初值并循环20次
6.清溢出
溢出后TF0=1;为了保证下次溢出正常计数,需要手动置0:TF0=0;
中断的基本知识
中断源 | 中断允许标志位 | 中断请求形式 | 中断请求标志位 | 中断号 |
外部中断INT0 | EX0 | 低电平 | IE0 | 0 |
定时器T0 | ET0 | 溢出 | TF0 | 1 |
外部中断INT1 | EX1 | 低电平 | IE1 | 2 |
定时器T1 | ET1 | 溢出 | TF1 | 3 |
串口 | ES | 接收或发送结束 | TX/RX | 4 |
IE寄存器 中断允许寄存器 | |||||||
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
EA | X | X | ES | ET1 | EX1 | ET0 | EX0 |
EA:总中断允许标志位
中断流程:
中断源---中断允许---中断请求---中断响应---断点---中断号---中断返回
中断服务程序示例:
timer0:函数名字可以随便取; interrupt:中断关键字; 1:中断号;
void timer0 () interrupt 1
{
}
中断演示--定时50ms,循环2次,即100ms,LED灯循环左移一次
代码分为两部分:1.主函数部分;2.中断服务程序部分
uchar k = 0; //由于K将要在中断服务函数中调用,所以K需要定义为全局变量
void main()
{
uchar n = 0; i = 0;
TMOD=0x01; //工作模式
TH0=(65536-50000)/256; //定时器置初值
TL0=(65536-50000)%256; //定时器置初值
EA=1; //中断总开关
ET0=1; //中断允许标志位
TR0=1; //启动计时器
while(1)
{
n=0x01;
for(i=0;i<8;i++) //循环8次
{
P0=~n; //进入第一次循环,并点亮第一颗灯
while(k<2); //等待,等待时间到来,然后进入中断
k=0; //中断服务程序中的内容执行完以后,K置0重新计数;
n=n<<1; //LED灯左移一位
}
}
}
中断服务程序
//定时器T0中断服务程序
void timer0 () interrupt 1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
K++;
}