51单片机学习记录(11)“定时中断”
用定时/计数器0让led 1s 闪烁一次
先前使用延时函数并不能精确的控制灯的亮灭,但是现在利用定时/计数器就可以达成精确定时
首先了解下CPU时序的有关知识
- 振荡周期:为单片机提供定时信号的振荡源(晶振或外部提供)的周期,又称时钟周期
- 状态周期: 2个振荡周期 = 1个状态周期
- 机器周期: 1个机器周期里含6个状态周期,12个振荡周期
- 指令周期: 完成一条指令所占用的全部时间,以机器周期为单位
例: 外接晶振为12MHz时,51单片机的具体周期值为
振荡周期 = 1/f = 1/12 us
状态周期 = 2* 1/12 = 1/6 us
机器周期 = 12 * 1/12 = 1 us
指令周期 = 1~4 us (通常)
定时/计数器的控制
定时/计数器是由两TCON和TMOD这寄存器控制的,TMOD用于设置其工作方式, TCON用于启动和中断申请
这两个先前的文章已有写到,可以返回查看具体的功能
https://blog.csdn.net/weixin_45931009/article/details/107886429
如何使定时/计数器
初始化工作
- 对TMOD赋值,确定T0和T1的工作方式
- 计算初值,并将其写入TH0,TL0或TH1,TL1
- 若为中断方式,则对EA赋值,开放定时器中断
- 使TR0或TR1置位,启动定时/计数器定时或计数
计数器初值的计算
计数初值与计算个数的关系: X = 2^16 - N,其中X是计数初值,16指工作时的计数位数是16位的,N是计数的个数
假设定时1ms 则初值应为(你要定时的时间除以机器周期) 1ms/1us = 1000(单片机的机器周期每1us是一个周期) 也就是需要计数1000个数
则X = 65536-1000 = 64536 = FC18H
当然也可以通过软件直接进行计算
补充: 定时/计数器的实质是加1计数器(16位)由高8位的低8位的两个寄存器THx的TLx组成,当我们计算完初值后,如上FC18H
则编写程序时就应这样赋值 THx = 0xFC , TLx = 0x18
或是THx = (65536-1000)/256; TLx = (65536-1000)%256;
为什么是这样呢,/256是为了获取(65536-1000)的高8位,%256则是为了获取低8位,至于为什么是256因为要取8位,换成二进制就是256
程序
#include <reg51.h>
typedef unsigned char u8;
typedef unsigned int u16;
sbit led = P2^0;
void Time0Init(); //声明定时/计数器0的初始化函数
void main()
{
Time0Init();
while(1);
}
void Time0Init()
{
TMOD |= 0x01; //选择定时器0,方式1,16位定时/计数器
// 这里不直接赋值0x01,而是用或运算是为了保持最低位为1,其它位不变
/*定时1ms*/
TH0 = 0xFC; //或TH0=(65536-1000)/256;
TL0 = 0x18; //或TL0=(65536-1000)%256;
EA = 1; //打开总中断
ET0 = 1; //打开定时/计数器0中断
TR0 = 1; //启动定时/计数器0
}
void Time0Start() interrupt 1
{
static u16 count = 0; //定义一个计数变量
// 因为选择的方式非自动重装,所以编写服务函数的时候需要重新写入
TH0 = 0xFC;
TL0 = 0x18;
count++;
countf(count == 1000) //当计数到1000时刚好达到1s
{
count = 0; //为了下次使用,清零处理
led = ~led; //让led灯闪烁
}
}