FreeRTOS & Cortex-M中断管理
Cortex-M 中断
Cortex-M 外中断
??Cortex-M内核提供了一个用于中断管理的 硬件单元 :NVIC(嵌套向量中断控制器). Cortex-M3 和 M4的NVIC最多支持240个IRQ(中断请求)、1个不可屏蔽中断(NMI)、1个滴答定时器中断(Systick)和多个系统异常中断(编号1-15)。 ??以CM4为例,我们找到随便一个STM32F4的项目打开它的core_cm4.h。一般路径为CMISIS/Include。
#define SCS_BASE (0xE000E000UL)
#define ITM_BASE (0xE0000000UL)
#define DWT_BASE (0xE0001000UL)
#define TPI_BASE (0xE0040000UL)
#define CoreDebug_BASE (0xE000EDF0UL)
#define SysTick_BASE (SCS_BASE + 0x0010UL)
#define NVIC_BASE (SCS_BASE + 0x0100UL)
#define SCB_BASE (SCS_BASE + 0x0D00UL)
#define SCnSCB ((SCnSCB_Type *) SCS_BASE )
#define SCB ((SCB_Type *) SCB_BASE )
#define SysTick ((SysTick_Type *) SysTick_BASE )
#define NVIC ((NVIC_Type *) NVIC_BASE )
#define ITM ((ITM_Type *) ITM_BASE )
#define DWT ((DWT_Type *) DWT_BASE )
#define TPI ((TPI_Type *) TPI_BASE )
#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE)
这里的宏定义主要看 NVIC 和 SCS_BASE。 NVIC的地址就是 SCS_BASE的地址偏移0x0100UL 。 NVIC_BASE 就是 xE000_E100。
#define NVIC ((NVIC_Type *) NVIC_BASE )
这一行就是在 NVIC_BASE 的基础上定义了一个结构体NVIC_Type来方便对NVIC内部寄存器的访问。
NVIC_Type结构体的定义如下:
typedef struct
{
__IOM uint32_t ISER[8U];
uint32_t RESERVED0[24U];
__IOM uint32_t ICER[8U];
uint32_t RSERVED1[24U];
__IOM uint32_t ISPR[8U];
uint32_t RESERVED2[24U];
__IOM uint32_t ICPR[8U];
uint32_t RESERVED3[24U];
__IOM uint32_t IABR[8U];
uint32_t RESERVED4[56U];
__IOM uint8_t IP[240U];
uint32_t RESERVED5[644U];
__OM uint32_t STIR;
} NVIC_Type;
__IOM uint32_t ISER[8U];
??每一个ISER都是一个uint32_t 的数据,也就是说ISER一共有[0-7] 8个,这个是干什么的呢,参考CM3权威指南
可以看到:
CM3 中可以有 240 对使能位/除能位,每个中断拥有一对。这 240 个对子分布在 8 对 32 位寄存器中(最后一对没有用完)。欲使能一个中断,你需要写 1 到对应 SETENA 的位中;欲除能一个中断,你需要写 1 到对应的 CLRENA 位中;如果往它们中写 0,不会有任何效果。
ISER : Interrupt Set Enable Register 就是其缩写。怎么验证说的是对的呢?我们可以简单计算一下他的兄弟ICER地址。
ICER= ISER_BASE + RESERVED0[24] +ISER[8]=xE000_E100 + 32 * 24/8 + 32 * 8/8 = xE000_E100 + x80UL = 0xE000_E180。
??对照权威指南(如下图)就是 ICER 寄存器开始的地址,对应上边代码段的 ICER 的偏移量0x80都正好符合。所以总的来说这个结构体里面定义的都是一类一类的关于中断的寄存器。 ??特别提示,这对这些外部中断,其对应优先级由IP这个寄存器来配置。下文会详细解释怎么配置。
??以上都是关于240个外中断的定义下面来看一下系统中断的定义。
Cortex-M 系统中断
以上每一个配置寄存器同样都是8位,针对PendSV、SysTick访问时都按字访问,即从0XE000_ED20开始访问。
Cortex-M 中断优先级分组
既然知道了这些终端由些寄存器控制,现在来讨论一下如何配置这些寄存器。
我们以STM32F4为例子,来看一下它是如何实现中断优先级分组的。
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
assert_param(IS_NVIC_PRIORITY_GROUP(PriorityGroup));
NVIC_SetPriorityGrouping(PriorityGroup);
}
从上面NVIC_SetPriorityGrouping中跳转到下面函数 :
__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
uint32_t reg_value;
uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL);
reg_value = SCB->AIRCR;
reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk));
reg_value = (reg_value |
((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
(PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) );
SCB->AIRCR = reg_value;
}
其操作的正是SCB->AIRCR寄存器。来看一下AIRCR寄存器(下图地址错误)。 ??通过设置[10:8] 这三个位来实现优先级分组, 结合Cortex-M系统中断中的图,每一个优先级配置寄存器都是8位,将这8位分为8种情况,当然这样分组不仅仅针对系统异常,同样对所有可配置中断的异常都适用,分组情况见下图。 ??打个比方将[10:8]这三个位设置成0x110,就是6,那所有可编程的中断优先级配置寄存器 -->IP 都必须按照抢占优先级占一个位[7:7],亚优先级占7个位[6:0]来设置优先级。
Freertos 中断
Freertos 中断配置
Freertos 中断配置的信息都存放于freertos/include/FreeRTOSConfig.h
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
??首先看最上边一行定义__NVIC_PRIO_BITS ,这个并没有定义,所以跳转到定义configPRIO_BITS 这里,因为样例使用的是STM32F4,对于STM32F4优先级配置寄存器 IP 只有高四位被用于使用,也就是说configPRIO_BITS 应该被设定成 4。
接下来看最下面两行的宏定义
*
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
??这个宏定义是根据configLIBRARY_LOWEST_INTERRUPT_PRIORITY 宏定义来确定的(在上面),configLIBRARY_LOWEST_INTERRUPT_PRIORITY 宏设定了最小的内核抢占优先级,这里设定的是0xf也就是15,再给任务设定优先级大小的时候,最低可以设置到15(优先级数字越大优先级越小)。
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
??configMAX_SYSCALL_INTERRUPT_PRIORITY 这个宏定义的是FreeRTOS内核控制的最大优先级,将此宏设定成5,则有[4:0]优先级不归内核控制,即无法屏蔽这些中断,在这些优先级高的中断中也不能安全的调用FreeRTOS的API。
移植配置FreeRTOS的时候将这个几个宏定义依次进行修改即可。
|