51单片机学习笔记8 中断系统及定时器
- 一、中断的概念
- 二、51单片机的中断
- 1. 51单片机的中断源
- 2. 中断的优先级
- 3. 中断结构
- 4. 外部中断解读
- 5. 定时器中断
- 6. 串口中断
- 三、中断相关寄存器
- 1. IE 中断允许寄存器
- 2. TCON 中断请求标志
- 3. IP 中断优先级
- 四、中断号
- 五、代码实现
- 按键 :
- LED:
- 1. 按键事件切换LED亮的状态
- key_utils.c
- main.c
- 六、定时/计数器结构
- 1. 寄存器介绍
- TMOD
- TCON
- 2. 定时器/计数器工作方式0
- (1)工作特点
- (2)门控位说明
- (3)C/T
- 3. 工作方式1
- 4. 工作方式2
- 5. 工作方式3
- 七、定时器代码实现
- 1. 定时器配置步骤
- 2. 计时器初值计算
- 3. 示例
- `timer_utils.c`
- main.c
一、中断的概念
中断是一种事件驱动的机制,允许单片机在执行程序的过程中暂时中断当前的任务,转而处理来自外部的优先级更高的事件。
当中断事件发生时,单片机会立即跳转到中断服务程序(ISR),执行相关的处理代码,然后返回到原来的程序继续执行。
二、51单片机的中断
1. 51单片机的中断源
51单片机中的中断源可以是外部硬件引脚的电平变化(外部中断),也可以是单片机内部的定时器/计数器溢出、串口接收等(内部中断)。
不同的51单片机中断源可能有所不同。一般51单片机至少有 5个中断:外部中断0、定时器0中断、外部中断1、定时器1中断、串口中断。
而STC89C51RC/RD+
的系列单片机提供了8个中断请求源,分别是:
- 外部中断0
- 定时器0 中断
- 外部中断1
- 定时器1 中断
- 串口中断
- 定时器2中断
- 外部中断2
- 外部中断3
在 中颖的SH79F6442
中,提供了27个中断源,其中有5个外部中断、3个定时器中断,另外还有4个PCA中断、4个EUART中断、系统时钟监控中断、1个SPI中断、ADC中断、3路PWM中断、LED中断、TWI中断、CRC中断、LPD中断。
2. 中断的优先级
51单片机中,不同中断源之间存在优先级关系,当多个中断同时发生时,优先级高的中断会先得到响应。
可以通过设置中断优先级和中断允许位来控制不同中断源之间的优先级和允许状态。
下图是来自《STC89Cxx中文参考手册.pdf》 里的中断说明:
3. 中断结构
下图也是摘自《STC89Cxx中文参考手册》,其结构
上图的解读,整体是以列的方式从左往右看,数据从最左侧是中断源,经过中断允许控制寄存器(开关),到中断优先级控制寄存器,再流向处理程序。
靠上的中断优先级更高。
4. 外部中断解读
- INT0对应P3.2引脚
- INT1对应P3.3引脚
中断信号传递流程:
- INT0 中断信号通过
IT0
(TCON控制寄存器第0位)引脚,来选择触发方式(0:低电平触发或 1:下降沿触发); - 触发中断后,将
IE0
(TCON寄存器第1位,中断标志)置1; - 判断
EX0
(IE中断允许寄存器第0位)如果是1,就是INT0使能,中断可以继续向后传递; - 如果全局总中断
EA
(IE中断允许寄存器第7位)打开,中断可以继续传递; - 中断优先级控制寄存器
IP
判断哪个中断先触发; - 触发CPU的中断服务函数。
从上面的流程可以看出,IE.EX0即使失能状态,也可以通过TCON.IE0读取中断信号。
外部中断INT1对应P3.3引脚,工作流程与INT0类似。
5. 定时器中断
- T0对应 P3.4引脚
- T1对应 P3.5 引脚
- 有计数信号进入T0;
- 计数溢出时,触发 TF0(TCON计数器寄存器,TF0溢出中断标志);
- 如果
ET0
(IE中断第1位,T0的溢出中断允许位),继续传递; - 如果全局总中断
EA
(IE中断允许寄存器第7位)打开,继续传递; - 中断优先级控制器
IP
判断优先级; - 触发CPU中断服务函数。
6. 串口中断
- RX 对应 P3.0引脚
- TX 对应 P3.1 引脚
- 当接收到数据或发送完成时,串口硬件会设置RI/TI标志位,表示接收到了数据;
- 如果 ES(IE寄存器的第4位,串行口中断允许位)被设置,允许串口中断继续传递;
- 如果全局总中断 EA(IE寄存器的第7位,全局中断允许位)被打开,则允许中断继续传递;
- 中断优先级控制器
IP
判断优先级; - 触发中断服务函数;
三、中断相关寄存器
1. IE 中断允许寄存器
IE 地址为 A8H,格式如下:
位 | 编号 | 作用 |
EA | 7 | 全局中断允许位 |
– | 6 | 无效位,保留 |
ET2 | 5 | 定时器/计数器2中断允许位 |
ES | 4 | 串行口中断允许位 |
ET1 | 3 | 定时器/计数器1中断允许位 |
EX1 | 2 | 外部中断1允许位 |
ET0 | 1 | 定时器/计数器0中断允许位 |
EX0 | 0 | 外部中断0允许位 |
2. TCON 中断请求标志
位 | 编号 | 作用 |
TF1 | 7 | 定时器/计数器1溢出标志位 |
TR1 | 6 | 定时器/计数器1运行控制位 |
TF0 | 5 | 定时器/计数器0溢出标志位 |
TR0 | 4 | 定时器/计数器0运行控制位 |
IE1 | 3 | 外部中断1标志位 |
IT1 | 2 | 外部中断1触发方式选择位 |
IE0 | 1 | 外部中断0标志位 |
IT0 | 0 | 外部中断0触发方式选择位 |
3. IP 中断优先级
位 | 编号 | 作用 |
– | 7 | 无效位,保留 |
PT2 | 6 | 定时器/计数器2中断优先级位 |
PS | 5 | 串行口中断优先级位 |
PT1 | 4 | 定时器/计数器1中断优先级位 |
PX1 | 3 | 外部中断1优先级位 |
PT0 | 2 | 定时器/计数器0中断优先级位 |
PX0 | 1 | 外部中断0优先级位 |
– | 0 | 无效位,保留 |
这里对寄存器简单介绍,更详细地说明可以参考本文对应的开源代码里的《STC89Cxx中文参考手册》。
四、中断号
下面是关于中断号的说明:
中断号 | 描述 |
0 | 外部中断0 |
1 | 定时器/计数器0 溢出中断 |
2 | 外部中断1 |
3 | 定时器/计数器1 溢出中断 |
4 | 串行口通信中断 |
5 | 定时器/计数器2 溢出中断 |
6-7 | 保留 |
五、代码实现
硬件连接:
按键 :
LED:
其中按键3是在P3.2引脚,中断0。
1. 按键事件切换LED亮的状态
key_utils.c
#include "key_utils.h"
#include <reg52.h>
#include "common_utils.h"
sbit KEY3 = P3^2;
sbit LED1 = P2^0;
/**
* @brief 按键中断初始化
*/
void key3_init(void){
IT0 = 1; // 下降沿触发
EX0 = 1; // 开启外部中断0
EA = 1; // 开启总中断
}
void exti0() interrupt 0{
delay_10us(500);
if(KEY3 == 0){
LED1 = !LED1;
}
}
main.c
#include <reg52.h>
#include "led_utils.h"
#include "common_utils.h"
#include "key_utils.h"
#include "types.h"
/**
* @brief 主函数
*/
main()
{
// 关闭所有led
led_all_off();
key3_init();
while(1)
{
}
}
六、定时/计数器结构
- T0:P3.4
- T1:P3.5 引脚
- TMOD:定时器的工作模式
- TCON:低4位控制外部中断,高4位用于定时器控制位
1. 寄存器介绍
TMOD
TMOD用于控制定时器/计数器的工作模式和计数方式,寄存器各位功能:
Bit: 7 6 5 4 3 2 1 0
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│ GATE │ C/T │ M1 │ M0 │ GATE │ C/T │ M1 │ M0 │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
T1 T1 T0 T0 T1 T1 T0 T0
TMOD 寄存器各位的功能:
分成2组,高4位、低4位,分别控制定时器1、定时器0的工作模式。
- 位7:GATE1(定时器1门控位):当 GATE1 为 1 时,定时器1的计数/定时工作由外部引脚控制。当 GATE1 为 0 时,定时器1的计数/定时工作受到内部控制。
- 位6:C/T1(定时器1计数/定时选择位):当 C/T1 为 0 时,定时器1为定时器模式;当 C/T1 为 1 时,定时器1为计数器模式。
- 位5-4:M11 和 M10(定时器1工作模式位):用于设置定时器1的工作模式,共有四种工作模式,分别为:
- 00:13位定时器模式
- 01:16位定时器模式
- 10:8位自动重装载定时器模式
- 11:两个8位定时器/计数器分开工作
- 位3:GATE0(定时器0门控位):与位7相似,用于控制定时器0的门控功能。
- 位2:C/T0(定时器0计数/定时选择位):与位6相似,用于设置定时器0的工作模式。
- 位1-0:M01 和 M00(定时器0工作模式位):与位5-4相似,用于设置定时器0的工作模式。
TCON
TCON 用于控制定时器/计数器的工作状态和中断标志位,各位功能:
Bit: 7 6 5 4 3 2 1 0
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│ TF1 │ TR1 │ TF0 │ TR0 │ IE1 │ IT1 │ IE0 │ IT0 │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
TCON 寄存器各位的功能:
- 位7:TF1(定时器1溢出标志位):当定时器1溢出时,TF1 置1,表示定时器1计数达到最大值并溢出。如果不使用中断,也可以通过查询知道是否溢出。
- 位6:TR1(定时器1运行控制位):当 TR1 置1 时,定时器1开始工作;当 TR1 清0 时,定时器1停止工作。
- 位5:TF0(定时器0溢出标志位):当定时器0溢出时,TF0 置1,表示定时器0计数达到最大值并溢出。
- 位4:TR0(定时器0运行控制位):当 TR0 置1 时,定时器0开始工作;当 TR0 清0 时,定时器0停止工作。
- 位3:IE1(外部中断1标志位):外部中断1的中断标志位,当外部中断1触发时,IE1 置1。
- 位2:IT1(外部中断1触发方式选择位):外部中断1的触发方式选择位,控制外部中断1的触发方式。
- 位1:IE0(外部中断0标志位):外部中断0的中断标志位,当外部中断0触发时,IE0 置1。
- 位0:IT0(外部中断0触发方式选择位):外部中断0的触发方式选择位,控制外部中断0的触发方式。
2. 定时器/计数器工作方式0
(1)工作特点
也称为13位定时器模式,结构图如下:
其特点是:
- 定时器/计数器的计数范围为0到8191(2^13-1)。
- 该模式下,定时器/计数器每计数一次,计数值加1,直到达到最大计数值(8191),然后溢出并重新从0开始计数。
计数由TL0的低5位和TH0的高8位组成,低5位溢出时向TH0进位,TH0 溢出时,TCON.TF0 置位。
(2)门控位说明
- 门控位GATE是0的时候,经过一个非门、或门、与门,定时器可以由 TR0控制。
- 当GATE是1的时候,定时器由INT0 引脚控制。
(3)C/T
控制计数还是定时模式,即开关:
3. 工作方式1
工作方式1称为16位定时器模式:
- 定时器/计数器的计数范围为0到65535(2^16-1)。
- 该模式下,定时器/计数器每计数一次,计数值加1,直到达到最大计数值(65535),然后溢出并重新从0开始计数。
4. 工作方式2
- 也称为8位自动重装载定时器模式,定时器/计数器的计数范围为0到255(2^8-1)。
- 该模式下,定时器/计数器每计数一次,计数值加1,当计数值达到最大值(255)时,自动重新加载初始计数值。
- 自动重新装载,是将TH0装载到TL0。
5. 工作方式3
工作方式只适应用于T0,也称为两个8位定时器/计数器分开工作模式,其特点:
- 两个8位定时器/计数器分开工作,分别为定时器0和定时器1,每个定时器/计数器的计数范围为0到255(2^8-1)。
- 可以独立控制每个定时器/计数器的工作模式、计数方式和中断功能。
七、定时器代码实现
1. 定时器配置步骤
- 选择定时器
- 选择工作模式
- 设置定时器初值
- 选择时钟源
- 中断使能
- TR0或TR1置位,启动定时器
- 中断处理
2. 计时器初值计算
使用开源仓库里的51定时器计算工具计算,晶震频率根据开发板实际频率填写。
3. 示例
本示例每秒LED闪烁一次:
timer_utils.c
#include "timer_utils.h"
#include <reg51.h>
#include "types.h"
sbit LED1 = P2^0;
/**
* @brief 定时器 0 初始化函数
*/
void timer1_init(void){
// 选择为定时器 0 模式,工作方式 1
TMOD |= 0X01;
// 给定时器赋初值,定时 1ms
TH0 = 0XFC;
TL0 = 0X66;
// 打开定时器 0 中断允许
ET0 = 1;
// 打开总中断
EA = 1;
// 打开定时器
TR0 = 1;
}
// 定义静态变量 i
static u16 i;
/**
* @brief 定时器 0 中断函数
*/
void timer1() interrupt 1
{
// 定时器重新赋初值,定时 1ms
TH0 = 0XFC;
TL0 = 0X66;
i++;
if(i == 1000)
{
i =0 ;
LED1 = !LED1;
}
}
main.c
#include <reg52.h>
#include "led_utils.h"
#include "common_utils.h"
#include "types.h"
#include "timer_utils.h"
/**
* @brief 主函数
*/
main()
{
// 关闭所有led
led_all_off();
time0_init();
while(1)
{
}
}
本文开源地址:
https://gitee.com/xundh/learn51