完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
我们知道在 MCU 裸机中程序代码之所以能完成多任务并行实时处理功能,其实主要是靠中断来调度的,没有中断,CPU 就只能按顺序"呆板"地执行代码。很多人都说是中断能力赋予了 MCU 真正的灵魂,能正确认识和熟练使用 MCU 中断,基本上就算玩熟了这颗 MCU。
痞子衡之前写过一篇 《中断处理函数(IRQHandler)的标准流程》,里面详细讲了中断处理函数里的标准代码流程与写法,这篇文章可让大家对 MCU 里的中断用法有个初步认识。今天痞子衡以 ARM Cortex-M 内核 MCU 为例再来介绍下业界标准的三重中断控制设计: 一、外设事件中断控制 MCU 中最底层的中断控制针对的是外设里某个具体的事件,这个控制来自于外设模块本身,以恩智浦 i.MXRT 系列 MCU 的 GPT 定时器模块为例。如下是 GPT 模块寄存器列表,你可以发现其中有经典的 IR 和 SR 寄存器,SR 是事件状态寄存器,IR 是中断事件控制寄存器: GPT 定时器一旦被使能,其运行状态(一共支持 6 个事件:超时、输入捕获 x 2ch、比较输出 x 3ch)都会实时记录在 SR 寄存器中,如果不在 IR 寄存器中将事件中断开启(默认是关闭的),那么就需要用户在代码里手动去查询 SR 寄存器置起的事件标志位以处理对应事件。 当然在实际应用中,为了节省 CPU 带宽,我们都是要开启外设事件中断的,MCU 厂商 SDK 包里一般都会提供相应接口函数(取自 fsl_gpt.h): typedef enum _gpt_interrupt_enable { kGPT_OutputCompare1InterruptEnable = GPT_IR_OF1IE_MASK, kGPT_OutputCompare2InterruptEnable = GPT_IR_OF2IE_MASK, kGPT_OutputCompare3InterruptEnable = GPT_IR_OF3IE_MASK, kGPT_InputCapture1InterruptEnable = GPT_IR_IF1IE_MASK, kGPT_InputCapture2InterruptEnable = GPT_IR_IF2IE_MASK, kGPT_RollOverFlagInterruptEnable = GPT_IR_ROVIE_MASK, } gpt_interrupt_enable_t; // 开启 GPTx 的 xx 事件中断 static inline void GPT_EnableInterrupts(GPT_Type *base, uint32_t mask) { base->IR |= mask; } // 关闭 GPTx 的 xx 事件中断 static inline void GPT_DisableInterrupts(GPT_Type *base, uint32_t mask) { base->IR &= ~mask; } 使能 GPT1 的超时事件中断代码示例如下: void periph_int_config(void) { // 初始化 GPT1... GPT_Init(GPT1, &gptConfig); // ... // 开启 GPT1 的超时事件中断 GPT_EnableInterrupts(GPT1, kGPT_RollOverFlagInterruptEnable); } 二、外设全局中断控制 MCU 中第二层的中断控制针对的是整个外设,这个控制来自于 Cortex-M 内核的 NVIC 模块。如下是 NVIC 模块寄存器列表(取自 ARMv8-M 手册,除了 IABRn 和 ITNSn 寄存器组外,其余寄存器适用全部的 Cortex-M 家族),其中跟中断开关相关的是 ISER 和 ICER 寄存器: 当 MCU 中某外设(比如上一节里的 GPT)被使能后,即使其内部事件中断已被开启,也不意味着系统中断一定会被触发,因为 NVIC 里对于这个外设的全局中断开关(同一外设中所有事件共享一个系统中断资源,即一个中断号)还没有开启。ARM CMSIS 包里提供了外设全局中断控制函数(取自 core_cm7.h 文件): #define NVIC_EnableIRQ __NVIC_EnableIRQ #define NVIC_DisableIRQ __NVIC_DisableIRQ // 开启 xx 外设的全局中断 __STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) { if ((int32_t)(IRQn) >= 0) { __COMPILER_BARRIER(); NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); __COMPILER_BARRIER(); } } // 关闭 xx 外设的全局中断 __STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) { if ((int32_t)(IRQn) >= 0) { NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); __DSB(); __ISB(); } } 增加了使能 GPT1 的全局中断代码示例如下,其中 GPT1_IRQn 和 GPT1_IRQHandler 是固定名字,在 MCU 厂商提供的头文件(MIMXRT1176_cm7.h)和启动文件(startup_MIMXRT1176_cm7.s)里有定义。 void periph_int_config(void) { // 初始化 GPT1... GPT_Init(GPT1, &gptConfig); // ... // 开启 GPT1 的超时事件中断 GPT_EnableInterrupts(GPT1, kGPT_RollOverFlagInterruptEnable); // 开启 GPT1 的全局中断 NVIC_EnableIRQ(GPT1_IRQn); } // GPT1 的中断响应函数 void GPT1_IRQHandler(void) { GPT_ClearStatusFlags(GPT1, kGPT_RollOverFlagInterruptEnable); // 中断业务处理代码 } 三、系统全局中断控制 MCU 中最顶层的中断控制针对的是整个芯片系统,这个控制来自于 Cortex-M 内核的 CPS 指令。如下是 CPS 指令用法(取自 ARMv7-M 手册): 当你想对 MCU 整个芯片的所有中断进行统一开关控制时,就必须借助 CPS 指令。一般情况下开启芯片系统全局中断动作在 MCU 启动文件里已经做好了,所以在用户代码环境里常常不需要使能系统全局中断的动作。如下是 IAR 环境下 i.MXRT1170 启动文件中系统全局中断操作,基于汇编指令实现: 为了便于用户在 C 代码中操作系统全局中断,各 IDE 下均按同样的接口函数( __disable_irq / __enable_irq )做了封装实现。IAR 环境见 IAR SystemsEmbedded Workbench 8.50.6armincciccarm_builtin.h 文件,但是封装进其 Lib 了,没有暴露源码: #include "iccarm_builtin.h" #define __disable_irq __iar_builtin_disable_interrupt #define __enable_irq __iar_builtin_enable_interrupt Keil 环境见 Keil_v5ARMARMCLANGincludearm_compat.h 文件,我们可以看到源码: static __inline__ unsigned int __attribute__((__always_inline__, __nodebug__)) __disable_irq(void) { unsigned int cpsr; #if __ARM_ARCH >= 6 #if defined(__ARM_ARCH_PROFILE) && __ARM_ARCH_PROFILE == 'M' __asm__ __volatile__("mrs %[cpsr], primaskn" "cpsid in" : [cpsr] "=r"(cpsr)); return cpsr & 0x1; #endif #endif } static __inline__ void __attribute__((__always_inline__, __nodebug__)) __enable_irq(void) { #if __ARM_ARCH >= 6 __asm__ __volatile__("cpsie i"); #endif } 最终 GPT 例程里完整的三重中断使能代码应如下: void periph_int_config(void) { // 初始化 GPT1... GPT_Init(GPT1, &gptConfig); // ... // 开启 GPT1 的超时事件中断 GPT_EnableInterrupts(GPT1, kGPT_RollOverFlagInterruptEnable); // 开启 GPT1 的全局中断 NVIC_EnableIRQ(GPT1_IRQn); // 开启芯片系统全局中断 __enable_irq(); } 至此,嵌入式MCU中标准的三重中断控制设计痞子衡便介绍完毕了 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1614 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1541 浏览 1 评论
970 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
682 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1592 浏览 2 评论
1863浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
644浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
515浏览 3评论
531浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
504浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-21 21:02 , Processed in 0.739150 second(s), Total 76, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号