点击上方"蓝字"关注我们
RTOS | 那么什么是RTOS?三大操作系统?(第十四天) |
---|
FreeRTOS | 原理介绍和资源get(第十四天) |
FreeRTOS | STM32F407 FreeRTOS移植(第十四天) |
FreeRTOS | 任务管理(第十四天) |
FreeRTOS | 内核控制函数和时间管理(第十四天) |
>>> 1.Cortex-M3和M4的中断介绍 Cortex-M3和M4的NVIC最多支持240个IRQ(中断请求)、一个不可屏蔽中断(NMI)、一个SysTick(滴答定时器)定时器中断和多个系统异常。 (1)PRIMASK寄存器 PRIMASK寄存器可以禁止除NMI和HardFault两个中断外的所有的异常和中断。 CPSIE I; // 清除PRIMASK(使能中断) CPSID I; // 设置PRIMASK(禁止中断) uC/OS中就是通过该方式实现中断的使能和禁止。 (2)FAULTMASK寄存器 FAULTMASK寄存器会把异常的优先级提升到-1,除复位外,其他的异常和中断都能屏蔽掉。 CPSIE F; // 清除FAULTMASK CPSID F; // 设置FAULTMASK (3)BASEPRI寄存器 该寄存器可以屏蔽低于某一个阈值的中断。 注意:FreeRTOS的开关中断就是操作BASEPRI寄存器来实现的,它可以关闭低于某个阈值的中断,高于这个阈值的中断就不会被关闭。
>>> (1)configPRIO_BITS 此宏用来设置MCU使用几位优先级。 (2)configLIBRARY_LOWEST_INTERRUPT_PRIORITY 此宏用来设置最低优先级。 (3)configKEYNEL_INTERRUPT_PRIORITY 此宏是configLIBRARY_LOWEST_INTERRUPT_PRIORITY左移得来的。 此宏用来配置系统调用(PendSV)和滴答定时器(Systick)的中断优先级。 (4)configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 此宏用来设置FreeRTOS系统可管理的最大优先级,也就是BASEPRI寄存器中存放的阈值。 (5)configMAX_SYSCALL_INTERRUPT_PRIORITY 此宏是configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY左移得来的,系统可管理的最高中断优先级。即中断优先级数值在 0~4 的这些中断是不受 FreeRTOS 管理的,不可被屏蔽,也不能调用 FreeRTOS 中的 API 函数接口,而中断优先级在 5 到 15 的这些中断是受到系统管理,可以被屏蔽的。
>>> FreeRTOS开关中断的函数为portENABLE_INTERRUPTS()和portDISABLE_INTERRUPTS(),它们都是宏定义。 函数的定义位于portmacro.h中: #define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI() // 关中断 #define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 ) // 开中断
>>> taskENTER_CRITICAL()和 taskEXIT_CRITICAL()是任务级的临界代码保护,一个是进入临界段,一个是退出临界段,这两个函数是成对使用的,这函数的定义如下: #define taskENTER_CRITICAL portENTER_CRITICAL() #define taskEXIT_CRITICAL portEXIT_CRITICAL() 函数vPortEnterCritical()和vPortExitCritical()在文件 port.c中。 vPortEnterCritical函数
void vPortEnterCritical( void ){ portDISABLE_INTERRUPTS(); uxCriticalNesting++; /* This is not the interrupt safe version of the enter critical function so assert() if it is being called from an interrupt context. Only API functions that end in "FromISR" can be used in an interrupt. Only assert if the critical nesting count is 1 to protect against recursive calls if the assert function also uses a critical section. */ if( uxCriticalNesting == 1 ) { configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 ); }}
vPortExitCritical函数
void vPortExitCritical( void ){ configASSERT( uxCriticalNesting ); uxCriticalNesting--; if( uxCriticalNesting == 0 ) { portENABLE_INTERRUPTS(); }}
>>>可以看出在进入函数vPortEnterCritical()以后会首先关闭中断,然后给变量uxCriticalNesting加一,uxCriticalNesting是个全局变量,用来记录临界段嵌套次数的。函数vPortExitCritical()是退出临界段调用的,函数每次将uxCriticalNesting减一,只有当uxCriticalNesting为0的时候才会调用函数portENABLE_INTERRUPTS()使能中断。 这样保证了在有多个临界段代码的时候不会因为某一个临界段代码的退出而打乱其他临界段的保护,只有所有的临界段代码都退出以后才会使能中断!
>>>临界区代码一定要精简!因为进入临界区会关闭中断,这样会导致优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断得不到及时的响应!
static void app_task2(void* pvParameters){ for(;;) { /*进入临界区*/ taskENTER_CRITICAL(); printf("app_task2 runing...........\n"); /*退出临界区*/ taskEXIT_CRITICAL(); vTaskDelay(3); }} static void app_task3(void* pvParameters){ int tim_count = 0; for(;;) { /*进入临界区*/ taskENTER_CRITICAL(); printf("app_task3 runing...........\n"); /*退出临界区*/ taskEXIT_CRITICAL(); vTaskDelay(3); }}
>>>函数taskENTER_CRITICAL_FROM_ISR()和 taskEXIT_CRITICAL_FROM_ISR()中断级别临界段代码保护,是用在中断服务程序中的,而且这个中断的优先级一定要低于configMAX_SYSCALL_INTERRUPT_PRIORITY!原因前面已经说了。这两个函数在文件task.h中有如下定义:
1.taskENTER_CRITICAL_FROM_ISR与taskEXIT_CRITICAL_FROM_ISR函数文件路径:task.h#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x ) |
---|
2.portSET_INTERRUPT_MASK_FROM_ISR与ulPortRaiseBASEPRI函数文件路径:portmacro.h#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI() |
// 关中断// 向basepri中写入configMAX_SYSCALL_INTERRUPT_PRIORITY,// 表明优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断都会被屏蔽static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void ){ uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY; __asm { /* Set BASEPRI to the max syscall priority to effect a critical section. */ mrs ulReturn, basepri msr basepri, ulNewBASEPRI dsb isb } return ulReturn;}
3.portCLEAR_INTERRUPT_MASK_FROM_ISR与vPortSetBASEPRI函数文件路径:portmacro.h |
---|
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x)// 开中断// 向basepri中写入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 }}
u8 rx_flag = 0; //rx_flag = 1表示接收数据完毕u8 buffer[32] = {0};u8 rx_buffer[32] = {0};u8 rx_i, count = 0;void USART1_IRQHandler(void){ uint32_t ulReturn; /*进入中断临界区,要保护临界区资源*/ ulReturn = taskENTER_CRITICAL_FROM_ISR(); if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET) { //从串口1获取数据 buffer[count++] = USART_ReceiveData(USART1); if(buffer[count-1] == ':') //判断是否为结尾符 { //过滤结尾符,并将数据存放在rx_buffer for(rx_i=0; rx_i<(count-1); rx_i++) { rx_buffer[rx_i] = buffer[rx_i]; } rx_flag = 1; //置标志位为1 count = 0; //数组下标设置为0,下次数组从数组下标0开始接收数据 memset(buffer, 0, sizeof(buffer)); } //清空更新中断标志位 USART_ClearITPendingBit(USART1, USART_IT_RXNE); } /*退出临界资源保护*/ taskEXIT_CRITICAL_FROM_ISR(ulReturn);}
>>>当然这里的临界区操作仅仅只是开关中断,许多自己公司写的,或者裁剪的都是这种简约开关中断版本,所以当调用Function1函数以后,后面的代码就不在临界区内了,此时就有可能会存在共享问题。
void Fuction1(void){ taskENTER_CRITICAL();//进入临界区 //do something~ taskEXIT_CRITICAL();//退出临界区}void Fuction2(void){ taskENTER_CRITICAL();//进入临界区 ...... Fuction1(); ...... //do something~ taskEXIT_CRITICAL();//退出临界区}
总结 目前已分享内容
>>>通过网盘分享的文件:FreeRTOS 链接: https://pan.baidu.com/s/1mxkWdl-gD9b4I3_cmPV_-A?pwd=5w6t 提取码: 5w6t
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。