0

【笔记】中断

 1 year ago
source link: https://en.loli.fj.cn/2022/11/23/%E4%B8%AD%E6%96%AD/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【笔记】中断

2022-11-232022-11-25

中断学习笔记
注意:本文所有代码均按照sdcc编译器代码规范编写,如果使用的Keli编译器,需要自行修改代码

  • 中断:打断CPU当前工作的一种事件
  • 中断源:所有可以引发中断的事件的集合
    • 标准51单片机的中断源(5种/3类)
      • 外部中断:由单片机引脚输入的中断
        • 外部中断0
        • 外部中断1
      • 定时/计数器中断:由定时/计数器引起的中断
        • 定时/计数器0中断
        • 定时/计数器1中断
      • 串行中断:由串行通讯(发送串行数据、接收串行数据)引起的中断
  • 中断向量:中断函数的入口地址
    • 在C51代码中,通常不直接使用中断向量,而是根据头文件中指定的指针变量来使用中断向量

数字信号的种类(4种/2类)

  • 电平信号
    • 高电平信号
    • 低电平信号
  • 边沿信号
    • 上升沿信号
    • 下降沿信号

外部中断请求类型

  • 标准51单片机支持的外部中断请求类型
    • 低电平请求
    • 下降沿请求

STC12C5A60S2使用外部中断

  1. 开中断:STC12C5A60S2的12引脚默认为P3.2端口、13引脚默认为P3.3端口。如果需要开中断,需要将12引脚切换为INT0端口、将13引脚设置为INT1端口
  2. 指定中断请求类型(低电平请求、下降沿请求):虽然标准51单片机支持2种中断请求,但是使用时只能设置1种,每次需要中断时,通过这1种中断请求实现中断
  3. 指定中断处理函数:当检测到中断请求后,立即执行中断处理函数
  • 通过IE寄存器实现开中断
  • IE寄存器是可以位寻址的
  • IE寄存器从第8位(最高位)到第1位(最低位)分别是:EAESET1EX1ET0EX0
    • EA:中断总开(1)关(0)
    • EX0:外部中断0的开(1)关(0)
    • EX1:外部中断1的开(1)关(0)
    • ET0:定时/计数器0的开(1)关(0)
    • ET1:定时/计数器1的开(1)关(0)
    • ES:串行中断的开(1)关(0)
关闭所有中断
通过字节寻址
IE = 0x00;
通过位寻址
EA = 0;
开启外部中断0
通过字节寻址
IE = 0x81;
通过位寻址
EA = 1;
EX0 = 1;
开启外部中断1
通过字节寻址
IE = 0x84;
通过位寻址
EA = 1;
EX1 = 1;
同时开启外部中断0和外部中断1
通过字节寻址
IE = 0x85;
通过位寻址
EA = 1;
EX0 = 1;
EX1 = 1;

指定中断请求类型

  • 通过TCON(Timer Config)寄存器的低4位指定中断请求的类型

  • TCON寄存器是可以位寻址的

  • TCON寄存器从第4位到第1位分别是:IE1IT1IE0IT0

    • IT0:设置外部中断0的请求类型为低电平请求(0)或下降沿请求(1)
    • IT1:设置外部中断1的请求类型为低电平请求(0)或下降沿请求(1)
    • IE0:外部中断0的使能信号。当外部中断0接收到中断信号后,CPU会自动将IE0设置为1;当外部中断0的中断信号处理结束后,CPU会自动将IE0设置为0
    • IE1:外部中断1的使能信号。当外部中断1接收到中断信号后,CPU会自动将IE1设置为1;当外部中断0的中断信号处理结束后,CPU会自动将IE1设置为0
  • 中断按键:将一个普通按键,一端接GND,另一端接INT0或INT1

中断请求类型设置为低电平请求(一个时刻)
  • 这种方式如果中断按键按下时间过长,会导致中断处理函数执行多次
IT0 = 0;
中断请求类型设置为下降沿请求(一个瞬间)
  • 这种方式如果中断按键按下出现抖动,会导致中断处理函数执行多次
IT0 = 1;

指定中断处理函数

  • 无参数列表
  • 无需声明,通常放在整个程序最后
  • 函数名自定义
  • 必须指定中断向量号
  • 中断处理函数不能手动调用

中断向量:用于控制中断的内存地址
中断向量号:用于标记中断的编号,方便编程

interrupt <num>:指定中断向量号,取值范围为0~4

0:外部中断0
1:定时/计数器中断0
2:外部中断1
3:定时/计数器中断1
4:串行终端

void int0(void) interrupt <num>
{
...
}
阻止中断的接收
  • 当一个中断处理函数正在执行时,阻止中断的接收
  • 阻止中断的接收可以用来消除抖动
void int0(void) __interrupt 0
{
// 阻止中断的接收
EX0 = 0;
// 业务代码
...
// 还原中断的接收
EX0 = 1;
}

完整代码示例

  • 利用外部中断0实现中断
#include<8052.h>

void init()
{
// 指定开启外部中断0
IE = 0x81;
// 指定外部中断0的终端请求类型为下降沿请求
IT0 = 1;
}

void main(void)
{
init();
while (1)
{
...
}
}

// 指定外部中断0的中断函数
void int0(void) __interrupt 0
{
// 阻止中断的接收
EX0 = 0;
// 业务代码
...
// 还原中断的接收
EX0 = 1;
}

定时/计数器中断(计数器)

  • 在标准51单片机上有2个16位的定时/计数器

  • 计数值:计数的总次数

  • 计数初值:开始计数的起点

  • 计数最大值:可以计数的上限

    • 51单片机做多可以计16位的数

定时/计数器的计数方式

  • 减计数:从计数初值(计数值)开始减到0。x86的PC使用的就是减计数的计数器
  • 加计数:从计数初值(最大值-计数值)开始加到计数器能力最大值。标准51单片机使用的就是加计数的计数器

定时/计数器的计数内容

  • 定时器:数内部的时基(时间基准)
  • 计数器:数外部的脉冲信号周期(每个周期一高一低)

定时器的时间计算

  • 时钟周期:1个时钟周期 = 1 / 晶振的频率
    • 如果51单片机接了一个12MHz的晶振,那么时钟周期为1/12μs
  • 机器周期:1个机器周期 = 12 * 时钟周期
    • 如果51单片机接了一个12MHz的晶振,那么机器周期为1μs

STC12C5A60S2使用计数器中断

  • 通过TMOD(Timer Model)寄存器指定模式
  • TMOD寄存器是不可以位寻址的
  • TMOD寄存器从第4位到第1位是用来设置定时/计数器0的,分别是:GATE、C/T、M1M0
  • TMOD寄存器从第8位到第5位是用来设置定时/计数器1的,分别是:GATE、C/T、M1M0
    • GATE:门控信号,用来指定定时/计数器的开关的方式,1表示硬件开启,0表示软件开启
    • C/T:1表示工作在计数模式,0表示工作在定时模式
    • M1:用来设置工作方式的高位
    • M0:用来设置工作方式的低位
工作方式 M1 M0
0 0 0
1 0 1
2 1 0
3 1 1

写入计数初值

  • 用2个初值寄存器存放初值
    • 定时/计数器0:高8位用TH0寄存器,低8位用TL0寄存器
    • 定时/计数器1:高8位用TH1寄存器,低8位用TL1寄存器
定时/计数器0
加计数(MCU)
TH0 = (计数最大值 - 计数值) / 256;
TL0 = (计数最大值 - 计数值) % 256;
减计数(CPU)
TH0 = (计数最大值 - 计数值) / 256;
TL0 = (计数最大值 - 计数值) % 256;

开启定时/计数器

  • 通过TCON(Timer Config)寄存器的高4位指定定时/计数器的开启
  • TCON寄存器从第8位到第5位分别是:TF1TR1TF0TR0
    • TR0:定时/计数器0的软开(1)关(0)
    • TR1:定时/计数器1的软开(1)关(0)
    • TF0:定时/计数器的计数溢出标识,标识计数是(1)否(0)已完成
    • TF1:定时/计数器的计数溢出标识,标识计数是(1)否(0)已完成
定时/计数器0
TR0 = 1;
监测是否完成计数
if (TF0)
{
...
}

完整代码示例

  • 通过按键,接收脉冲信号,累计5次开始中断
    • 在计数模式下,需要利用T0T1引脚来接收脉冲信号,初始是高电平
    • 可以将T0T1引脚接按键,实现发出脉冲信号
查询式中断
  • 定时/计数器完成计数后只会改变计数溢出标识,需要自己判断进行中断
  • 进入中断后需要手动将溢出标识置0
#include<8052.h>

void init()
{
// 为定时/计数器0指定模式
// 指定门控信号为软件开启
// 指定工作在计数模式
// 指定工作方式3
TMOD = 0x05;
// 为定时/计数器0指定计数初值
TH0 = (65535 - 5) / 256;
TL0 = (65535 - 5) % 256;
// 为定时/计数器0开启计数
TR0 = 1;
}

void main(void)
{
init();
while (1)
{

// 当定时/计数器0溢出标识为1时开始中断
if (TF0)
{
// 阻止计数
TR0 = 0;

// 业务代码
...

// 重置溢出标识
TF0 = 0;
// 重新指定计数初值
TH0 = (65535 - 5) / 256;
TL0 = (65535 - 5) % 256;
// 恢复计数
TR0 = 1;
}
}
}
中断式中断
  • 定时/计数器完成计数后自动中断
  • 进入中断后溢出标识会自动置0
#include<8052.h>

void init()
{
// 指定开启定时计数器0中断
IE = 0x82;
// 为定时/计数器0指定模式
// 指定门控信号为软件开启
// 指定工作在计数模式
// 指定工作方式3
TMOD = 0x05;
// 为定时/计数器0指定计数初值
TH0 = (65535 - 5) / 256;
TL0 = (65535 - 5) % 256;
// 为定时/计数器0开启计数
TR0 = 1;
}

void main(void)
{
init();
while (1)
{

}
}

void timer0(void) __interrupt 1
{
// 阻止计数
TR0 = 0;

// 业务代码
...

// 重新指定计数初值
TH0 = (65535 - 5) / 256;
TL0 = (65535 - 5) % 256;
// 恢复计数
TR0 = 1;
}

STC12C5A60S2使用定时器中断

#include<8052.h>

void init()
{
// 指定开启定时计数器0中断
IE = 0x82;
// 为定时/计数器0指定模式
// 指定门控信号为软件开启
// 指定工作在定时模式
// 指定工作方式3
TMOD = 0x01;
// 为定时/计数器0指定时间初值(1ms=1000次)
TH0 = (65535 - 50000) / 256;
TL0 = (65535 - 50000) % 256;
// 为定时/计数器0开启计数
TR0 = 1;
}

void main(void)
{
init();
while (1)
{

}
}

void timer0(void) __interrupt 1
{
// 阻止计数
TR0 = 0;

// 业务代码
...

// 重新指定计数初值
TH0 = (65535 - 50000) / 256;
TL0 = (65535 - 50000) % 256;
// 恢复计数
TR0 = 1;
}

Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK