良许Linux教程网 干货合集 单片机中断程序,如何被中断?

单片机中断程序,如何被中断?

如果外部中断请求的频率足够快,上一个中断没有处理完成,新的中断请求又来了,该如何处理呢?

一般来说,中断是由硬件(例如外设、外部引脚)产生的。当某种内部或者外部事件发生时,MCU的中断系统会迫使CPU暂停正在执行的程序,转而去执行中断事件的处理。中断处理完毕后,又会返回到被中断的程序处,继续执行下去。所有的Cortex-M内核系统都有一个用于中断处理的组件,即NVIC(Nested Vectored Interrupt Controller),它主要负责处理中断,同时也处理其他需要服务的事件。

在处理器中,NVIC能够处理多个可屏蔽中断通道和可编程优先级。中断输入请求可以是电平触发,也可以是最小的一个时钟周期的脉冲信号。每一个外部中断线都可以独立地使能、清除或挂起,并且挂起状态也可以手动地设置和清除。

当主程序正在执行时,如果遇到中断请求(Interrupt Request),则会暂停主程序的执行,转而去执行中断服务例程(Interrupt Service Routine,ISR),这称为中断响应。中断服务例程执行完毕后,程序会返回到主程序断点处,并继续执行主程序。如果外部中断请求频率很高,可能会出现上一个中断还没有处理完成,新的中断请求又来了的情况。在这种情况下,可以使用嵌套中断的方式进行处理。即正在执行的较低优先级中断可以被较高优先级的中断所打断,然后执行高等级中断的服务例程,再返回到低优先级中断中继续执行,这种机制通常称为“咬尾中断”。

内核中断(异常管理和休眠模式等),其中断优先级则由SCB寄存器来管理,IRQ的中断优先级是由NVIC来管理。

NVIC的寄存器经过了存储器映射,其寄存器的起始地址为0xE000E100,对其访问必须是每次32bit。

SCB寄存器的起始地址:0xE000ED00,也是每次32bit访问,SCB寄存器主要包含SysTick操作、异常管理和休眠模式控制。

NVIC具有以下特性:

  • 灵活的中断管理:使能\清除、优先级配置
  • 硬件嵌套中断支持
  • 向量化的异常入口
  • 中断屏蔽

1. 中断使能和清除使能


arm将处理器的中断使能设置和清除设置寄存器分在两个不同的地址,这种设计主要有如下优势:一方面这种方式减少了使能中断所需要的步骤,使能一个中断NVIC只需要访问一次,同时也减少了程序代码并且降低了执行时间,另一方面当多个应用程序进程同时访问寄存器或者在读写操作寄存器时有操作其他的中断使能位,这样就有可能导致寄存器丢失,设置和清除分成两个寄存器能够有效防止控制信号丢失。

image-20231108221936198
image-20231108221936198

因此我可以独立的操作每一个中断的使能和清除设置。

1.1 C代码
*(volatile unsigned long) (0xE000E100) = 0x4 ; //使能#2中断
*(volatile unsigned long) (0xE000E180) = 0x4 ; //清除#2中断
1.2 汇编代码
__asm void Interrupt_Enable()
{
 LDR R0, =0xE000E100  ;  //ISER寄存器的地址
 MOVS R1, #04         ;  //设置#2中断
 STR R1, [R0]         ;  //使能中断#2
}

__asm void Interrupt_Disable()
{
 LDR R0, =0xE000E180  ;  //ICER寄存器的地址
 MOVS R1, #04         ;  //设置#2中断
 STR R1, [R0]         ;  //使能中断#2
}
1.3 CMSIS标准设备驱动函数
//使能中断#IRQn
__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) 
{
    if ((int32_t)(IRQn) >= 0) {
        NVIC->ISER[0U] = (uint32_t)(1UL #IRQn
__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) 
{
    if ((int32_t)(IRQn) >= 0) {
        NVIC->ICER[0U] = (uint32_t)(1UL #IRQn
__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn)
{
    if ((int32_t)(IRQn) >= 0) {
        return((uint32_t)(((NVIC->ISER[0U] & (1UL else {
        return(0U);
    }
}

2. 中断挂起和清除挂起


如果一个中断发生了,却无法立即处理,这个中断请求将会被挂起。挂起状态保存在一个寄存器中,如果处理器的当前优先级还没有降低到可以处理挂起的请求,并且没有手动清除挂起状态,该状态将会一直保持。

可以通过操作中断设置挂起和中断清除挂起两个独立的寄存器来访问或者修改中断挂起状态,中断挂起寄存器也是通过两个地址来实现设置和清除相关位。这使得每一个位都可以独立修改,并且无需担心在两个应用程序进程竞争访问时出现的数据丢失。

image-20231108221941842
image-20231108221941842

中断挂起状态寄存器允许使用软件来触发中断。如果中断已经使能并且没有被屏蔽掉,当前还没有更高优先级的中断在运行,这时中断的服务程序就会立即得以执行。

2.1 C代码
*(volatile unsigned long)(0xE000E100) = 0x4 ; //使能中断#2
*(volatile unsigned long)(0xE000E200) = 0x4 ; //挂起中断#2
*(volatile unsigned long)(0xE000E280) = 0x4 ; //清除中断#2的挂起状态
2.2 汇编代码
__asm void Interrupt_Set_Pending()
{
 LDR R0, =0xE000E100   ;  //设置使能中断寄存器地址
 MOVS R1, #0x4         ;  //中断#2
 STR R1, [R0]          ;  //使能#2中断
 LDR R0, =0xE000E200   ; //设置挂起中断寄存器地址
 MOVS R1, #0x4         ;  //中断#2
 STR R1, [R0]          ;  //挂起#2中断
}

__asm void Interrupt_Clear_Pending()
{
 LDR R0, =0xE000E100   ;  //设置使能中断寄存器地址
 MOVS R1, #0x4         ;  //中断#2
 STR R1, [R0]          ;  //使能#2中断
 LDR R0, =0xE000E280   ; //设置清除中断挂起寄存器地址
 MOVS R1, #0x4         ;  //中断#2
 STR R1, [R0]          ;  //清除#2的挂起状态
}
2.3 CMSIS标准设备驱动函数
//设置一个中断挂起
__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) 
{
    if ((int32_t)(IRQn) >= 0) {
        NVIC->ISPR[0U] = (uint32_t)(1UL if ((int32_t)(IRQn) >= 0) {
        NVIC->ICPR[0U] = (uint32_t)(1UL if ((int32_t)(IRQn) >= 0) {
        return((uint32_t)(((NVIC->ISPR[0U] & (1UL else {
        return(0U);
    }
}

NVIC属于处理器内核部分,因此在MM32 MCU芯片的用户手册中只有简单的提及,没有重点讲述,需要深入了解相关寄存器和功能需要参考《Cortex-M0技术参考手册》。

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部