目录
介绍
8051微控制器有两个定时器/计数器,可以在时钟频率上工作。定时器/计数器可用于产生时间延迟,计算外部事件等。
时钟
每个定时器都需要一个时钟才能工作,8051通过外部晶振提供它,这是Timer的主要时钟源。8051微控制器中的内部电路为定时器提供时钟源,该定时器是连接到微控制器的晶体频率的1/12,也称为机器周期频率。
8051定时器时钟
例如,假设我们的晶振频率为11.0592 MHz,那么微控制器将提供1/12即:
定时器时钟频率=(Xtal Osc.frequency)/ 12 =(11.0592 MHz)/ 12 = 921.6 KHz
周期T = 1 /(921.6KHz)=1.085μS
计时器
8051有两个定时器Timer0(T0)和Timer1(T1),两者都是16位宽。由于8051具有8位架构,因此每个都由两个独立的8位寄存器访问,如下图所示。这些寄存器用于加载定时器计数。
8051具有定时器模式寄存器和定时器控制寄存器,用于选择操作模式和控制目的。
我们来看看这些寄存器,
TMOD寄存器
TMOD是一个8位寄存器,用于设置timer0和timer1的定时器模式。
8051 TMOD寄存器
其低4位用于Timer0,高4位用于Timer1
Bit 7,3 – GATE:
1 =仅当INT0 / INT1引脚为高电平且TR0 / TR1置1时,才使能定时器/计数器。
0 = TR0 / TR1置位时使能定时器/计数器。
Bit 6,2 – C/(Counter/Timer): 定时器或计数器选择位
1 =用作计数器
0 =用作定时器
Bit 5:4 & 1:0 – M1:M0: 定时器/计数器模式选择位
这些是定时器/计数器模式选择位,如下表所示
TCON注册
TCON是8位控制寄存器,包含定时器和中断标志。
Bit 7 – TF1: Timer1溢出标志
1 =发生Timer1溢出(即Timer1变为最大值并翻转回零)。
0 =未发生Timer1溢出。
它通过软件清除。在Timer1溢出中断服务程序中,该位在退出ISR时自动清零。
Bit 6 – TR1: Timer1运行控制位
1 = Timer1启动。
0 = Timer1停止。
它由软件设置和清除。
Bit 5 – TF0: Timer0溢出标志
1 =发生Timer0溢出(即Timer0进入最大值并翻转回零)。
0 =未发生Timer0溢出。
它通过软件清除。在Timer0溢出中断服务程序中,该位在退出ISR时自动清零。
Bit 4 – TR0: Timer0运行控制位
1 = Timer0启动。
0 = Timer0停止。
它由软件设置和清除。
Bit 3 – IE1:外部中断1边沿标志
1 =发生外部中断1。
0 =外部中断1已处理。
它由硬件设置和清除。
Bit 2 – IT1: :外部中断1触发类型选择位
1 = INT1引脚的下降沿发生中断。
0 = INT1引脚在低电平时发生中断。
Bit 1 – IE0: 外部中断0边沿标志
1 =发生外部中断0。
0 =外部中断0已处理。
它由硬件设置和清除。
Bit 0 – IT0:外部中断0触发类型选择位
1 = INT0引脚的下降沿发生中断。
0 = INT0引脚在低电平时发生中断。
我们来看看定时器模式
定时器模式
定时器具有使用M0和M1位组合在TMOD寄存器中选择的操作模式。
模式0(13位定时器模式)
模式0是13位定时器模式,其中使用8位THx和5位TLx(作为预分频器)。它主要用于与旧的MCS-48系列微控制器进行对接。
如上图所示,用于形成总13位定时器的8位THx和低5位TLx。使用定时器模式0时,应将高3位的TLx写为0,否则会影响结果。
例
让我们使用AT89C51微控制器生成一个2ms周期的方波,其中timer0在port1的P1.0引脚上的mode0中。假设xtal振荡器频率为11.0592 MHz。
8051 Mode0波形示例
由于Xtal振荡器频率为11.0592 MHz,因此机器周期为1.085uSec。因此,需要计数以产生1毫秒的延迟:
计数= =(1×10^-3)/(1.085×10^-6) ≈ 921
Mode0的最大计数为2 ^ 13(0 – 8191),Timer0计数将从0 – 8191递增。因此我们需要加载的值比最大计数少921,即8191。此外,在下面的程序中,我们需要从调用到返回延迟功能的额外13 MC(机器周期)。因此需要加载的Value是:
Value=(8191-Count)+Function_MCycles+1 =7284= 0x1C74
所以我们需要在Timer0中加载0x1C74值。
1C74 = 0001 1100 0111 0100 ,现在在TL0中加载低5位,在TH0中加载下一个8位
所以我们得到了,
TL0 = 0001 0100 = 0x14 and TH0 = 1110 0011 = 0xE3
延迟功能的编程步骤
- 加载Tmod寄存器值,即TM0 = 0x00,用于Timer0 / 1 mode0(13位定时器模式)。
- 加载计算的THx值,即TH0 = 0xE3。
- 加载计算的TLx值,即TL0 = 0x14。
- 通过设置TRx位启动定时器。即这里TR0 = 1。
- 轮询TFx标志直到它没有设置。
- 通过清零TRx位来停止定时器。即这里TR0 = 0。
- 清除定时器标志TFx位,即TF0 = 0。
- 从步骤1到7重复再次延迟。
定时器模式0的程序
/*
* 8051_Timer_Mode0
* http://www.qutaojiao.com
*/
#include <reg51.h> /* Include x51 header file */
sbit test = P1^0; /* set test pin 0 of port1 */
void timer_delay() /* Timer0 delay function */
{
TH0 = 0xE3; /* Load 8-bit in TH0 (here Timer0 used) */
TL0 = 0x14; /* Load 5-bit in TL0 */
TR0 = 1; /* Start timer0 */
while(TF0 == 0); /* Wait until timer0 flag set */
TR0 = 0; /* Stop timer0 */
TF0 = 0; /* Clear timer0 flag */
}
余下程序:
模式1(16位定时器模式)
模式1是16位定时器模式,用于产生延迟,它使用8位THx和8位TLx组成一个总共16位寄存器。
例
让我们使用AT89C51微控制器生成2ms时间周期的方波,其中timer0在mode1的端口1的P1.0引脚上。假设Xtal振荡器频率为11.0592 MHz。
由于Xtal是11.0592 MHz,我们的机器周期为1.085us。
因此,需要计数以产生1毫秒的延迟。是,
计数=(1×10^(-3)) / (1.085×10^(-6) ) ≈ 921
并且mode1的最大计数是2 ^ 16(0 – 65535),并且它从0增加到65535所以我们需要加载比最大值少921的值。计数即65535。此外,在下面的程序中,我们需要额外的13个MC(机器周期)从调用到返回延迟功能。因此需要加载的Value是,
Value=(65535-Count)+Function_MCycles+1 =64615= (FC74)Hex
所以我们需要在TH0中加载FC74 Hex值高字节,在TL0中加载低字节,
TH0 = 0xFC & TL0 = 0x74
延迟功能的编程步骤
- 加载Tmod寄存器值,即TM0 = 0x01,用于Timer0模式1(16位定时器模式)。
- 加载计算的THx值,即TH0 = 0xFC。
- 加载计算的TLx值,即TL0 = 0x74。
- 通过设置TRx位启动定时器。即这里TR0 = 1。
- 轮询TFx标志直到它没有设置。
- 通过清零TRx位来停止定时器。即这里TR0 = 0。
- 清除定时器标志TFx位,即TF0 = 0。
- 从步骤1到7重复再次延迟。
定时器模式的程序1
/*
* 8051_Timer_Mode1
* http://www.qutaojiao.com
*/
#include <reg51.h> /* Include x51 header file */
sbit test = P1^0; /* set test pin0 of port1 */
void timer_delay() /* Timer0 delay function */
{
TH0 = 0xFC; /* Load higher 8-bit in TH0 */
TL0 = 0x74; /* Load lower 8-bit in TL0 */
TR0 = 1; /* Start timer0 */
while(TF0 == 0); /* Wait until timer0 flag set */
TR0 = 0; /* Stop timer0 */
TF0 = 0; /* Clear timer0 flag */
}
余下程序:
模式2(8位自动重载定时器模式)
模式2是8位自动重载定时器模式。在此模式下,我们只需加载THx-8位值。当Timer启动时,THx值自动被装入TLx,TLx从该值开始计数。在TLx的值从0xFF溢出到0x0之后,TFx标志被置位,并且来自THx的值再次自动加载到TLx寄存器中。这就是为什么这被称为自动重载模式。
例
在这里,我们在PORT1.0上生成200us的方波。在mode2中使用Timer1的时间段。我们将使用11.0592 MHz Xtal振荡器频率。
8051 Mode2示例Wave
由于Xtal是11.0592 MHz,我们的机器周期为1.085us。因此,需要计数以产生1毫秒的延迟。是,
计数=(100×10^(-6)) / (1.085×10^(-6) )≈92
并且mode2的最大计数是2 ^ 8(0 – 255)并且它从0到255递增,因此我们需要加载比其最大值少92的值。计数即255.因此需要加载的Value是:
Value=(255-Count)+1 =164= 0xA4
所以我们需要在高字节中加载A4的十六进制值,
TH1 = 0xA4
延迟功能的编程步骤
- 加载Tmod寄存器值,即Timer1 mode2的TMOD = 0x20(8位定时器自动重载模式)。
加载计算的THx值,即TH1 = 0xA4。
为TLx加载相同的值,即TL1 = 0xA4。
通过设置TRx位启动定时器。即这里TR1 = 1。
轮询TFx标志直到它没有设置。
清除定时器标志TFx位,即TF1 = 0。
从步骤5和6重复再次延迟。
定时器模式2的程序
/*
* 8051_Timer_Mode2
* http://www.qutaojiao.com
*/
#include <reg51.h> /* Include x51 header file */
sbit test = P1^0; /* set test pin0 of port1 */
void main()
{
TMOD = 0x20; /* Timer1 mode2 (8-bit auto reload timer mode) */
TH1 = 0xA4; /* Load 8-bit in TH1 */
TL1 = 0xA4; /* Load 8-bit in TL1 once */
TR1 = 1; /* Start timer1 */
while(1)
{
test = ~test; /* Toggle test pin */
while(TF1 == 0); /* Wait until timer1 flag set */
TF1 = 0; /* Clear timer1 flag */
}
}
8051中的定时器中断
8051具有两个分配有不同向量地址的定时器中断。当定时器计数从其最大值翻转到0时,它设置定时器标志TFx。如果启用了全局和定时器中断,这将中断8051微控制器以服务ISR(中断服务程序)。
定时器溢出中断分配有表中所示的向量地址。8051微控制器在发生相应的中断时直接跳转到向量地址。
8051定时器中断
例
这里我们将使用Timer0中断在PORT1.0上产生10Hz的方波。我们将在mode1中使用Timer0,振荡器频率为11.0592 MHz。
8051定时器中断示例波形
由于Xtal是11.0592 MHz,我们的机器周期为1.085us。因此,需要计数以产生50毫秒的延迟。是,
计数 =(50×10^(-3)) / (1.085×10^(-6) ) ≈ 46080
并且mode1的最大计数是2 ^ 16(0 – 65535),并且它从0到65535递增,因此我们需要加载比其最大值少46080的值。计数即65535.因此需要加载的Value是:
Value=(65535-Count)+1 =19456= (4C00)Hex
因此,我们需要在较高字节和较低字节中加载4C00 Hex值:
TH0 = 0x4C & TL0 = 0x00
注意,TF0标志无需通过软件清除,因为微控制器在完成ISR例程后将其清除。
定时器中断程序
/*
* 8051_Timer_Interrupt
* http://www.qutaojiao.com
*/
#include<reg51.h> /* Include x51 header file */
sbit test = P1^0; /* set test pin0 of port1 */
void Timer_init()
{
TMOD = 0x01; /* Timer0 mode1 */
TH0 = 0x4C; /* 50ms timer value */
TL0 = 0x00;
TR0 = 1; /* Start timer0 */
}
void Timer0_ISR() interrupt 1 /* Timer0 interrupt service routine (ISR) */
{
test = ~test; /* Toggle port pin */
TH0 = 0x4C; /* 50ms timer value */
TL0 = 0x00;
}
余下程序:
本节课程序下载:
8051 vs arduino 計時及中斷就理論和實驗有何不同?
原理其实一样,只不过arduino操作中断计时器的时候,封装成了库,操作简便一些。
我想学习一下