版权声明:本文为博主原创文章,转载请附上原文出处链接。
开始做 定时器/计数器 实验啦!先介绍原理再开始实验。
定时器(timer)几乎是每个MCU必有的重要外设之一,可用于定时、精确延时、计数等等,在检测、控制领域有广泛应用。
定时器运行时不占用CPU时间,配置好之后,可以与CPU并行工作,实现精确的定时和计数,并且可以通过软件控制其是否产生中断,使用起来灵活方便。通常MCU在介绍定时器外设时,总是和计数器(counter)一起出现,所以我们有必要先了解一下定时器和计数器的区别。
定时器和计数器实际都是通过计数器来计数,定时器是对周期不变的脉冲计数(一般来自于系统时钟),由计数的个数和脉冲的周期即可计算出时间,同时,通过一个给定的预期值(即比较值,对应预期的计数值,也就是预期时间),当计数值达到预期值时产生中断,这样就实现了定时,应用程序通过设置不同的预期值实现不同时长的定时。
计数器是对某一事件进行计数,这个事件每发生一次,计数值加/减1,而这个事件的产生可能是没有规律的。也就是计数器的用途是对事件的发生次数进行计数,由计数值来反映事件产生的次数。
☆注:不同MCU定时器外设使用的计数器位数是个重要的参数,STC8A8K64S4A12系列MCU的定时器使用的是16位计数器(由定时器高8位寄存器和定时器低8位寄存器组合起来实现)。
STC8A8K64S4A12系列单片机有5个16位的定时器/计数器,即定时器/计数器T0、定时器/计数器T1、定时器/计数器T2、定时器/计数器T3、定时器/计数器T4。
这些定时器/计数器外设有2种工作方式:定时方式(定时器)和计数方式(计数器)。可通过特殊功能寄存器设置相应的C/T控制位,来选择定时器/计数器工作在哪种方式。
定时器/计数器外设的核心部件是一个加法计数器,其本质是对脉冲信号进行计数。不同的是,定时器的脉冲信号来自于系统时钟,而计数器的脉冲信号来自于单片机特定外部输入引脚。STC8A8K64S4A12系列单片机计数器特定外部输入引脚如下表。
Tx | INT | 对应IO口 | 功能描述 | 说明 | 备注 |
---|---|---|---|---|---|
T1 | P3.4 | P3.2 | 计数器0外部输入引脚 | 非独立GPIO | LCD12864/LCD1602屏接口 |
T2 | P3.5 | P3.3 | 计数器1外部输入引脚 | 非独立GPIO | LCD12864/LCD1602屏接口 |
T3 | P1.2 | P3.6 | 计数器2外部输入引脚 | 非独立GPIO | nRF24L01P模块接口 |
T4 | P0.4 | P3.7 | 计数器3外部输入引脚 | 非独立GPIO | nRF24L01P模块接口 |
T5 | P0.6 | P3.0 | 计数器4外部输入引脚 | 非独立GPIO | 电位器ADC采样 |
☆注:独立GPIO表示开发板没有其他的电路使用这个GPIO,非独立GPIO说明开发板有其他电路用到了该GPIO。针对非独立GPIO使用时需特别注意。
STC8A8K64S4A12系列单片机定时器/计数器通过相关寄存器的C/T位选择计数器输入脉冲信号来源,也即选择了工作方式是定时方式还是计数方式。后配置相关寄存器位控制输入脉冲信号至计数器,计数器溢出后产生中断,也可通过特定引脚输出产生溢出时钟。定时器/计数器结构原理示意图如下。
☆注:12T模式是选择对系统时钟12分频,1T模式是选择对系统时钟不分频。另外,该计数器是递增计数器,不具有递减功能。
■ 定时器/计数器工作在定时方式(定时器)时:
TxCLKO | 对应IO口 | 功能描述 | 说明 | 备注 |
---|---|---|---|---|
T0CLKO | P3.5 | T0时钟输出引脚 | 非独立GPIO | LCD12864/LCD1602屏接口 |
T1CLKO | P3.4 | T1时钟输出引脚 | 非独立GPIO | LCD12864/LCD1602屏接口 |
T2CLKO | P1.3 | T2时钟输出引脚 | 非独立GPIO | nRF24L01P模块接口 |
T3CLKO | P0.5 | T3时钟输出引脚 | 非独立GPIO | 用户按键KEY4 |
T4CLKO | P0.7 | T4时钟输出引脚 | 非独立GPIO | 用户按键KEY3 |
☆注:独立GPIO表示开发板没有其他的电路使用这个GPIO,非独立GPIO说明开发板有其他电路用到了该GPIO。针对非独立GPIO使用时需特别注意。
■ 定时器/计数器工作在计数方式(计数器)时:
STC8A8K64S4A12系列MCU不同的定时器/计数器所具有的可供选择的工作模式不同,定时器/计数器0和定时器/计数器1有多种模式可供选择,选择模式是通过寄存器TMOD对应的M0位和M1位实现。定时器/计数器2、定时器/计数器3和定时器/计数器4只有默认的一种工作模式。具体如下面列表所示。
序号 | 定时器/计数器工作模式 | T0 | T1 | T2 | T3 | T4 |
---|---|---|---|---|---|---|
1 | 模式0:16位自动重装载模式 | 有 | 有 | 有 | 有 | 有 |
2 | 模式1:16位不可重装载模式 | 有 | 有 | |||
3 | 模式2:8位自动重装载模式 | 有 | 有 | |||
4 | 模式3:不可屏蔽16位自动重装载模式 | 有 |
☆注:模式0是STC官方推荐学习的定时器/计数器工作模式,也是我们讲解的重点。当T0工作在模式3,T0可作为实时操作系统用节拍定时器。
下面介绍下定时器/计数器0和定时器/计数器1使用时非常重要的一个寄存器TMOD。
☆注:定时器/计数器的门控位在一些特殊应用中会有使用,一般情况下是将门控位置0。
■ 定时器/计数器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分析。
1)定时器/计数器2、定时器/计数器3和定时器/计数器4不存在门控位GATE,对定时器/计数器的控制完全由TxR(定时器运行控制位)决定。
2)定时器/计数器2、定时器/计数器3和定时器/计数器4有且只有一种模式,即16位重装载模式,实现重装载的原理请参考对定时器/计数器0和定时器/计数器的分析。
STC8A8K64S4A12系列单片机作为定时器使用时,由于计数脉冲的周期是固定的,所以溢出前的脉冲数乘以脉冲周期就是定时时间,或者称定时溢出时间。
下面是定时器溢出时间计算公式:
☆注:公式中的分频因子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的定时器计算器)。
针对STC8A8K64S4A12系列单片机5个定时器/计数器外设,软件的配置过程如下:
☆注:实验例程即是按照上述配置步骤操作寄存器相关位实现,后有详述。
STC8A8K64S4A12系列单片机操作定时器/计数器时会用到18个寄存器,如下表所示:
序号 | 寄存器名 | 读/写 | 功能描述 |
---|---|---|---|
1 | TCON | 读/写 | 定时器/计数器中断控制寄存器。 |
2 | TMOD | 读/写 | 定时器/计数器模式寄存器。 |
3 | AUXR | 读/写 | 辅助寄存器1。 |
4 | INTCLKO | 读/写 | 中断与时钟输出控制寄存器。 |
5 | IE | 读/写 | 中断允许寄存器1。 |
6 | IE2 | 读/写 | 中断允许寄存器2。 |
7 | IP | 读/写 | 中断优先级寄存器1。 |
8 | T4T3M | 读/写 | T3和T4控制寄存器。 |
9 | TL0 | 读/写 | 定时器/计数器0低8位寄存器。 |
10 | TH0 | 读/写 | 定时器/计数器0高8位寄存器。 |
11 | TL1 | 读/写 | 定时器/计数器1低8位寄存器。 |
12 | TH1 | 读/写 | 定时器/计数器1高8位寄存器。 |
13 | T2L | 读/写 | 定时器/计数器2低8位寄存器。 |
14 | T2H | 读/写 | 定时器/计数器2高8位寄存器。 |
15 | T3L | 读/写 | 定时器/计数器3低8位寄存器。 |
16 | T3H | 读/写 | 定时器/计数器3高8位寄存器。 |
17 | T4L | 读/写 | 定时器/计数器4低8位寄存器。 |
18 | T4H | 读/写 | 定时器/计数器4高8位寄存器。 |
☆注:上述寄存器有部分不单单是用于定时器/计数器外设中的,比如寄存器TCON、IE、IE2等。
外部中断允许寄存器IE支持位寻址,该寄存器的B1和B3位是定时器/计数器0和定时器/计数器1的中断允许位。
中断允许寄存器IE2不支持位寻址,该寄存器的B2、B5和B6位是定时器/计数器2、定时器/计数器3和定时器/计数器4的中断允许位。因为IE2寄存器不支持位寻址,所以举例操作该寄存器B2位时,不可以直接“ET2=0;”进行操作,参考下图。
定时器/计数器中断控制寄存器TCON支持位寻址,该寄存器的B4位和B6位是T0和T1的运行控制位,寄存器B5位和B7位是T0和T1的溢出中断标志,含义如下图。
☆注:TCON寄存器的低4个位是用于外部中断的,在操作该寄存器时一定要按位操作,用不到的位不要操作。
辅助寄存器AUXR不支持位寻址,该寄存器的B6和B7位是定时器/计数器1和定时器/计数器0的速度控制位,寄存器的B1、B2和B3位是定时器/计数器2的速度控制位、工作方式选择位和允许控制位。
☆注:AUXR寄存器的B0和B5位在串口外设配置时可能会用到。
中断与时钟输出控制寄存器INTCLKO不支持位寻址,该寄存器的B0、B1和B2位是定时器/计数器0、定时器/计数器1和定时器/计数器2的时钟输出引脚控制位。
☆注:INTCLKO寄存器的定时器时钟输出功能和定时器工作模式及工作方式(定时还是计数)密切相关,用户暂只需知道有这些关系,待项目应用时再分析频率关系。
寄存器T4T3M不支持位寻址,该寄存器的B0到B3位是定时器/计数器3配置过程中会用到的位,寄存器的B4到B7位是定时器/计数器4配置过程中会用到的位。
中断优先级控制寄存器IP支持位寻址,该寄存器的B1位和B3位是设置T0和T1中断优先级的,含义如下图。需要说明的是T2、T3和T4是没有中断优先级的。
☆注:本节的实验源码是在“实验2-4-1:外部中断0(下降沿中断方式)”的基础上修改。本节对应的实验源码是:“实验2-5-1:定时器0定时”。
本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。
序号 | 文件名 | 后缀 | 功能描述 |
---|---|---|---|
1 | led | .c | 包含与用户led控制有关的用户自定义函数 |
2 | timer | .c | 外部定时器有关的用户自定义函数 |
3 | delay | .c | 包含用户自定义延时函数 |
■ 需要引用的头文件
#include "delay.h"
#include "led.h"
#include "timer.h"
■ 需要包含的头文件路径
本例需要包含的头文件路径如下表:
序号 | 路径 | 描述 |
---|---|---|
1 | …\ Source | led.h、timer.h和delay.h头文件在该路径,所以要包含 |
2 | …\User | STC8.h头文件在该路径,所以要包含 |
STC8.h头文件在该路径,所以要包含
首先,在timer.c文件中编写定时器0的初始化函数Timer0Init,代码如下。
程序清单:定时器0初始化函数
/***********************************************************
功能描述:定时器0初始化
入口参数:无
返回值:无
************************************************************/
void Timer0Init(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开始计时
}
然后,编写定时器0中断服务函数,一旦响应中断达到一定次数会执行翻转用户指示灯D3的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:定时器0中断服务程序
入口参数:无
返回值:无
************************************************************/
void timer0_int (void) interrupt 1
{
cnt++; //5ms进入1次中断
if(cnt == 200) //200次中断被响应后,正好1000ms
{
led_toggle(LED_3); //翻转用户指示灯D3
cnt = 0;
}
//进入中断时会将定时器中断溢出标志位硬件清零,因此下面一句可以不加的
TF0 = 0; //清除T0中断溢出标志位
}
最后,在主函数中调用T0初始化函数,开启总中断,而主循环中没有任务,说明用户指示灯D3变化来自于中断。
代码清单:主函数
int main(void)
{
Timer0Init(); //定时器0初始化
EA = 1; //使能总中断
while (1)
{
; //无任务,说明LED亮灭来自于中断
}
}
==☆注:本节的实验源码是在“实验2-5-1:定时器0定时”的基础上修改。本节对应的实验源码是:“实验2-5-2:定时器1定时”。 ==
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-5-1:定时器0定时”部分。
首先,在timer.c文件中编写定时器1的初始化函数Timer1Init,代码如下。
程序清单:定时器1初始化函数
/***********************************************************
功能描述:定时器1初始化
入口参数:无
返回值:无
************************************************************/
void Timer1Init(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中断服务函数,一旦响应中断达到一定次数会执行翻转蓝色指示灯D3的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:定时器1中断服务程序
入口参数:无
返回值:无
************************************************************/
void timer1_int (void) interrupt TIMER1_VECTOR
{
cnt++; //5ms进入1次中断
if(cnt == 200) //200次中断被响应后,正好1000ms
{
led_toggle(LED_3); //翻转用户指示灯D3
cnt = 0;
}
//进入中断时会将定时器中断溢出标志位硬件清零,因此下面一句可以不加的
TF1 = 0; //清除T1中断溢出标志位
}
最后,在主函数中调用T1初始化函数,开启总中断,而主循环中没有任务,说明用户指示灯D3变化来自于中断。
代码清单:主函数
int main(void)
{
Timer1Init(); //定时器1初始化
EA = 1; //使能总中断
while (1)
{
; //无任务,说明LED亮灭来自于中断
}
}
==☆注:本节的实验源码是在“实验2-5-1:定时器0定时”的基础上修改。本节对应的实验源码是:“实验2-5-3:定时器2定时”。 ==
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-5-1:定时器0定时”部分。
首先,在timer.c文件中编写定时器2的初始化函数Timer2Init,代码如下。
程序清单:定时器2初始化函数
/***********************************************************
功能描述:定时器2初始化
入口参数:无
返回值:无
************************************************************/
void Timer2Init(void)
{
AUXR &= 0xF7; //定时器2设置为定时方式
AUXR |= 0x04; //设置定时器2为1T模式
T2L = 0x00; //1T模式下初始装载值
T2H = 0x28; //1T模式下初始装载值
IE2 |= 0x04; //使能定时器2中断
AUXR |= 0x10; //打开定时器2
}
然后,编写定时器2中断服务函数,一旦响应中断达到一定次数会执行翻转蓝色指示灯D3的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:定时器2中断服务程序
入口参数:无
返回值:无
************************************************************/
void timer2_int (void) interrupt TIMER2_VECTOR
{
cnt++; //5ms进入1次中断
if(cnt == 200) //200次中断被响应后,正好1000ms
{
led_toggle(LED_3); //翻转用户指示灯D3
cnt = 0;
}
}
最后,在主函数中调用T2初始化函数,开启总中断,而主循环中没有任务,说明用户指示灯D3变化来自于中断。
代码清单:主函数
int main(void)
{
Timer2Init(); //定时器2初始化
EA = 1; //使能总中断
while (1)
{
; //无任务,说明LED亮灭来自于中断
}
}
==☆注:本节的实验源码是在“实验2-5-1:定时器0定时”的基础上修改。本节对应的实验源码是:“实验2-5-4:定时器3定时”。 ==
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-5-1:定时器0定时”部分。
首先,在timer.c文件中编写定时器3的初始化函数Timer2Init,代码如下。
程序清单:定时器3初始化函数
/***********************************************************
功能描述:定时器3初始化
入口参数:无
返回值:无
************************************************************/
void Timer3Init(void)
{
T4T3M &= 0xFB; //定时器3设置为定时方式
T4T3M |= 0x02; //设置定时器3为1T模式
T3L = 0x00; //1T模式下初始装载值
T3H = 0x28; //1T模式下初始装载值
IE2 |= (1<<5); //使能定时器3中断
T4T3M |= 0x08; //打开定时器3
}
然后,编写定时器3中断服务函数,一旦响应中断达到一定次数会执行翻转蓝色指示灯D3的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:定时器3中断服务程序
入口参数:无
返回值:无
************************************************************/
void timer3_int (void) interrupt TIMER3_VECTOR
{
cnt++; //5ms进入1次中断
if(cnt == 200) //200次中断被响应后,正好1000ms
{
led_toggle(LED_3); //翻转用户指示灯D3
cnt = 0;
}
}
最后,在主函数中调用T3初始化函数,开启总中断,而主循环中没有任务,说明用户指示灯D3变化来自于中断。
代码清单:主函数
int main(void)
{
Timer3Init(); //定时器3初始化
EA = 1; //使能总中断
while (1)
{
; //无任务,说明LED亮灭来自于中断
}
}
==☆注:本节的实验源码是在“实验2-5-1:定时器0定时”的基础上修改。本节对应的实验源码是:“实验2-5-5:定时器4定时”。 ==
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-5-1:定时器0定时”部分。
首先,在timer.c文件中编写定时器4的初始化函数Timer4Init,代码如下。
程序清单:定时器4初始化函数
/***********************************************************
功能描述:定时器4初始化
入口参数:无
返回值:无
************************************************************/
void Timer4Init(void)
{
T4T3M &= 0xBF; //定时器4设置为定时方式
T4T3M |= 0x20; //设置定时器4为1T模式
T4L = 0x00; //1T模式下初始装载值
T4H = 0x28; //1T模式下初始装载值
IE2 |= (1<<6); //使能定时器4中断
T4T3M |= 0x80; //打开定时器4
}
然后,编写定时器4中断服务函数,一旦响应中断达到一定次数会执行翻转蓝色指示灯D3的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:定时器4中断服务程序
入口参数:无
返回值:无
************************************************************/
void timer4_int (void) interrupt TIMER4_VECTOR
{
cnt++; //5ms进入1次中断
if(cnt == 200) //200次中断被响应后,正好1000ms
{
led_toggle(LED_3); //翻转用户指示灯D3
cnt = 0;
}
}
最后,在主函数中调用T4初始化函数,开启总中断,而主循环中没有任务,说明用户指示灯D3变化来自于中断。
代码清单:主函数
int main(void)
{
Timer4Init(); //定时器4初始化
EA = 1; //使能总中断
while (1)
{
; //无任务,说明LED亮灭来自于中断
}
}
==☆注:本节的实验源码是在“实验2-5-1:定时器0定时”的基础上修改。本节对应的实验源码是:“实验2-5-6:多个定时器定时”。 ==
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-5-1:定时器0定时”部分。
首先,在timer.c文件中编写定时器0和定时器1的初始化函数Timer0Init和Timer1Init,代码如下。
程序清单:定时器0和定时器1初始化函数
/***********************************************************
功能描述:定时器0初始化
入口参数:无
返回值:无
************************************************************/
void Timer0Init(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初始化
入口参数:无
返回值:无
************************************************************/
void Timer1Init(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开始计时
}
然后,编写定时器0和定时器1的中断服务函数,一旦响应中断达到一定次数会执行翻转对应用户指示灯的操作,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:定时器0中断服务程序
入口参数:无
返回值:无
************************************************************/
void timer0_int (void) interrupt TIMER0_VECTOR
{
cnt1++; //5ms进入1次中断
if(cnt1 == 100) //100次中断被响应后,正好500ms
{
led_toggle(LED_3); //翻转用户指示灯D3
cnt1 = 0;
}
//进入中断时会将定时器中断溢出标志位硬件清零,因此下面一句可以不加的
TF0 = 0; //清除T0中断溢出标志位
}
/***********************************************************
功能描述:定时器1中断服务程序
入口参数:无
返回值:无
************************************************************/
void timer1_int (void) interrupt TIMER1_VECTOR
{
cnt2++; //5ms进入1次中断
if(cnt2 == 200) //200次中断被响应后,正好1000ms
{
led_toggle(LED_4); //翻转用户指示灯D4
cnt2 = 0;
}
//进入中断时会将定时器中断溢出标志位硬件清零,因此下面一句可以不加的
TF1 = 0; //清除T1中断溢出标志位
}
最后,在主函数中调用T0和T1初始化函数,开启总中断,而主循环中没有任务,蓝色指示灯D3和蓝色指示灯D4变化来自于中断。
代码清单:主函数
int main(void)
{
Timer0Init(); //定时器0初始化
Timer1Init(); //定时器1初始化
EA = 1; //使能总中断
while (1)
{
; //无任务,说明LED亮灭来自于中断
}
}