痞子衡嵌入式:嵌入式MCU中通用的三重中断控制设计
大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是嵌入式MCU中通用的三重中断控制设计。
我们知道在 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 寄存器置起的事件标志位以处理对应事件。
- Note: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 Systems\Embedded Workbench 8.50.6\arm\inc\c\iccarm_builtin.h 文件,但是封装进其 Lib 了,没有暴露源码:
#include "iccarm_builtin.h"
#define __disable_irq __iar_builtin_disable_interrupt
#define __enable_irq __iar_builtin_enable_interrupt
Keil 环境见 \Keil_v5\ARM\ARMCLANG\include\arm_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], primask\n"
"cpsid i\n"
: [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中通用的三重中断控制设计痞子衡便介绍完毕了,掌声在哪里~~~
欢迎订阅
文章会同时发布到我的 博客园主页、CSDN主页、知乎主页、微信公众号 平台上。
微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。

痞子衡嵌入式:嵌入式MCU中通用的三重中断控制设计的更多相关文章
- WinForm开发中通用附件管理控件设计开发参考
1.引言 在WinForm开发中,文件附件的管理几乎在任何一个应用上都会存在,是一个非常通用集中的公共模块.我们日常记录会伴随着有图片.文档等附件形式来展现,如果为每个业务对象都做一个附件管理,或者每 ...
- 痞子衡嵌入式:嵌入式Cortex-M裸机环境下临界区保护的三种实现
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是Cortex-M裸机环境下临界区保护的三种实现. 搞嵌入式玩过 RTOS 的朋友想必都对 OS_ENTER_CRITICAL().OS_ ...
- 痞子衡嵌入式:飞思卡尔i.MX RT系列MCU启动那些事(9)- 从Parallel NOR启动
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔i.MX RT系列MCU的Parallel NOR启动. 上一篇讲i.MXRT从Raw NAND启动的文章 从Raw NAND启 ...
- 痞子衡嵌入式:改动i.MXRT1xxx里IOMUXC_GPR寄存器保留位可能会造成系统异常
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是改动i.MXRT1xxx里IOMUXC_GPR寄存器保留位可能会造成系统异常. 痞子衡的嵌入式技术交流群里有一位非常活跃的朋友(网名:文 ...
- 嵌入式外部中断控制编程方法论—比較CC2541(51核)和S5PV210(ARM核)
这是一篇阐述怎样对嵌入式SOC外部中断进行控制编程的方法论文章.希望读者理解本篇文章后.能够具备对市场上全部已经面世和将来面世的嵌入式芯片的外部中断进行控制编程的能力. 笔者原创的技术分享一直都恪守下 ...
- 痞子衡嵌入式:恩智浦MCU安全加密启动一站式工具NXP-MCUBootUtility用户指南
NXP MCU Boot Utility English | 中文 1 软件概览 1.1 介绍 NXP-MCUBootUtility是一个专为NXP MCU安全加密启动而设计的工具,其特性与NXP M ...
- 痞子衡嵌入式:飞思卡尔i.MX RT系列MCU启动那些事(4)- Flashloader初体验(blhost)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔i.MX RT系列MCU的Flashloader. 在上一篇文章 Serial Downloader模式(sdphost, mf ...
- 痞子衡嵌入式:飞思卡尔i.MX RT系列MCU启动那些事(6)- Bootable image格式与加载(elftosb/.bd)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔i.MX RT系列MCU的Bootable image格式与加载过程. 在i.MXRT启动系列第三篇文章 Serial Down ...
- 痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU硬件那些事(2.5)- 串行NOR Flash下载算法(IAR EWARM篇)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是IAR开发环境下i.MXRT的串行NOR Flash下载算法设计. 在i.MXRT硬件那些事系列之<在串行NOR Flash XI ...
随机推荐
- 微调BERT:序列级和令牌级应用程序
微调BERT:序列级和令牌级应用程序 Fine-Tuning BERT for Sequence-Level and Token-Level Applications 为自然语言处理应用程序设计了不同 ...
- jmeter工作目录介绍、jmeter元件及组件介绍
一.jmeter工作目录介绍: bin:放置各项配置文件(如日志设置.JVM设置).启动文件.启动Jar包.示例脚本等: docs:放置JMeter API的离线帮助文档: extras:JMeter ...
- python+selenium_鼠标事件
引言--在实际的web产品测试中,对于鼠标的操作,不单单只有click(),有时候还要用到右击.双击.拖动等操作,这些操作包含在ActionChains类中. 一.ActionChains类中鼠标操作 ...
- 『无为则无心』Python基础 — 4、Python代码常用调试工具
目录 1.Python的交互模式 2.IDLE工具使用说明 3.Sublime3工具的安装与配置 (1)Sublime3的安装 (2)Sublime3的配置 4.使用Sublime编写并调试Pytho ...
- 冷备搭建DG
1.主库开启归档 SQL> archive log list;(查询当前归档状态) SQL> shutdown immediate; SQL> startup mount;(启动到m ...
- Django(70)接口版本控制
前言 在RESTful规范中,有关版本的问题,用restful规范做开放接口的时候,用户请求API,系统返回数据.但是难免在系统发展的过程中,不可避免的需要添加新的资源,或者修改现有资源.因此,改 ...
- Java的一些细节语法(不定时更新。。。)
可信考试Java相关题目 目录 可信考试Java相关题目 ConcurrentHashMap不允许key为null,但是HashMap是可以的.TreeMap key不支持null. 以下代码里面,请 ...
- Java安全之基于Tomcat实现内存马
Java安全之基于Tomcat实现内存马 0x00 前言 在近年来红队行动中,基本上除了非必要情况,一般会选择打入内存马,然后再去连接.而落地Jsp文件也任意被设备给检测到,从而得到攻击路径,删除we ...
- Spring学习日记01_IOC_xml的三种注入方式
什么是IOC 控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理 使用IOC目的:为了耦合度降低 做入门案例就是IOC实现 IOC底层原理 xml解析 工厂模式 反射 原始方式 cla ...
- 学Java,找对圈子,跟对人
我大学学的是机械专业,到大四才决定要学Java,以后当一名程序员. 到现在,十几年过去了,我现在已经是一家上市公司的技术总监了,管理的技术团队有100多人.很庆幸当初了选择了学Java. 还记得当初学 ...