(1)FreeRTOS学习笔记---中断配置和临界段

背景

项目中使用了XX公司的NB-iot通信芯片,采用OPEN-CPU方案做产品。OPEN-CPU方案,我理解的是一种以通信模块作为主处理器的应用方式。优势不展开讲了,详细可参见这位博主的知乎。借此机会理解装载Free RTOS系统NB芯片中的中断。

Cortex-M 中断

Cortex-M 内核的 MCU 提供了一个用于中断管理的嵌套向量中断控制器(NVIC)。
Cotex-M3 的 NVIC 最多支持 240 个 IRQ(中断请求)、1 个不可屏蔽中断(NMI)、1 个 Systick(滴答定时器)定时器中断和多个系统异常。

中断管理简介

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

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;
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;

多个中断来临时,处理器根据中断优先级决定处理顺序。高优先级中断会首先响应,同时还可以抢占低优先级中断。
有些特殊优先级地位无法撼动,比如复位、NMI、HardFault,这些中断优先级是最高的,固定的。

优先级分组

Cortex-M处理器有3个固定优先级和 256 个可编程的优先级,最多有 128 个抢占等级,但是实际的优先级数量是由芯片厂商来决定的。绝大多数的芯片都会精简设计的,以致实际上支持的优先级数会更少,如 8 级、16 级、32 级等。
为使抢占变得更可控,256个优先级按位分高低两端,前面使分组优先级,后面使亚优先级。NVIC中有一个寄存器叫做应用程序中断及复位控制寄存器AIRCR,它可以决定优先级分组,占32位寄存器的10~8三位,可以表示8种分组,如下表所示:

分组位置表达抢占优先级的位段表达亚优先级的位段
0(默认)[7:1][0:0]
1[7:2][1:0]
2[7:3][2:0]
3[7:4][3:0]
4[7:5][4:0]
5[7:6][5:0]
6[7:7][6:0]
7[7:0]

优先级设置

每个外部中断都有一个对应的优先级寄存器,每个寄存器占 8 位,因此最大宽度是 8 位,但是最小为 3 位。4 个相临的优先级寄存器拼成一个 32 位寄存器。如前所述,根据优先级组的设置,优先级又可以分为高、低两个位段,分别抢占优先级和亚优先级。优先级寄存器都可以按字节访问,当然也可以按半字/字来访问,有意义的优先级寄存器数目由芯片厂商来实现。

中断屏蔽的特殊寄存器

FreeRTOS 的时候需要重点关注 PRIMASK、FAULTMASK 和 BASEPRI 这三个寄存器。
在许多应用中,需要暂时屏蔽所有的中断去执行一些对时序要求严格的任务,这个时候就可以使用 PRIMASK 寄存器,PRIMASK 用于禁止除 NMI 和 HardFalut 外的所有异常和中断, 汇编编程的时候可以使用 CPS(修改处理器状态)指令修改 PRIMASK 寄存器的数值:

CPSIE I; //清除 PRIMASK(使能中断)
CPSID I; //设置 PRIMASK(禁止中断)

FAULTMASK 比 PRIMASK 更狠,它可以连 HardFault 都屏蔽掉,使用方法和 PRIMASK 类似,FAULTMASK 会在退出时自动清零。
PRIMASK 和 FAULTMASK 寄存器太粗暴了,直接关闭除复位、NMI 和HardFault 以外的其他所有中断,但是在有些场合需要对中断屏蔽进行更细腻的控制,比如只屏蔽优先级低于某一个阈值的中断。那么这个作为阈值的优先级值存储在哪里呢?在 BASEPRI 寄存器中,不过如果向 BASEPRI 写 0 的话就会停止屏蔽中断。

中断配置宏

#define portNVIC_SYSPRI2_REG				( * ( ( volatile uint32_t * ) 0xe000ed20 ) )
#define __NVIC_PRIO_BITS                         3U                             
#define configPRIO_BITS		       __NVIC_PRIO_BITS
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			0x7
#define configKERNEL_INTERRUPT_PRIORITY		 ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )//numeric value 0xe0
#define portNVIC_PENDSV_PRI					( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL )
#define portNVIC_SYSTICK_PRI				( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL )
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY	0x1// os api will disable int priority lower than this value when enter critical section
#define configMAX_SYSCALL_INTERRUPT_PRIORITY  0x20//(configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))

NB模块中设置优先级位数是3,则优先级一共有8个,其中最低逻辑优先级7。
内核中断优先级设为最低逻辑优先级7。
系统中断最大优先级设为1。
以“FromISR”结尾的FreeRTOS函数是具有中断调用保护的(执行这些函数会进入临界区),但是这些函数不可以被逻辑优先级高于系统中断最大优先级的中断服务函数调用。因此,任何使用RTOSAPI函数的中断服务例程中断优先级数值大于等于系统中断最大优先级的值。能保证中断的逻辑优先级等于或低于系统中断最大优先级。
Cortex中断默认情况下有一个数值为0的优先级。大多数情况下0代表最高级优先级。因此,绝对不可以在优先级为0的中断服务例程中调用RTOSAPI函数。具体关系可以用下图表示:
在这里插入图片描述

开关中断

FreeRTOS 开关中断函数为 portENABLE_INTERRUPTS ()和 portDISABLE_INTERRUPTS(), 这两个函数其实是宏定义,在 portmacro.h 中有定义,如下:

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

static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
	__asm
	{
		/* Barrier instructions are not used as this function is only used to
		lower the BASEPRI value. */
		msr basepri, ulBASEPRI
	}
}
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
	}
}

函数 vPortSetBASEPRI()是向寄存器 BASEPRI 写入一个值,此值作为参数 ulBASEPRI 传递进来,portENABLE_INTERRUPTS()是开中断,它传递了个 0 给 vPortSetBASEPRI(),根据我们前面讲解 BASEPRI 寄存器可知,结果就是开中断。
函 数 vPortRaiseBASEPRI()是向寄存器BASEPRI写configMAX_SYSCALL_INTERRUPT_PRIORITY ,那 么优 先级 低于configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断就会被屏蔽!

临界段代码

临界段代码也叫做临界区,是指那些必须完整运行,不能被打断的代码段,比如有的外设的初始化需要严格的时序,初始化过程中不能被打断。FreeRTOS 在进入临界段代码的时候需要关闭中断,当处理完临界段代码以后再打开中断。FreeRTOS 系统本身就有很多的临界段代码, 这些代码都加了临界段代码保护,我们在写自己的用户程序的时候有些地方也需要添加临界段代码保护。
FreeRTOS与临界段代码保护有关的函数有4 个: taskENTER_CRITICAL() 、taskEXIT_CRITICAL() 、taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR(),这四个函数其实是宏定义,在task.h文件中有定义。这四个函数的区别是前两个是任务级的临界段代码保护,后两个是中断级的临界段代码保护。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

火山宝 && 王林宝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值