前言
开始做定时器/计数器实验啦!先介绍原理再开始实验。
一、硬件设计
1.TIMER概念介绍
定时器(timer)几乎是每个MCU必有的重要外设之一,可用于定时、精确延时、计数等等,在检测、控制领域有广泛应用。
定时器运行时不占用CPU时间,配置好之后,可以与CPU并行工作,实现精确的定时和计数,并且可以通过软件控制其是否产生中断,使用起来灵活方便。通常MCU在介绍定时器外设时,总是和计数器(counter)一起出现,所以我们有必要先了解一下定时器和计数器的区别。
定时器和计数器实际都是通过计数器来计数,定时器是对周期不变的脉冲计数(一般来自于系统时钟),由计数的个数和脉冲的周期即可计算出时间,同时,通过一个给定的预期值(即比较值,对应预期的计数值,也就是预期时间),当计数值达到预期值时产生中断,这样就实现了定时,应用程序通过设置不同的预期值实现不同时长的定时。
计数器是对某一事件进行计数,这个事件每发生一次,计数值加/减1,而这个事件的产生可能是没有规律的。也就是计数器的用途是对事件的发生次数进行计数,由计数值来反映事件产生的次数。
☆注:不同MCU定时器外设使用的计数器位数是个重要的参数,STC8A8K64S4A12系列MCU的定时器使用的是16位计数器(由定时器高8位寄存器和定时器低8位寄存器组合起来实现)。
2.STC8A8K64S4A12系列单片机定时器/计数器介绍
STC8A8K64S4A12系列单片机有5个16位的定时器/计数器,即定时器/计数器T0、定时器/计数器T1、定时器/计数器T2、定时器/计数器T3、定时器/计数器T4。
这些定时器/计数器外设有2种工作方式:定时方式(定时器)和计数方式(计数器)。可通过特殊功能寄存器设置相应的C/T控制位,来选择定时器/计数器工作在哪种方式。
定时器/计数器外设的核心部件是一个加法计数器,其本质是对脉冲信号进行计数。不同的是,定时器的脉冲信号来自于系统时钟,而计数器的脉冲信号来自于单片机特定外部输入引脚。STC8A8K64S4A12系列单片机计数器特定外部输入引脚如下表。
表1:单片机计数器外部输入引脚分配
TxINT对应IO口功能描述说明备注
计数器0外部输入引脚非独立GPIOLCD12864/LCD1602屏接口
计数器1外部输入引脚非独立GPIOLCD12864/LCD1602屏接口
计数器2外部输入引脚非独立GPIOnRF24L01P模块接口
计数器3外部输入引脚非独立GPIOnRF24L01P模块接口
计数器4外部输入引脚非独立GPIO电位器ADC采样
☆注:独立GPIO表示开发板没有其他的电路使用这个GPIO,非独立GPIO说明开发板有其他电路用到了该GPIO。针对非独立GPIO使用时需特别注意。
图1:定时器/计数器结构原理示意图
☆注:12T模式是选择对系统时钟12分频,1T模式是选择对系统时钟不分频。另外,该计数器是递增计数器,不具有递减功能。
■定时器/计数器工作在定时方式(定时器)时:
系统时钟进行输入计数,每输入一个脉冲,计数值加1,当计数到计数器为全1时,再输入一个脉冲就使计数值回零,同时从最高位溢出一个脉冲使特殊功能寄存器TCON的TFx位置1(T2、T3、T4没有该寄存器位TFx),作为计数器的溢出中断标志。
由于计数脉冲的周期是固定的,所以脉冲数乘以脉冲周期就是定时时间,或者称定时溢出时间(关于定时溢出时间计算公式后有详述)。
定时器可作为串口通信时的波特率发生器,需配置相关寄存器。
可通过寄存器的TxCLKO位选择特定时钟输出引脚输出脉冲信号,该脉冲信号的频率等于计数器溢出率的一半。
表2:单片机计数器时钟输出引脚分配
TxCLKO对应IO口功能描述说明备注
时钟输出引脚非独立GPIOLCD12864/LCD1602屏接口
时钟输出引脚非独立GPIOLCD12864/LCD1602屏接口
时钟输出引脚非独立GPIOnRF24L01P模块接口
时钟输出引脚非独立GPIO用户按键KEY4
时钟输出引脚非独立GPIO用户按键KEY3
☆注:独立GPIO表示开发板没有其他的电路使用这个GPIO,非独立GPIO说明开发板有其他电路用到了该GPIO。针对非独立GPIO使用时需特别注意。
■定时器/计数器工作在计数方式(计数器)时:
单片机特定引脚输入的外部脉冲信号源进行输入计数,每输入一个从1到0的负跳变的脉冲时,计数值加1,当计数到计数器为全1时,再输入一个脉冲就使计数值回零,同时从最高位溢出一个脉冲使特殊功能寄存器TCON的TFx位置1(T2、T3、T4没有该寄存器位TFx),作为计数器的溢出中断标志。
外部输入信号的最高允许频率不能大于系统时钟频率SYSclk的1/4,比如CPU运行的系统时钟为11.0592MHz,允许外部最高输入信号频率为11.0592MHz/4=2.7648MHz,如果频率高于这个值,输入信号的部分脉冲在检测过程中会被丢失,导致测量得到的频率比真实频率低。
由于系统每个时钟对外部计数器引脚采样1次,当前一次采样到外部引脚为高电平而后一次采样到低电平则形成一个负跳变,因此确认外部输入信号的一次负跳变至少需要2个系统时钟周期,实际上,引脚输入通道中还有一个同步采样与边沿检测电路,所以外部输入信号的最高允许频率不能大于系统时钟频率SYSclk的1/4。
3.定时器/计数器工作模式
STC8A8K64S4A12系列MCU不同的定时器/计数器所具有的可供选择的工作模式不同,定时器/计数器0和定时器/计数器1有多种模式可供选择,选择模式是通过寄存器TMOD对应的M0位和M1位实现。定时器/计数器2、定时器/计数器3和定时器/计数器4只有默认的一种工作模式。具体如下面列表所示。
表3:STC8A8K64S4A12系列定时器/计数器工作模式
序号定时器/计数器工作模式T0T1T2T3T4
1模式0:16位自动重装载模式有有有有有
2模式1:16位不可重装载模式有有
3模式2:8位自动重装载模式有有
4模式3:不可屏蔽16位自动重装载模式有
☆注:模式0是STC官方推荐学习的定时器/计数器工作模式,也是我们讲解的重点。当T0工作在模式3,T0可作为实时操作系统用节拍定时器。
下面介绍下定时器/计数器0和定时器/计数器1使用时非常重要的一个寄存器TMOD。
图2:模式寄存器TMOD
☆注:定时器/计数器的门控位在一些特殊应用中会有使用,一般情况下是将门控位置0。
■定时器/计数器0和定时器/计数器1工作模式0分析。
图3:定时器/计数器0和定时器/计数器1模式0结构图
1)定时器/计数器0和定时器/计数器1均有GATE门控位为0、为1的情况。
2)当GATE=0时,控制定时器/计数器完全由TRx(定时器运行控制位)决定。
3)当GATE=1时,控制定时器/计数器不仅由TRx(定时器运行控制位)决定,还由外部中断引脚上的信号决定。此时,可用于脉宽测量。
4)图中隐藏2个寄存器RL_THx和RL_TLx,RL_THx和THx共用同一个地址,RL_TLx和TLx共用同一个地址。当Tx被禁止工作时,写入THx和TLx的内容会同时被写入RL_THx和RL_TLx中。当Tx开启工作时,准备写入THx和TLx的内容,其实没有被写入到写入THx和TLx中,而是写到了RL_THx和RL_TLx中。这样便巧妙的实现16位重装载定时。而读THx和TLx的内容时,读取的就是THx和TLx的内容,而不是RL_THx和RL_TLx中的值。
■定时器/计数器2、定时器/计数器3和定时器/计数器4工作模式0分析。
图4:定时器/计数器2、定时器/计数器3和定时器/计数器4模式0结构图
1)定时器/计数器2、定时器/计数器3和定时器/计数器4不存在门控位GATE,对定时器/计数器的控制完全由TxR(定时器运行控制位)决定。
2)定时器/计数器2、定时器/计数器3和定时器/计数器4有且只有一种模式,即16位重装载模式,实现重装载的原理请参考对定时器/计数器0和定时器/计数器的分析。
4.定时器/计数器溢出时间计算
STC8A8K64S4A12系列单片机作为定时器使用时,由于计数脉冲的周期是固定的,所以溢出前的脉冲数乘以脉冲周期就是定时时间,或者称定时溢出时间。
下面是定时器溢出时间计算公式:
图5:定时器溢出时间计算公式
☆注:公式中的分频因子PSC在配置定时器/计数器为12T模式时值为12,在配置定时器/计数器为1T模式时值为1。
举例,配置定时器/计数器为1T模式,系统时钟频率为11.0592MHZ,高8位寄存器初始值为0x28,低8位寄存器初始值为0x00,计算下定时器溢出时间。
1)十六进制0x28转成十进制是40,十六进制0x00转成十进制是0。这样初始装载值为:256*40+0=10240。
2)16位计数器溢出前所计脉冲数为:65536-10240=55296。
3)分频因子PSC在1T模式下值为1。系统时钟频率为11059200HZ。
4)定时器溢出时间:55296/11059200=0.005s=5ms。
5)如果已知定时器溢出时间,计算高8位寄存器初始装载值和低8位寄存器初始装载值,则是反推过来即可(建议使用软件STC-ISP的定时器计算器)。
5.定时器/计数器中断配置步骤
针对STC8A8K64S4A12系列单片机5个定时器/计数器外设,软件的配置过程如下:
图6:定时器/计数器中断软件配置步骤
☆注:实验例程即是按照上述配置步骤操作寄存器相关位实现,后有详述。
二、软件设计
1.定时器/计数器寄存器汇集
STC8A8K64S4A12系列单片机操作定时器/计数器时会用到18个寄存器,如下表所示:
表4:STC8A8K64S4A12系列定时器/计数器使用寄存器汇总
序号寄存器名读/写功能描述
1TCON读/写定时器/计数器中断控制寄存器。
2TMOD读/写定时器/计数器模式寄存器。
3AUXR读/写辅助寄存器1。
4INTCLKO读/写中断与时钟输出控制寄存器。
5IE读/写中断允许寄存器1。
6IE2读/写中断允许寄存器2。
7IP读/写中断优先级寄存器1。
8T4T3M读/写T3和T4控制寄存器。
9TL0读/写定时器/计数器0低8位寄存器。
10TH0读/写定时器/计数器0高8位寄存器。
11TL1读/写定时器/计数器1低8位寄存器。
12TH1读/写定时器/计数器1高8位寄存器。
13T2L读/写定时器/计数器2低8位寄存器。
14T2H读/写定时器/计数器2高8位寄存器。
15T3L读/写定时器/计数器3低8位寄存器。
16T3H读/写定时器/计数器3高8位寄存器。
17T4L读/写定时器/计数器4低8位寄存器。
18T4H读/写定时器/计数器4高8位寄存器。
☆注:上述寄存器有部分不单单是用于定时器/计数器外设中的,比如寄存器TCON、IE、IE2等。
2.寄存器解析
2.1.中断允许寄存器IE
外部中断允许寄存器IE支持位寻址,该寄存器的B1和B3位是定时器/计数器0和定时器/计数器1的中断允许位。
图7:中断允许寄存器
2.2.中断允许寄存器IE2
中断允许寄存器IE2不支持位寻址,该寄存器的B2、B5和B6位是定时器/计数器2、定时器/计数器3和定时器/计数器4的中断允许位。因为IE2寄存器不支持位寻址,所以举例操作该寄存器B2位时,不可以直接“ET2=0;”进行操作,参考下图。
图8:中断允许寄存器
2.3.定时器/计数器中断控制寄存器TCON
定时器/计数器中断控制寄存器TCON支持位寻址,该寄存器的B4位和B6位是T0和T1的运行控制位,寄存器B5位和B7位是T0和T1的溢出中断标志,含义如下图。
图9:定时器/计数器中断控制寄存器
☆注:TCON寄存器的低4个位是用于外部中断的,在操作该寄存器时一定要按位操作,用不到的位不要操作。
2.4.辅助寄存器AUXR
辅助寄存器AUXR不支持位寻址,该寄存器的B6和B7位是定时器/计数器1和定时器/计数器0的速度控制位,寄存器的B1、B2和B3位是定时器/计数器2的速度控制位、工作方式选择位和允许控制位。
图10:辅助寄存器
☆注:AUXR寄存器的B0和B5位在串口外设配置时可能会用到。
2.5.中断与时钟输出控制寄存器INTCLKO
中断与时钟输出控制寄存器INTCLKO不支持位寻址,该寄存器的B0、B1和B2位是定时器/计数器0、定时器/计数器1和定时器/计数器2的时钟输出引脚控制位。
图11:中断与时钟输出控制寄存器
☆注:INTCLKO寄存器的定时器时钟输出功能和定时器工作模式及工作方式(定时还是计数)密切相关,用户暂只需知道有这些关系,待项目应用时再分析频率关系。
2.6.寄存器T4T3M
寄存器T4T3M不支持位寻址,该寄存器的B0到B3位是定时器/计数器3配置过程中会用到的位,寄存器的B4到B7位是定时器/计数器4配置过程中会用到的位。
图12:T3T4M寄存器
2.7.中断优先级控制寄存器IP
中断优先级控制寄存器IP支持位寻址,该寄存器的B1位和B3位是设置T0和T1中断优先级的,含义如下图。需要说明的是T2、T3和T4是没有中断优先级的。
图13:中断优先级控制寄存器
3.定时器0定时实验
☆注:本节的实验源码是在“实验2-4-1:外部中断0(下降沿中断方式)”的基础上修改。本节对应的实验源码是:“实验2-5-1:定时器0定时”。
3.1.工程需要用到的c文件
本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。
表5:实验需要用到的c文件
序号文件名后缀功能描述
1包含与用户led控制有关的用户自定义函数
2外部定时器有关的用户自定义函数
3包含用户自定义延时函数
3.2.头文件引用和路径设置
■需要引用的头文件
include""
#include""
1
2
3
■需要包含的头文件路径
本例需要包含的头文件路径如下表:
序号路径描述
1…\、和头文件在该路径,所以要包含
2…\头文件在该路径,所以要包含
头文件在该路径,所以要包含
图14:添加头文件包含路径
3.3.编写代码
首先,在文件中编写定时器0的初始化函数Timer0Init,代码如下。
程序清单:定时器0初始化函数
/***********************************************************
功能描述:定时器0初始化
入口参数:无
返回值:无
************************************************************/
voidTimer0Init(void)
{
AUXR|=0x80;//定时器0为1T模式
TMOD=0xF8;//定时器0设置为定时方式,工作模式为16位自动重装模式
TMOD=0xF7;//定时器0门控位GATE设置为0
TL0=0x00;//1T模式下初始装载值
TH0=0x28;//1T模式下初始装载值
TF0=0;//清除T0中断溢出标志位
ET0=1;//使能定时器0的溢出中断允许位
TR0=1;//定时器0开始计时
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
然后,编写定时器0中断服务函数,一旦响应中断达到一定次数会执行翻转用户指示灯D3的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:定时器0中断服务程序
入口参数:无
返回值:无
************************************************************/
voidtimer0_int(void)interrupt1
{
cnt++;//5ms进入1次中断
if(cnt==200)//200次中断被响应后,正好1000ms
{
led_toggle(LED_3);//翻转用户指示灯D3
cnt=0;
}
//进入中断时会将定时器中断溢出标志位硬件清零,因此下面一句可以不加的
TF0=0;//清除T0中断溢出标志位
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
最后,在主函数中调用T0初始化函数,开启总中断,而主循环中没有任务,说明用户指示灯D3变化来自于中断。
代码清单:主函数
intmain(void)
{
Timer0Init();//定时器0初始化
EA=1;//使能总中断
while(1)
{
;//无任务,说明LED亮灭来自于中断
}
}
1
2
3
4
5
6
7
8
9
4.定时器1定时实验
==☆注:本节的实验源码是在“实验2-5-1:定时器0定时”的基础上修改。本节对应的实验源码是:“实验2-5-2:定时器1定时”。==
4.1.工程需要用到的c文件
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-5-1:定时器0定时”部分。
4.2.编写代码
首先,在文件中编写定时器1的初始化函数Timer1Init,代码如下。
程序清单:定时器1初始化函数
/***********************************************************
功能描述:定时器1初始化
入口参数:无
返回值:无
************************************************************/
voidTimer1Init(void)
{
AUXR|=0x40;//定时器1为1T模式
TMOD=0x8F;//定时器1设置为定时方式,工作模式为16位自动重装模式
TMOD=0x7F;//定时器1门控位GATE设置为0
TL1=0x00;//1T模式下初始装载值
TH1=0x28;//1T模式下初始装载值
TF1=0;//清除T1中断溢出标志位
ET1=1;//使能定时器1的溢出中断允许位
TR1=1;//定时器1开始计时
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
然后,编写定时器1中断服务函数,一旦响应中断达到一定次数会执行翻转蓝色指示灯D3的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:定时器1中断服务程序
入口参数:无
返回值:无
************************************************************/
voidtimer1_int(void)interruptTIMER1_VECTOR
{
cnt++;//5ms进入1次中断
if(cnt==200)//200次中断被响应后,正好1000ms
{
led_toggle(LED_3);//翻转用户指示灯D3
cnt=0;
}
//进入中断时会将定时器中断溢出标志位硬件清零,因此下面一句可以不加的
TF1=0;//清除T1中断溢出标志位
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
最后,在主函数中调用T1初始化函数,开启总中断,而主循环中没有任务,说明用户指示灯D3变化来自于中断。
代码清单:主函数
intmain(void)
{
Timer1Init();//定时器1初始化
EA=1;//使能总中断
while(1)
{
;//无任务,说明LED亮灭来自于中断
}
}
1
2
3
4
5
6
7
8
9
5.定时器2定时实验
==☆注:本节的实验源码是在“实验2-5-1:定时器0定时”的基础上修改。本节对应的实验源码是:“实验2-5-3:定时器2定时”。==
5.1.工程需要用到的c文件
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-5-1:定时器0定时”部分。
5.2.编写代码
首先,在文件中编写定时器2的初始化函数Timer2Init,代码如下。
程序清单:定时器2初始化函数
/***********************************************************
功能描述:定时器2初始化
入口参数:无
返回值:无
************************************************************/
voidTimer2Init(void)
{
AUXR=0xF7;//定时器2设置为定时方式
AUXR|=0x04;//设置定时器2为1T模式
T2L=0x00;//1T模式下初始装载值
T2H=0x28;//1T模式下初始装载值
IE2|=0x04;//使能定时器2中断
AUXR|=0x10;//打开定时器2
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
然后,编写定时器2中断服务函数,一旦响应中断达到一定次数会执行翻转蓝色指示灯D3的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:定时器2中断服务程序
入口参数:无
返回值:无
************************************************************/
voidtimer2_int(void)interruptTIMER2_VECTOR
{
cnt++;//5ms进入1次中断
if(cnt==200)//200次中断被响应后,正好1000ms
{
led_toggle(LED_3);//翻转用户指示灯D3
cnt=0;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
最后,在主函数中调用T2初始化函数,开启总中断,而主循环中没有任务,说明用户指示灯D3变化来自于中断。
代码清单:主函数
intmain(void)
{
Timer2Init();//定时器2初始化
EA=1;//使能总中断
while(1)
{
;//无任务,说明LED亮灭来自于中断
}
}
1
2
3
4
5
6
7
8
9
6.定时器3定时实验
==☆注:本节的实验源码是在“实验2-5-1:定时器0定时”的基础上修改。本节对应的实验源码是:“实验2-5-4:定时器3定时”。==
6.1.工程需要用到的c文件
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-5-1:定时器0定时”部分。
6.2.编写代码
首先,在文件中编写定时器3的初始化函数Timer2Init,代码如下。
程序清单:定时器3初始化函数
/***********************************************************
功能描述:定时器3初始化
入口参数:无
返回值:无
************************************************************/
voidTimer3Init(void)
{
T4T3M=0xFB;//定时器3设置为定时方式
T4T3M|=0x02;//设置定时器3为1T模式
T3L=0x00;//1T模式下初始装载值
T3H=0x28;//1T模式下初始装载值
IE2|=(15);//使能定时器3中断
T4T3M|=0x08;//打开定时器3
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
然后,编写定时器3中断服务函数,一旦响应中断达到一定次数会执行翻转蓝色指示灯D3的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:定时器3中断服务程序
入口参数:无
返回值:无
************************************************************/
voidtimer3_int(void)interruptTIMER3_VECTOR
{
cnt++;//5ms进入1次中断
if(cnt==200)//200次中断被响应后,正好1000ms
{
led_toggle(LED_3);//翻转用户指示灯D3
cnt=0;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
最后,在主函数中调用T3初始化函数,开启总中断,而主循环中没有任务,说明用户指示灯D3变化来自于中断。
代码清单:主函数
intmain(void)
{
Timer3Init();//定时器3初始化
EA=1;//使能总中断
while(1)
{
;//无任务,说明LED亮灭来自于中断
}
}
1
2
3
4
5
6
7
8
9
7.定时器4定时实验
==☆注:本节的实验源码是在“实验2-5-1:定时器0定时”的基础上修改。本节对应的实验源码是:“实验2-5-5:定时器4定时”。==
7.1.工程需要用到的c文件
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-5-1:定时器0定时”部分。
7.2.编写代码
首先,在文件中编写定时器4的初始化函数Timer4Init,代码如下。
程序清单:定时器4初始化函数
/***********************************************************
功能描述:定时器4初始化
入口参数:无
返回值:无
************************************************************/
voidTimer4Init(void)
{
T4T3M=0xBF;//定时器4设置为定时方式
T4T3M|=0x20;//设置定时器4为1T模式
T4L=0x00;//1T模式下初始装载值
T4H=0x28;//1T模式下初始装载值
IE2|=(16);//使能定时器4中断
T4T3M|=0x80;//打开定时器4
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
然后,编写定时器4中断服务函数,一旦响应中断达到一定次数会执行翻转蓝色指示灯D3的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:定时器4中断服务程序
入口参数:无
返回值:无
************************************************************/
voidtimer4_int(void)interruptTIMER4_VECTOR
{
cnt++;//5ms进入1次中断
if(cnt==200)//200次中断被响应后,正好1000ms
{
led_toggle(LED_3);//翻转用户指示灯D3
cnt=0;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
最后,在主函数中调用T4初始化函数,开启总中断,而主循环中没有任务,说明用户指示灯D3变化来自于中断。
代码清单:主函数
intmain(void)
{
Timer4Init();//定时器4初始化
EA=1;//使能总中断
while(1)
{
;//无任务,说明LED亮灭来自于中断
}
}
1
2
3
4
5
6
7
8
9
8.多个定时器定时实验
==☆注:本节的实验源码是在“实验2-5-1:定时器0定时”的基础上修改。本节对应的实验源码是:“实验2-5-6:多个定时器定时”。==
8.1.工程需要用到的c文件
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-5-1:定时器0定时”部分。
8.2.编写代码
首先,在文件中编写定时器0和定时器1的初始化函数Timer0Init和Timer1Init,代码如下。
程序清单:定时器0和定时器1初始化函数
/***********************************************************
功能描述:定时器0初始化
入口参数:无
返回值:无
************************************************************/
voidTimer0Init(void)
{
AUXR|=0x80;//定时器0为1T模式
TMOD=0xF8;//定时器0设置为定时方式,工作模式为16位自动重装模式
TMOD=0xF7;//定时器0门控位GATE设置为0
TL0=0x00;//1T模式下初始装载值
TH0=0x28;//1T模式下初始装载值
TF0=0;//清除T0中断溢出标志位
ET0=1;//使能定时器0的溢出中断允许位
TR0=1;//定时器0开始计时
}
/***********************************************************
功能描述:定时器1初始化
入口参数:无
返回值:无
************************************************************/
voidTimer1Init(void)
{
AUXR|=0x40;//定时器1为1T模式
TMOD=0x8F;//定时器1设置为定时方式,工作模式为16位自动重装模式
TMOD=0x7F;//定时器1门控位GATE设置为0
TL1=0x00;//1T模式下初始装载值
TH1=0x28;//1T模式下初始装载值
TF1=0;//清除T1中断溢出标志位
ET1=1;//使能定时器1的溢出中断允许位
TR1=1;//定时器1开始计时
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
然后,编写定时器0和定时器1的中断服务函数,一旦响应中断达到一定次数会执行翻转对应用户指示灯的操作,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:定时器0中断服务程序
入口参数:无
返回值:无
************************************************************/
voidtimer0_int(void)interruptTIMER0_VECTOR
{
cnt1++;//5ms进入1次中断
if(cnt1==100)//100次中断被响应后,正好500ms
{
led_toggle(LED_3);//翻转用户指示灯D3
cnt1=0;
}
//进入中断时会将定时器中断溢出标志位硬件清零,因此下面一句可以不加的
TF0=0;//清除T0中断溢出标志位
}
/***********************************************************
功能描述:定时器1中断服务程序
入口参数:无
返回值:无
************************************************************/
voidtimer1_int(void)interruptTIMER1_VECTOR
{
cnt2++;//5ms进入1次中断
if(cnt2==200)//200次中断被响应后,正好1000ms
{
led_toggle(LED_4);//翻转用户指示灯D4
cnt2=0;
}
//进入中断时会将定时器中断溢出标志位硬件清零,因此下面一句可以不加的
TF1=0;//清除T1中断溢出标志位
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
最后,在主函数中调用T0和T1初始化函数,开启总中断,而主循环中没有任务,蓝色指示灯D3和蓝色指示灯D4变化来自于中断。
代码清单:主函数
intmain(void)
{
Timer0Init();//定时器0初始化
Timer1Init();//定时器1初始化
EA=1;//使能总中断
while(1)
{
;//无任务,说明LED亮灭来自于中断
}
}
1
2
3
4
5
6
7
8
9
10
总结
以上就是今天要讲的内容,大家看完可以思考一下:本例可以设置T0和T1的中断优先级吗?会有什么样的实验现象呢?
版权声明:本站所有作品(图文、音视频)均由用户自行上传分享,仅供网友学习交流,不声明或保证其内容的正确性,如发现本站有涉嫌抄袭侵权/违法违规的内容。请举报,一经查实,本站将立刻删除。