简介
FreeRTOS是一个C库集合,包含了实时内核和模块化库实现互补功能。
免费的实时操作系统,可裁减移植。
文件结构
demo、license和source。source为主体。
source有三部分
源码.c 、include文件夹里头文件.h和portable文件夹。其中portable文件将FreeRTOS系统软件与MCU芯片建立连接。
移植
芯片中断
中断由硬件产生,当中断产生以后 CPU 就会中断当前的流程转而去处理中断服务。Cortex-M 内核的 MCU 提供了一个用于中断管理的嵌套向量中断控制器(NVIC)。
Cotex-M3 的 NVIC 最多支持 240 个 IRQ(中断请求)、 1 个不可屏蔽中断(NMI)、 1 个 Systick(滴答定时器)定时器中断和多个系统异常。
Cortex-M 处理器有多个用于**管理中断和异常的可编程寄存器,这些寄存器大多数都在NVIC 和系统控制块(SCB)**中,CMSIS文件 将这些寄存器定义为结构体。NVIC_Type结构体
typedef struct
{
__IO uint32_t ISER[8];
/*!< Offset: 0x000 Interrupt Set Enable Register
*/
uint32_t RESERVED0[24];
__IO uint32_t ICER[8];
/*!< Offset: 0x080 Interrupt Clear Enable Register
*/
uint32_t RSERVED1[24];
__IO uint32_t ISPR[8];
/*!< Offset: 0x100 Interrupt Set Pending Register
*/
uint32_t RESERVED2[24];
__IO uint32_t ICPR[8];
/*!< Offset: 0x180 Interrupt Clear Pending Register
*/
uint32_t RESERVED3[24];
__IO uint32_t IABR[8];
/*!< Offset: 0x200 Interrupt Active bit Register
*/
uint32_t RESERVED4[56];
__IO uint8_t IP[240];
/*!< Offset: 0x300 Interrupt Priority Register (8Bit wide) */
uint32_t RESERVED5[644];
__O uint32_t STIR;
/*!< Offset: 0xE00 Software Trigger Interrupt Register
*/
} NVIC_Type;
NVIC 和 SCB 都位于系统控制空间(SCS)内,SCS 的地址从 0XE000E000 开始,SCB 和 NVIC的地址也在 core_cm3.h 中有定义.
中断优先级
当多个中断来临的时,高优先级的中断(优先级编号小)肯定是首先得到响应,而且高优先级的中断可以抢占低优先级的中断–中断嵌套。Cortex-M 处理器的有些中断是具有固定最高的优先级的,比如复位、NMI、HardFault。
Cortex-M 处理器有三个固定优先级和 256 个可编程的优先级,ST芯片厂商精简设计,将STM32设计只使用4个bit位来代表优先级,因此只有16 级优先级。多有 5 组优先级分组设置。
每个外部中断都有一个对应的优先级寄存器,每个寄存器占 8 位,因此最大宽度是 8 位,但是最小为 3 位。4 个相临的优先级寄存器拼成一个 32 位寄存器。0xE000_ED20~0xE000_ED23 这四个寄存器就可以拼接成一个地址为 0xE000_ED20 的 32 位寄存器。这一点很重要!因为 FreeRTOS 在设置 PendSV 和 SysTick 的中断优先级的时候都是直接操作的地址 0xE000_ED20。
中断屏蔽
PRIMASK寄存器
使用PRIMASK寄存器屏蔽所有的中断,执行时序严格的任务。PRIMASK寄存器可禁止NMI和HardFalut外所有中断和异常,汇编编程可以使用CPS(修改处理器状态)指令修改PRIMASK寄存器的值:
CPSIE 1; //清除PRIMASK(使能中断)
CPSID 1; //设置PRIMASK (禁止中断)
//还可以通过MRS和MSR指令。
MOVS R0, #1
MSR PRIMASK,R0 ;//将 1 写入 PRIMASK 禁止所有中断
MOVS R0, #0
MSR PRIMASK,R0 ;//将 0 写入 PRIMASK 以使能中断
FAULTMASK
FAULTMASK寄存器可屏蔽除NMI(不可屏蔽中断)外的所有中断,其中屏蔽包括了HardFalut硬件中断。
CPSIE F ;//清除 FAULTMASK
CPSID F ;//设置 FAULTMASK
BASEPRI 寄存器
在有些场合需要对中断屏蔽进行更细腻的控制,比如只屏蔽优先级低于某一个阈值的中断.
BASEPRI 寄存器中,向 BASEPRI 写 0 的话就会停止屏蔽中断.我们要屏蔽优先级于 0X60 的中断,则可以使用如下汇编编程
//屏蔽低于0x60优先级的中断
MOV R0,#0X60
MSR BASEPRI, R0
//停止中断
MOV R0,#0
MSR BASEPRI, R0
FreeRTOS 的开关中断就是操作 BASEPRI 寄存器来实现的!它可以关闭低于某个阈值的中断,高于这个阈值的中断就不会被关闭!
FreeRTOS中断配置宏
中断宏 | 描述 |
---|---|
configPRIO_BITS | 设置 MCU 使用几位优先级,STM32 使用的是 4 位,因此此宏为 4 |
configLIBRARY_LOWEST_INTERRUPT_PRIORITY | 设置最低优先级,STM32 优先级使用了 4 位,优先级数就是 16 个,最低优先级那就是 15 |
configKERNEL_INTERRUPT_PRIORITY | 设置内核中断优先级0XF0,用来设置 PendSV 和滴答定时器的中断优先级,FreeRTOS中 PendSV 和 SysTick 的中断优先级都是最低的! |
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY | 设置 FreeRTOS 系统可管理的最大优先级,BASEPRI 寄存器的阈值优先级,设置为5。高于 5 的优先级(优先级数小于 5)不归 FreeRTOS 管理 |
configMAX_SYSCALL_INTERRUPT_PRIORITY | 上个宏左移4位。低于该中断可以调用FreeRTOS的API,高于则禁止。 |
FreeRTOS开关中断
FreeRTOS 开关中断函数为 portENABLE_INTERRUPTS ()和 portDISABLE_INTERRUPTS(),本质是操作BASEPRI寄存器进行开关中断。
临界段代码
临界段代码也叫做临界区,是指那些必须完整运行,不能被打断的代码段。比如硬件初始化时,必须严格遵守时序,不能被中断打断。FreeRTOS进入临界段代码都需禁止中断,当临界段代码处理完后,再使能中断。
FreeRTOS与临界代码有两种4个,任务级临界段代码保护和中断级临界段代码保护。
任务级临界代码保护
taskENTER_CRITICAL()和 taskEXIT_CRITICAL()是任务级的临界代码保护,一个是进入临界段,一个是退出临界段,这两个函数是成对使用的,这函数的定义如下:
#define taskENTER_CRITICAL() portENTER_CRITICAL()
#define taskEXIT_CRITICAL() portEXIT_CRITICAL()
void vPortEnterCritical( void )
{
portDISABLE_INTERRUPTS();
uxCriticalNesting++;
if( uxCriticalNesting == 1 )
{
configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
}
}
void vPortExitCritical( void )
{
configASSERT( uxCriticalNesting );
uxCriticalNesting--;
if( uxCriticalNesting == 0 )
{
portENABLE_INTERRUPTS();
}
}
看出在进入函数 vPortEnterCritical()以后会首先关闭中断,然后给变量 uxCriticalNesting加一,uxCriticalNesting 是个全局变量,用来记录临界段嵌套次数的。函数 vPortExitCritical()是退出临界段调用的,函数每次将 uxCriticalNesting 减一,只有当 uxCriticalNesting 为 0 的时候才会调用函数 portENABLE_INTERRUPTS()使能中断。
任务级临界代码保护使用方法如下:
void taskcritical_test(void)
{
while(1)
{
taskENTER_CRITICAL(); //进入临界区
total_num+=0.01f;
printf("total_num 的值为: %.4f\r\n",total_num);
taskEXIT_CRITICAL(); //退出临界区
vTaskDelay(1000);
}
}
中断级临界段代码保护
函数 taskENTER_CRITICAL_FROM_ISR()和 taskEXIT_CRITICAL_FROM_ISR()中断级别临界段代码保护,是用在中断服务程序中的,而且这个中断的优先级一定要低于configMAX_SYSCALL_INTERRUPT_PRIORITY!
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define taskEXIT_CRITICAL_FROM_ISR(x) portCLEAR_INTERRUPT_MASK_FROM_ISR (x)
static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
mrs ulReturn, basepri //先读出 BASEPRI 的值,保存在 ulReturn 中
msr basepri, ulNewBASEPRI //将 configMAX_SYSCALL_INTERRUPT_PRIORITY 写入到寄存器 BASEPRI 中
dsb
isb
}
return ulReturn; //返回 ulReturn,退出临界区代码保护的时候要使用到此值!
}
中断级临界代码保护使用方法如下:文章来源:https://www.toymoban.com/news/detail-830797.html
//定时器 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); //清除中断标志位
}
}
链接
官方线上资料:RTOS
线下pdf:pdf
源码链接:源码
官方中断介绍:中断文章来源地址https://www.toymoban.com/news/detail-830797.html
到了这里,关于FreeRTOS 中断的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!