文档结构  
翻译进度:已翻译     翻译赏金:0 元 (?)    ¥ 我要打赏

在 “ARM Cortex-M、中断和FreeRTOS: Part 1”, 我开始ARM Cortex-M中断系统。因为ARM的实现非常混乱,我自己也被弄晕了,所以必须纠正和延伸下第1部分中的描述。

感谢所有的反馈和评论!

本来我想在第二部分讲述FreeRTOS。但基于第一部分中的问题和讨论,我想提供一个实例可能更好。

NXP KV58F ARM Cortex-M7

NXP KV58F ARM Cortex-M7

 

概述

我使用NXP FRDM-K64F开发板,它使用ARM Cortex-M4F (F表示浮点单元)架构,实现了四个中断优先级位,支持可选的子优先级位。

第 1 段(可获 1.38 积分)

需要注意的是,ARM Cortex-M0/M0+并未实现子优先级位。

Toggle LED

NXP FRDM-K64F Board

 

为了说明中断,我使用Segger SystemView with Segger RTT: 它可以记录没有任何外部硬件或跟踪引脚的中断。

Nested Interrupts in Segger SystemView

Segger SystemView中的嵌套中断

监视中断的另一种方法是在中断入口/出口切换一个GPIO引脚,然后用逻辑分析仪进行记录。这需要空闲引脚和一个外部逻辑分析仪。如果你有SWO或者其他跟踪引脚,也可以使用。

 

K64F上的中断级别

FRDM-K64F开发板上的K64F实现了四个优先级位:

第 2 段(可获 1.3 积分)

四个NVIC中断标志位可用

所以,使用移位数值则有2^4 (16) 优先级,

  • 十六进制表示: 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90 0xA0, 0xB0 0xC0, 0xD0, 0xE0, 0xF0.
  • 十进制表示: 0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240.

记住:数值上低的优先级高。零为最高优先级。 

小心一些 CMSIS APIs 使用非移位优先级数值 (0-16) ,而非移位优先级数值 (0x00-0xF0)。

嵌套中断

ARM NVIC是指嵌套中断向量表控制器 ( Nested Vectored Interrupt Controller):高优先级的中断请求可以嵌套(中断)低优先级的中断请求。

第 3 段(可获 1.36 积分)

为了说明这一点,我在板上配置了两个定时器中断:

  • ISR #58 (exception #58, IRQ #42) 配置频率为100 Hz(每隔10 ms) 的定时器中断。
  • ISR #59 (exception #59, IRQ #43) 配置频率为33.33 Hz (每隔3 ms)的定时器中断。

记住异常号和IRQ号之间的关系(见下图)。这在CMSIS中又增加了另一个令人困惑的地方:仔细检查API是需要异常号还是IRQ号。

Cortex-M Vector Table

Cortex-M 向量表(图片来源: ARM)

我配置了 ISR #58 and ISR #59 如下的优先级:

第 4 段(可获 1.31 积分)
  • ISR #58: 中断优先级值为160 (0xA0).
  • ISR #59: 中断优先级值为112 (0x70).
NVIC_SetPriority(58-16, 0xA); /* IRQ Number 42, Interrupt Priority 0xA0 */
NVIC_SetPriority(59-16, 0x7); /* IRQ Number 43, Interrupt Priority 0x70 */

为了更好地可视化中断,我在中断处理函数里面添加了一些延迟:

void ISR59_OnInterrupt(void)
{
  /* ISR #59, every 3 ms */
  SYS1_RecordEnterISR(); /* record interrupt entry in Segger SystemViewer */
  WAIT1_Waitms(1);       /* burn time for 1 ms */
  SYS1_RecordExitISR();  /* record interrupt exit in Segger SystemViewer */
}

void ISR58_OnInterrupt(void)
{
  /* ISR #58, every 10 ms */
  SYS1_RecordEnterISR(); /* record interrupt entry in Segger SystemViewer */
  WAIT1_Waitms(2);       /* burn time for 2 ms */
  SYS1_RecordExitISR();  /* record interrupt exit in Segger SystemViewer */
}

 

第 5 段(可获 0.38 积分)

 

  • ISR #59 (橙色) 每3毫秒触发一次,并占用1毫秒。
  • ISR #58 (红色) 每10毫秒触发一次,并占用2毫秒。

Nested Interrupts

中断嵌套(点击放大)

如上所示,因为 ISR #59 具有较高的紧急度 (中断级别0x70),它中断了较低紧急度的ISR #58(中断级别0xA0):

  1. 较高的紧迫性IRQ会中断较低的紧急度的IRQ(嵌套).
  2. 如果两个或多个相同紧急度的IRQ同时发生或处于挂起状态,那么IRQ号低的先执行。

下图在事件视图中显示了中断嵌套:

第 6 段(可获 1.3 积分)

Interrupt Nesting

中断嵌套

 

下面的例子,我交换了这两个中断的优先级:ISR #58比ISR #59 (0xA0)有更高的紧急度 (0x70)。 由于ISR #58 需要更多的时间(ISR #58需2ms vs. ISR #59需1 ms),所以它会增加ISR #59所需的时间。

Missed Deadlines

错过执行时间(点击放大)

 

中断延迟

在前面的示例中,因为ISR #59有更高的紧急度,他可以中断ISR #58。 此外,如果ISR # 59正在执行, 则ISR #58 会被延迟直到较高紧急度的中断完成执行。这导致中断延迟:从中断触发到中断处理的时间。中断将被延迟一段时间,不能在触发时直接开始中断处理。

第 7 段(可获 1.64 积分)

在下面的例子中, ISR #58 本来应该在+10 ms执行,但因为那个时候ISR #59正在执行,所以它被较高紧急度的ISR #59阻塞并延迟了。

Delayed Interrupt

中断延迟

 

因为嵌套的关系,ISR #59额外增加了ISR #58的总执行时间。

子优先级

基于ARM Cortex-M4F的NXP FRDM-K64F,实现了四个优先级位。我现在给它配置一个子优先级位:

注意:ARM Cortex-M0/M0+ *没有* 子优先级

Three preemption bits and one subpriority bit

三个抢占位和一个子优先级位

 

CMSIS提供下列分组功能:

第 8 段(可获 1.29 积分)

下面实例中我配置它使用一位子优先级,剩余三位作为抢占优先级。ISR #59的优先级配置为4.0 (抢占优先级为4, 子优先级为1) ,ISR #58的优先级配置为4.1.

对于“抢占优先级“ 通常也使用术语”主优先级“。

NVIC_SetPriorityGrouping(4); /* Having 4 interrupt priority bits: sub-priority at bit 4 ==> 3 preemption priority, 1 sub-priority bit */
NVIC_SetPriority(59-16, 0x8); /* IRQ Number 43, 0b100.0 (preempt prio 4, subprio 0), Interrupt Priority value 0x80 */
NVIC_SetPriority(58-16, 0x9); /* IRQ Number 42, 0b100.1 (preempt prio 4, subprio 1), Interrupt Priority value 0x90 */
第 9 段(可获 0.64 积分)

子优先级使用以下规则:

  1. 一个较高抢占紧急度的IRQ(低数值优先级)会中断任何较低紧急度的ISR(正常嵌套:2.1会中断3.0)。
  2. 一个挂起的IRQ不会中断和它具有相同紧急度的正在执行的ISR(较高子紧急度不会中断一个正在执行的主紧急度服务程序): 一个挂起的2.0 IRQ不会中断正在执行的2.1 ISR。
  3. 如果两个或多个相同抢占紧急度的IRQ挂起,那么最高子紧急度(低子优先级值)的首先被执行: 如果 2.1和2.0挂起, 那么2.0的先执行。
  4. 如果多个相同的抢占和子优先级值的IRQ挂起,那么最低IRQ号的首先被执行:如果IRQ #59和IRQ #58 挂起, 那么 IRQ #58先执行。
第 10 段(可获 1.78 积分)

规则二很重要,因为这条规则使有子优先级的系统和没有子优先级的系统区分开来。这条规则指明一个正在运行的ISR不能被相同主优先级或分组的ISR中断。这是非常有用的,比如有两个I²C 总线中断,在我的驱动里面我不想让两条总线同时激活中断:那么我可以把它们放在一个组,这样他们就不会出现嵌套。或者换句话说,分组禁止了组内的中断嵌套,因为在组内的多个中断挂起时,子优先级提供一个分配紧急度级别的方法。

第 11 段(可获 1.46 积分)

下面Segger SystemView 显示优先级4.0的ISR #59和优先级4.1的ISR #58:他们不在嵌套了,并且在4.0和4.1的挂起时,4.0的有优先权。

System with Subpriorites

带有子优先级的系统

 

总结

ARM NVIC 是一个非常灵活且强大的硬件模块。嵌套允许它中断一个较低紧急度的正在运行的中断服务程序。NVIC的好处是中断优先级取决于用户或应用程序。多个IRQ可以共享相同的紧急度或中断级别。 所以,NVIC可用比特数是没有限制。一些系统可能需要16个以上的优先级。但大部分系统尤其是小型系统只需要几个。

第 12 段(可获 1.58 积分)

除了正常的中断优先级外,NVIC优先级分组是另一种配置系统的方法。它确保同一分组的IRQ不会发生嵌套。虽然这是一个很好的特性,但大多数系统并不真的需要它。它会使系统复杂化,不正确的使用它会导致错误。我在一些系统中使用过子优先级,它们只应用于非常特定的用例。要知道子优先级的功能增加了硅成本:这可能就是为什么ARM在Cortex-M0/M0+核心上移除了这个功能。我认为大约98%的ARM Cortex-M3/M4/M7系统并没有使用子优先级。但这并不意味着它可以被忽略 — 需要时有它很好。

第 13 段(可获 1.75 积分)

在第三部分,我将讲述FreeRTOS实时操作系统如何使用NVIC。除非这篇文章引发其他需要再做延伸的评论和问题。

这篇文章使用的例子代码可以在GitHub上获得。

Happy interrupting.

第 14 段(可获 0.53 积分)

文章评论