STC89C52单片机的定时器个数一共有3个(T0、T1、T2)
传统(英特尔)的 51 单片机的内部有 T0 和 T1 这两个定时器,T2 是此型号单片机增加的资源
对于单片机的每一个功能模块来说,都是由它的 SFR ,也就是 特殊寄存器 来控制的。
STC89C52的定时器T0和定时器T1均有4种工作模式
模式0→13位定时器/计数器。
★模式1→16位定时器/计数器,这个是最常用的一个模式。★模式2→8位自动重装模式,这个算是第二个最常用的一个模式。第二种模式主要用于串口通信产生波特率
模式3→两个8位计数器。
由于这里模式一是最常用的一个模式,那么接下来我们就来详细讲解下模式一的工作模式。
1.定时器是怎么计数的?
TL0 和 TH0,其实这个是一个十六位的一个计数器。在这个里面它拥有一个计数系统,高字节叫做 TH,低字节叫做 TL,0 代表的是定时器 0,这两个字节总共可以计数到 65535。
2.计数系统是如何工作的?
计数系统左边的时钟给了它提供了一个脉冲(方波),每来一个脉冲它的定时器的值就会加 1,一直循环往复。当加到 65535 的时候,就会产生一个溢出,溢出的时候这个计数器就会到原来设定的初值的一个位置(初值也就是设置的TH0和TL0)然后往后继续加。当它溢出的时候会掷出一个标志位。『TF0→标志位』,拥有了这个标志位它就会想中断系统进行申请中断,同时我们要在中断函数中重新设置初值TH0和TL0。
3.定时器加1经过了多长时间?
SYSclk⇢系统时钟,即晶振周期,本开发板使用的晶振是11.0592 MHz。
当晶振频率是11.0592MHz的时候,等于11059.2KHz = 11059200Hz
机器周期 = 12 x 时钟周期 =12 x (1/时钟频率) 秒 = 12 / 时钟频率 秒 = 12 / 11059200 秒 = 12 000 000 / 11059200 微秒 = 1.085 微秒(us)= 0.001085毫秒(ms)
4.如何算出10ms定时器的初值?
就不让他从0开始数数,10ms需要数9216下,你让他从65536-9126=56320(16进制表示为 0xDC00)开始数数 这样TL0=0x00;TH0=0xDC
52单片机中常用的中断有以下五个
interrupt0 表示 外部中断0
interrupt1 表示 定时器中断0
interrupt2 表示 外部中断1
interrupt3 表示 定时器中断1
interrupt4 表示 串口中断
下面的中断系统图很重要,后面写定时器和中断函数就可以参考它来写
TF0:当定时器溢出时,由硬件置1,并且向CPU请求中断,CPU响应中断后,由硬件清0;
TR0:TR0 = 1表示允许定时器T0开始计数,TR0 = 1表示禁止T0计数;
这里我们选择定时器T0最常用的模式 1 也就是 16位定时器/计数器
TCON的可位寻址 指可以直接给它特定的位赋值
TMOD的不可位寻址 指只能同时给它的8个位同时赋值
用法显而易见了,根据上面的中断系统图也能知道 任何中断都要配置这个寄存器。
最后开始写代码
sbit led = P3^6;
int cnt = 0;
void Time0Init()
{
//1. 配置定时器0工作模式位16位计时
TMOD = 0x01;
//2. 给初值,定一个10ms出来
TL0=0x00;
TH0=0xDC;
//3. 开始计时,定时器开始"数数"
TR0 = 1;
TF0 = 0;
//4. 打开定时器0中断
ET0 = 1;
//5. 打开总中断EA
EA = 1;
}
void main()
{
led = 1;
Time0Init();
while(1){
led1 = 0;
Delay300ms();
led1 = 1;
Delay300ms();
}
}
void Time0Handler() interrupt 1 //每10ms进来一次
{
cnt++; //统计爆表的次数
//重新给初值
TL0=0x00;
TH0=0xDC;
if(cnt == 100){//进来100次,经过了1s
cnt = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s
led = !led;//每经过1s,翻转led的状态
}
}|
定时器1的用法和定时器0基本一样,这里不多做说明。
52单片机中外部中断0和1是分别被放在P3^2和P3^3口的
在上面中断系统图中能看到外部中断可分为下降沿触发和低电平触发,理解起来很容易
例如使用外部中断0 :
下降沿触发就是 当CPU检测到P3^2口有一个高电平向低电平的跳动,就会触发一次外部中断。
低电平触发就是 当CPU检测到P3^2口为低电平,就会触发一次外部中断。
这里使用震动传感器来举例,把震动传感器的信号线接在P3^2口,用下面代码举例:
int mark = 0;
void EX0_init()
{
EX0 = 1;//开外部中断0
IT0=0;//设置为低电平触发
}
void main()
{
while(1)
{
if(mark == 1){
//报警
//mark = 0;
}
}
}
void EX0_handler() interrupt 0 //检测到P3^2口为低电平,就进来一次
{
mark = 1;
}
根据上面中断系统图知道,我们需要配置SCON寄存器
常用的是方式1
也要配置REN位,REN = 1允许串口接收数据,REN = 0禁止串口接收数据。
要选择方式1 同时想能接收数据,则SCON应配置为 0x50
当有数据发送过去,发完后,TI由硬件置1,必须用软件复位置0;
当有数据发送过来,发完后,RI由硬件置1,必须用软件复位置0;
在定时器T0和T1的四种工作模式中,串口通信常用的是模式2,即8位自动重装模式,主要用于串口通信产生波特率。则TMOD应配置为0x20;
大致使用下面示例代码用定时器T1的模式2来举例
void UartInit(void) //[email protected]
{
SCON = 0x50; //配置串口工作方式1,REN使能接收
TMOD = 0x20;//定时器1工作方式位8位自动重装
TH1 = 0xFD;//9600波特率的初值
TL1 = 0xFD;
TR1 = 1;//启动定时器
EA = 1;//开启总中断
ES = 1;//开启串口中断
}
void main()
{
UartInit();
while(1){
}
}
void Uart_Handler() interrupt 4 //当数据发完或接收完 TI和RI都会被置1 同时进入这个中断函数
{
if(RI)//中断处理函数中,对于接收中断的响应
{
RI = 0;//清除接收中断标志位
cmd = SBUF;
if(cmd == 'o'){ //发o进来 开灯
D5 = 0;//点亮D5
}
if(cmd == 'c'){ //发c进来 熄灭
D5 = 1;//熄灭D5
}
}
if(TI);
{
TI = 0;
//后面就能做其他操作
}
}
这里要记住:当数据 发完 或 接收完, TI 和 RI 都会被置1 同时都会进入这个中断函数,在里面用if判断后,就能进行想要的一系列操作,记得要把TI 和 RI 重新置0;
最后说一下如何计算TH1 和 HL1的初值
SMOD:波特率选择位。当用软件置位SMOD,即SMOD=1,则使串行通信方式1、2、3的波特率加倍;SMOD=0,则各工作方式的波特率不加倍。复位时SMOD=0。
一般选择不加倍,所以SMOD为0,SYSclk是单片机时钟,也就是晶振的频率,11.0592MHz,运算时要转化为基本单位Hz,即11059200Hz
定时器工作模式是8位自动重装载,TH1和TL1赋的初值一样
根据以上公式最终能计算出TH1 = TL1 = 0xFD