FreeRTOS中断分析

本文是《ALIENTEK STM32F429 FreeRTOS 开发教程》第四章学习笔记
第一章笔记–FreeRTOS简介与源码下载
第二章笔记–FreeRTOS在STM32F4上移植
第三章笔记-FreeRTOS系统配置

一、Cortex-M中断管理

1.1 中断概述

中断由硬件产生,当中断产生后CPU就会中断当前流程转去处理中断服务,Cortex-M内核的MCU提供一个用于管理中断的嵌套向量中断控制器(NVIC)

NVIC 与 CM3 内核共同完成对中断的响应。NVIC的寄存器以存储器映射的方式来访问,除了包含控制寄存器和中断处理的控制逻辑之外,NVIC 还包含了 MPU
的控制寄存器、 SysTick 定时器以及调试控制。

Cortex-M3和M4的NVIC最多支持249个外部中断输入(IRQs)、1个不可屏蔽中断(NMI)、1个Systick(滴答定时器)定时器中断和多个系统异常

1.1.1中断类型

在 NVIC 的中断控制及状态寄存器中,有一个 VECTACTIVE 位段;另外,还有一个特殊功能寄存器IPSR。在它们二者的里面,都记录了当前正服务异常的编号,编号为 1-15 的对应系统异常,
这里写图片描述
编号大于等于 16 的则全是外部中断
这里写图片描述

1.2 中断管理

Cortex-M处理器有多个用于管理中断和异常的可编程寄存器,这些寄存器都在NVIC和系统控制块(SCB)中,CMSIS将这些寄存器定义为结构体。

NVIC访问地址是0xE000_E000,打开core_cm4.h文件:
这里写图片描述
可以看到NVIC基址是从 0xE000_E000 + 0x0100UL = 0xE000_E100开始的

宏定义的NVIC中有一结构体 NVIC_Type

1.2.1NVIC_Type

typedef struct
{
  __IOM uint32_t ISER[8U];               /*!< Offset: 0x000 (R/W)  Interrupt Set Enable Register */
        uint32_t RESERVED0[24U];
  __IOM uint32_t ICER[8U];               /*!< Offset: 0x080 (R/W)  Interrupt Clear Enable Register */
        uint32_t RSERVED1[24U];
  __IOM uint32_t ISPR[8U];               /*!< Offset: 0x100 (R/W)  Interrupt Set Pending Register */
        uint32_t RESERVED2[24U];
  __IOM uint32_t ICPR[8U];               /*!< Offset: 0x180 (R/W)  Interrupt Clear Pending Register */
        uint32_t RESERVED3[24U];
  __IOM uint32_t IABR[8U];               /*!< Offset: 0x200 (R/W)  Interrupt Active bit Register */
        uint32_t RESERVED4[56U];
  __IOM uint8_t  IP[240U];               /*!< Offset: 0x300 (R/W)  Interrupt Priority Register (8Bit wide) */
        uint32_t RESERVED5[644U];
  __OM  uint32_t STIR;                   /*!< Offset: 0xE00 ( /W)  Software Trigger Interrupt Register */
}  NVIC_Type;
1.2.1.1 中断使能除能寄存器

CM3和CM4 中可以有240对使能位/除能位,每个中断拥有一对。这 240 个对子分布在8对32位寄存器中(最后一对没有用完)。欲使能一个中断,需要写 1 到对应 SETENA 的位中;欲除能一个中断,需要写 1 到对应的 CLRENA 位中;如果往它们中写 0,不会有任何效果

类型为32位,长度为8的数组ISER对应着8个32位的中断使能寄存器(开始地址为0xE000_E100);

类型为32位,长度为8的数组ICER对应着8个32位的中断除能寄存器(开始地址为0xE000_E100+(8+24)*4=0xE000_E180);
这里写图片描述

1.2.1.2 中断悬起与解悬寄存器

如果中断发生时,正在处理同级或高优先级异常,或者被掩蔽,则中断不能立即得到响应。此时中断被悬起。中断的悬起状态可以通过“中断设置悬起寄存器(SETPEND)”和“中断悬起清除寄存器(CLRPEND)”来读取,还可以写它们来手工悬起中断

类型为32位,长度为8的数组ISPR对应着8个32位的中断悬起寄存器(开始地址为:0xE000_E200);

类型为32位,长度为8的数组ICPR对应着8个32位的中断悬起寄存器(开始地址为:0xE000_E280)
这里写图片描述

1.2.1.3 活动状态寄存器

每个外部中断都有一个活动状态位。在处理器执行了其 ISR 的第一条指令后,它的活动位就被置1,并且直到ISR返回时才硬件清零

由于支持嵌套,允许高优先级异常抢占某个ISR。然而,哪怕一个中断被抢占,其活动状态也依然为 1

类型为32位,长度为8的数组IABR对应着8个32位的活动状态寄存器(开始地址为:0xE000_E300)
这里写图片描述

1.2.1.4 优先级寄存器

每个外部中断都有一个对应的优先级寄存器,每个寄存器占用 8 位,但是允许最少只使用最高3位。4个相临的优先级寄存器拼成一个 32 位寄存器

根据优先级组设置,优先级可以被分为高低两个位段,分别是抢占优先级和亚优先级

类型为32位,长度为240的数组IP对应着240个32位的优先级寄存器(开始地址为:0xE000_E400)
这里写图片描述
这里写图片描述

1.2.1.5 软件触发中断寄存器

用一个 32位类型的数据 STIR 表示 软件触发中断寄存器STIR(地址为:0xE000_EF00)
这里写图片描述

1.2.2 SCB_Type

以STM32F429为例子,打开core_cm4.h文件 可以看到结构体 SCB_Type:

typedef struct
{
  __IM  uint32_t CPUID;                  /*!< Offset: 0x000 (R/ )  CPUID Base Register */
  __IOM uint32_t ICSR;                   /*!< Offset: 0x004 (R/W)  Interrupt Control and State Register */
  __IOM uint32_t VTOR;                   /*!< Offset: 0x008 (R/W)  Vector Table Offset Register */
  __IOM uint32_t AIRCR;                  /*!< Offset: 0x00C (R/W)  Application Interrupt and Reset Control Register */
  __IOM uint32_t SCR;                    /*!< Offset: 0x010 (R/W)  System Control Register */
  __IOM uint32_t CCR;                    /*!< Offset: 0x014 (R/W)  Configuration Control Register */
  __IOM uint8_t  SHP[12U];               /*!< Offset: 0x018 (R/W)  System Handlers Priority Registers (4-7, 8-11, 12-15) */
  __IOM uint32_t SHCSR;                  /*!< Offset: 0x024 (R/W)  System Handler Control and State Register */
  __IOM uint32_t CFSR;                   /*!< Offset: 0x028 (R/W)  Configurable Fault Status Register */
  __IOM uint32_t HFSR;                   /*!< Offset: 0x02C (R/W)  HardFault Status Register */
  __IOM uint32_t DFSR;                   /*!< Offset: 0x030 (R/W)  Debug Fault Status Register */
  __IOM uint32_t MMFAR;                  /*!< Offset: 0x034 (R/W)  MemManage Fault Address Register */
  __IOM uint32_t BFAR;                   /*!< Offset: 0x038 (R/W)  BusFault Address Register */
  __IOM uint32_t AFSR;                   /*!< Offset: 0x03C (R/W)  Auxiliary Fault Status Register */
  __IM  uint32_t PFR[2U];                /*!< Offset: 0x040 (R/ )  Processor Feature Register */
  __IM  uint32_t DFR;                    /*!< Offset: 0x048 (R/ )  Debug Feature Register */
  __IM  uint32_t ADR;                    /*!< Offset: 0x04C (R/ )  Auxiliary Feature Register */
  __IM  uint32_t MMFR[4U];               /*!< Offset: 0x050 (R/ )  Memory Model Feature Register */
  __IM  uint32_t ISAR[5U];               /*!< Offset: 0x060 (R/ )  Instruction Set Attributes Register */
        uint32_t RESERVED0[5U];
  __IOM uint32_t CPACR;                  /*!< Offset: 0x088 (R/W)  Coprocessor Access Control Register */
} SCB_Type;

同结构体NVIC_Type作用相似,都是将相关寄存器用结构体封装表示出来

1.3 中断优先级

1.3.1 优先级概念

优先级影响一个中断是否被响应以及何时被响应

优先级数值越小,则优先级越高

CM3和CM4支持中断嵌套,使高优先级抢占低优先级中断

3个系统异常,有固定优先级且为负数
这里写图片描述

原则上, Cortex-M处理器支持3个固定的高优先级和多达256级的可编程优先级,并且支持 128级抢占,但为了精简设计,STM32最后只有16级优先级(选择4位作为优先级),在设计时裁掉表达优先级的几个低端有效位来减少优先级数

使用3个位来表达优先级情况:
这里写图片描述
[4:0]没有被实现,所以读它们总是返回零,写它们则忽略写入的值,对于3个位的情况,我们能够使用的8个优先级为: 0x00(最高), 0x20, 0x40, 0x60, 0x80,0xA0, 0xC0 以及 0xE0

1.3.2 优先级分组(抢占优先级和子优先级)

为了使抢占机能变得更可控,Cortex_M还把256级优先级按位分成高低两段,分别是抢占优先级和子优先级

STM32F429将中断分为 5 个组,组0~4。该分组的设置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的
这里写图片描述

打开stm32HAL库里NVIC相关代码可以看到:

这里写图片描述
可以设置0-4组,使用FreeRTOS推荐使用组4

HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);//设置为中断优先级分组4

1.3.3 优先级设置

通过三个中断优先级设置函数

void HAL_NVIC_SetPriority(IRQn_Type IRQn,uint32_t PreemptPriority, uint32_t SubPriority);
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn);
void HAL_NVIC_DisableIRQ(IRQn_Type IRQn);
1.3.3.1 设置单个优先级抢占优先级和子优先级
void HAL_NVIC_SetPriority(IRQn_Type IRQn,uint32_t PreemptPriority, uint32_t SubPriority);

IRQn_Type IRQn:中断优先级类型,每个类型对应NVIC_Type.IP[x](x为0-239)
这里写图片描述

uint32_t PreemptPriority: 抢占优先级

uint32_t SubPriority:子优先级

1.3.3.2使能某个中断通道
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn);
1.3.3.3中断失能
void HAL_NVIC_DisableIRQ(IRQn_Type IRQn);

1.3.4 设置PendSV和SysTick(滴答定时器)优先级

这里写图片描述

portNVIC_SYSPRI2_REG:

#define portNVIC_SYSPRI2_REG (*((volatile uint32_t *)0xe000ed20))

前面说过每个外部中断都有一个对应的优先级寄存器,每个寄存器占用 8位,4个相临的优先级寄存器拼成一个32位寄存器
这里写图片描述
要设置PendSV与SYSTick的优先级,则对最后4个相邻的寄存器组成的32位寄存器进行操作,地址为0xE000_ED20

#define portNVIC_PENDSV_PRI(((uint32_t)configKERNEL_INTERRUPT_PRIORITY)<<16UL)

#define pportNVIC_SYSTICK_PRI(((uint32_t)configKERNEL_INTERRUPT_PRIORITY)<<24UL)

#define configKERNEL_INTERRUPT_PRIORITY (configLIBRARY_LOWEST_INTERRUPT_PRIORITY<<(8-configPRIO_BITS))

#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf

#define configPRIO_BITS 4  /*STM32使用了几位作为优先级 */

通过代码一系列的宏定义可以分析: 因为一开始设置优先级分组是组4,所以最低优先级是0xf(15), 八位的优先级寄存器只使用了4位作为优先级,所以要左移(8-4)位进行赋值,分别给基地址为0xE000_ED20的32位寄存器的高八位和次高八位进行赋值,因此要左移24位和16位,这样就可以把PendSV与SYSTick的优先级设置成最低了

1.4.中断屏蔽

1.4.1中断屏蔽寄存器PRIMASK与FAULTMASK特殊功能寄存器

PRIMASK 用于除能在NMI和硬fault之外的所有异常,它有效地把当前优先级改为0(可编程优先级中的最高优先级)

该寄存器可以通过 MRS 和 MSR 以下例方式访问:

关中断:
MOV R0, #1
MSR PRIMASK, R0

开中断:
MOV R0, #0
MSR PRIMASK, R0

还可以通过CPS指令快速完成上述功能:

CPSID i ;关中断
CPSIE i ;开中断

例如工程中sys.h就是这样操作
这里写图片描述

FAULTMASK会把当前优先级改为‐1。硬fault都会被掩蔽。使用方案与PRIMASK的相似。但要注意的是,FAULTMASK会在异常退出时自动清零。

1.4.2 BASEPRI寄存器

FreeRTOS中使用这个寄存器

在更精巧的设计中,需要对中断掩蔽进行更细腻的控制——只掩蔽优先级低于某一阈值的中断——它们的优先级在数字上大于等于某个数。那么这个数存储在哪里?就存储在
BASEPRI中。不过,如果往BASEPRI中写0,则另当别论——BASEPRI将停止掩蔽任何中断

屏蔽所有优先级不高于0x60的中断:
MOV R0, #0x60
MSR BASEPRI, R0

取消对所有中断的屏蔽
MOV R0, #0
MSR BASEPRI, R0

打开代码可以看到屏蔽中断的操作:

#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()

static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

    __asm
    {
        /* Set BASEPRI to the max syscall priority to effect a critical
        section. */
        msr basepri, ulNewBASEPRI
        dsb
        isb
    }
}

#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    5 /*系统可管理的最高中断优先级*/

取消所有屏蔽写入0就可以:

#define portENABLE_INTERRUPTS()  vPortSetBASEPRI( 0 )

二、FreeRTOS中断配置宏

2.1 configPRIO_BITS

这里写图片描述
用来设置MCU使用几位优先级,STM32使用是4位,所以此宏设置为4

2.2 configLIBRARY_LOWEST_INTERRUPT_PRIORITY

用来设置最低优先级,优先级使用了4位,而且是组4,所以最低优先级是15

2.3 configKERNEL_INTERRUPT_PRIORITY

用来设置内核中断优先级

#define configKERNEL_INTERRUPT_PRIORITY (configLIBRARY_LOWEST_INTERRUPT_PRIORITY<<(8-configPRIO_BITS))

用来设置PendSV和SysTick的中断优先级,在1.3.4中有详细配置

2.4 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY

用来设置FreeRTOS系统可管理的最大优先级,在1.4.2当中提到对BASEPRI寄存器说的阈值优先级,这里设置了5,即高于5的优先级不归FreeRTOS管理

2.5 configMAX_SYSCALL_INTERRUPT_PRIORITY

宏configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY左移4位得到,与configKERNEL_INTERRUPT_PRIORITY同理,设置好后,高于此优先级中断FreeRTOS是不能禁止的

三、FreeRTOS开关中断

开关中断函数为

portENABLE_INTERRUPTS()
portDISABLE_INTERRUPTS() 

这两个函数只是一层封装,真正执行的是:

#define portDISABLE_INTERRUPTS()   vPortRaiseBASEPRI()
#define portENABLE_INTERRUPTS()   vPortSetBASEPRI( 0 )

在1.4.2已经分析过

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值