四、FreeRTOS 中断配置和临界段

FreeRTOS 的中断配置是一个很重要的内容,我们需要根据所使用的 MCU 来具体配置。因此要先了解 MCU 架构中有关中断的知识。

Cortex-M 中断

1.中断简介

中断由硬件产生,当中断产生以后 CPU 就会中断当前的流程转而去处理中断服务,待中断服务函数执行完后再回来执行之前被中断的任务。Cortex-M 内核的 MCU 提供了一个用于中断管理的嵌套向量中断控制器(NVIC)。

Cotex-M3 和 M4 的 NVIC 最多支持 240 个 IRQ(中断请求)、1 个不可屏蔽中断(NMI)、1 个Systick(滴答定时器)定时器中断和多个系统异常。

2.中断管理简介

Cortex-M 处理器有多个用于管理中断和异常的可编程寄存器,这些寄存器大多数都在NVIC 和系统控制块(SCB)中,CMSIS 将这些寄存器定义为结构体。以 STM32F407 为例,打开core_cm4.h,有两个结构体,NVIC_Type 和 SCB_Type,NVIC 和 SCB 都位于系统控制空间(SCS)内,SCS 的地址从 0XE000E000 开始,SCB 和 NVIC的地址也在 core_cm4.h 中有定义。这些中断控制寄存器在我们一直FreeRTOS操作系统时是不必关系的,如果想了解的话可以可以参考 Cortex-M 权威指南,我们重点关心的是三个中断屏蔽寄存器:PRIMASK、FAULTMASK 和 BASEPRI,这三个寄存器后面会讲解。

3.优先级分组定义

当多个中断来临的时候处理器应该响应哪一个中断是由中断的优先级来决定的,高优先级的中断(优先级编号小)肯定是首先得到响应,而且高优先级的中断可以抢占低优先级的中断,这个就是中断嵌套。Cortex-M 处理器的有些中断是具有固定的优先级的,比如复位、NMI、HardFault,这些中断的优先级都是负数,优先级也是最高的。

Cortex-M 处理器有三个固定优先级和 256 个可编程的优先级,最多有 128 个抢占等级,但是实际的优先级数量是由芯片厂商来决定的。但是,绝大多数的芯片都会精简设计的,以致实际上支持的优先级数会更少,如 8 级、16 级、32 级等,比如 STM32 就只有 16 级优先级。在设计芯片的时候会裁掉表达优先级的几个低端有效位,以减少优先级数,所以不管用多少位来表达优先级,都是 MSB 对齐的,如下图就是使用三位来表达优先级。

Bit0~Bit4 没有实现,所以读它们总是返回零,写如它们的话则会忽略写入的值。因此,对于 3 个位的情况,可是使用的优先级就是 8 个:0X00(最高优先级)、0X20、0X40、0X60、0X80、0XA0、0XC0 和 0XE0。注意,这个是芯片厂商来决定的!不是我们能决定的,比如 STM32 就选择了 4 位作为优先级!

为了使抢占机能变得更可控,Cortex-M 处理器还把 256 个优先级按位分为高低两段:抢占优先级(分组优先级)和亚优先级(子优先级)。NVIC 中有一个寄存器是“应用程序中断及复位控制寄存器(AIRCR)”,AIRCR 寄存器里面有个位段名为“优先级组” PRIGROUP,它把优先级分为两个位段:MSB 所在的位段(左边的)对应抢占优先级,LSB 所在的位段(右边的)对应亚优先级

STM32 使用了 4 位,因此最多有 5 组优先级分组设置,这 5 个分组在 msic.h 中有定义。注意,STM32 中定义的分组 0 对应的是分组位置7,因为它对应的值是0x700(=7)。因为FreeRTOS 的中断配置没有处理亚优先级这种情况,所以我们只能配置中断优先级分组为 4,直接就 16 个主优先级,使用起来也简单!

#define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
4 bits for subpriority */
#define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
3 bits for subpriority */
#define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
2 bits for subpriority */
#define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
1 bits for subpriority */
#define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
0 bits for subpriority */

4.用于中断屏蔽的特殊寄存器--PRIMASK,FAULTMASK,BASEPRI

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

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

PRIMASK 寄存器还可以通过 MRS 和 MSR 指令访问:

MOVS R0, #1
MSR PRIMASK,  R0  ;//将 1 写入 PRIMASK 禁止所有中断

MOVS R0, #0 
MSR PRIMASK,  R0  ;//将 0 写入 PRIMASK 以使能中断

FAULTMASK 比 PRIMASK 更狠,它可以连 HardFault 都屏蔽掉,使用方法和 PRIMASK 类似,FAULTMASK 会在退出时自动清零。使用方法如下:

CPSIE F  ;清除 FAULTMASK
CPSID F  ;设置 FAULTMASK

MOVS  R0, #1 
MSR FAULTMASK, R0  ;将 1 写入 FAULTMASK 禁止所有中断
MOVS R0, #0
MSR FAULTMASK, R0  ;将 0 写入 FAULTMASK 使能中断

BASEPRI 寄存器用于设置某个阈值,只屏蔽优先级低于这个阈值的中断。比如,我们要屏蔽优先级不高于0x60的中断,则可以使用如下汇编编程:

MOV  R0, #0X60
MSR BASEPRI, R0

如果需要取消 BASEPRI 对中断的屏蔽,可以使用如下代码:

MOV  R0, #0
MSR BASEPRI, R0

注意!FreeRTOS 的开关中断就是操作 BASEPRI 寄存器来实现的!它可以关闭低于某个阈值的中断,高于这个阈值的中断就不会被关闭!


FreeRTOS  开关中断

FreeRTOS 开关中断函数为 portENABLE_INTERRUPTS ()和 portDISABLE_INTERRUPTS()。利用他们可以打开或屏蔽FreeRTOSConfig.h中指定优先级的中断。


临界段代码

临界段代码也叫做临界区,是指那些必须完整运行,不能被打断的代码段,比如有的外设的初始化需要严格的时序,初始化过程中不能被打断。FreeRTOS 在进入临界段代码的时候需要关闭中断,当处理完临界段代码以后再打开中断。FreeRTOS 系统本身就有很多的临界段代码,这些代码都加了临界段代码保护,我们在写自己的用户程序的时候有些地方也需要添加临界段代码保护。

FreeRTOS 与 临 界 段 代 码 保 护 有 关 的 函 数 有 4 个 : taskENTER_CRITICAL() 、taskEXIT_CRITICAL() 、 taskENTER_CRITICAL_FROM_ISR() 和 taskEXIT_CRITICAL_FROM_ISR()。他们在 task.h 文件中有相应的宏定义。

1.任务级临界段代码保护

taskENTER_CRITICAL()和 taskEXIT_CRITICAL()是任务级的临界代码保护,一个是进入临界段,一个是退出临界段,这两个函数是成对使用的。任务级临界代码保护使用方法如下:

void taskcritical_test(void)
{
    while(1)
    {
        taskENTER_CRITICAL(); 
        total_num+=0.01f;
        printf("total_num 的值为: %.4f\r\n",total_num);
        taskEXIT_CRITICAL();
        vTaskDelay(1000);
    } 
}

注意临界区代码一定要精简!因为进入临界区会关闭中断,这样会导致优先级低于 configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断得不到及时的响应!

2.中断级临界段代码保护

taskENTER_CRITICAL_FROM_ISR()和 taskEXIT_CRITICAL_FROM_ISR()中断级别临界段代码保护,是用在中断服务程序中的,而且这个中断的优先级一定要低于configMAX_SYSCALL_INTERRUPT_PRIORITY,因为高于这个优先级的中断服务函数不能调用 FreeRTOS 的 API 函数。中断级临界代码保护使用方法如下:

//定时器 3 中断服务函数
void TIM3_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
    {
        status_value=taskENTER_CRITICAL_FROM_ISR(); 
        total_num+=1;
        printf("float_num 的值为: %d\r\n",total_num);
        taskEXIT_CRITICAL_FROM_ISR(status_value);
    }
    TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中断标志位
}

 

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页