嵌入式实时系统需要对整个系统环境产生的事件作出反应。 这些事件对处理时间和响应时间都有不同的要求。在各种情况下,都需要作出合理的判断,以达到最佳事件处理的实现策略:
值得注意的是:只有以”FromISR”或”FROM_ISR”结束的 API 函数或宏才可以在中断服务例程中。
概念:
二值信号量可以在某个特殊的中断发生时,让任务解除阻塞,相当于让任务与中断同步。这样就可以让中断事件处理量大的工作在同步任务中完成,中断服务例程(ISR)中只是快速处理少部份工作。 如此,中断处理可以说是被”推迟(deferred)”到一个”处理(handler)”任务。
如果某个中断处理要求特别紧急,其延迟处理任务的优先级可以设为最高,
FreeRTOS的各种信号量都被存储在xSemaphoreHandle类型的变量中
创建二值信号量:
void vSemaphoreCreateBinary( xSemaphoreHandle xSemaphore );除了互斥信号量,所有 类型信号量都可以用函数xSemaphoreTake获取:(返回值:pdPASS pdFALSE)
portBASE_TYPE xSemaphoreTake( xSemaphoreHandle xSemaphore, portTickType xTicksToWait );//参数:获取的信号,超时等待时间//如 果 把 xTicksToWait 设 置 为 portMAX_DELAY , 并 且 在FreeRTOSConig.h 中设定 INCLUDE_vTaskDelayUntil 为 1,那么阻塞等待将没有超时限制除 互 斥 信 号 量 外 , FreeRTOS 支 持 的 其 它 类 型 的 信 号 量 都 可 以 通 过 调 用
xSemaphoreGiveFromISR()给出
portBASE_TYPE xSemaphoreGiveFromISR( xSemaphoreHandle xSemaphore, portBASE_TYPE *pxHigherPriorityTaskWoken );参数解释:
pxHigherPriorityTaskWoken :
对某个信号量而言,可能有不止一个任务处于阻塞态在等待其有效。调用 xSemaphoreGiveFromISR()会让信号量变为有效,所以会让其中一个等待任务切出阻塞态。如果调用 xSemaphoreGiveFromISR()使得一个任务解除阻塞,并且这个任务的优先级高于当前任务(也就是被中断的任务),那么 xSemaphoreGiveFromISR()会在 函 数 内 部 将 *pxHigherPriorityTaskWoken 设 为pdTRUE。如 果 xSemaphoreGiveFromISR() 将 此 值 设 为pdTRUE,则在中断退出前应当进行一次上下文切换。这样才能保证中断直接返回到就绪态任务中优先级最高的任务中。
1、中断函数中使用xSemaphoreGiveFromISR()
2、使用任务通知功能代替二值信号量(更快 代码量更少)
代码示例:main.c
#include "stm32f4xx.h"#include "FreeRTOS.h"#include "task.h"#include "queue.h"#include "list.h"#include "my_include.h"//自定义头文件库extern xQueueHandle xQueue;static void BSP_init(void);uint8_t ucTemp=0;uint8_t RxFlag=0;int main(void){xQueue=xQueueCreate(5,sizeof(long));//创建队列BSP_init();APPTaskCreate();vTaskStartScheduler();while(1);}//设备初始化static void BSP_init(void){NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);// 中断分组:第四组,4位全用于设置抢占优先级,0位由于响应优先级,中断中也要设置为第四组LED_GPIO_Config();Key_GPIO_Config();BEEP_GPIO_Config();USART2_Config();}bsp_task.c
#include "stm32f4xx.h"#include "FreeRTOS.h"#include "task.h"#include "queue.h"#include "list.h"#include "semphr.h"#include "my_include.h"//全局队列xQueueHandle xQueue;//创建二值信号量句柄,为空SemaphoreHandle_t Binary_USART=NULL;//任务句柄TaskHandle_t xHandleTaskKey;TaskHandle_t xHandleUSART;static void vTaskCMD(void *pvParameters){BaseType_t err=pdFAIL;Binary_USART=xSemaphoreCreateBinary();//创建二值量,此时Binary_USART非空while(1){if(Binary_USART!=NULL)//非空{printf("等待二值信号n");err=xSemaphoreTake(Binary_USART,portMAX_DELAY);//portMAX_DELAY,超时等待无限长时间printf("获得二值信号n");}}}//任务key1 2 ,0.03秒扫描一次,key1按下 LED任务挂起,Key2按下,任务恢复static void vTaskKey(void *pvParameters){while(1){if(Key_Scan(KEY1_GPIO_PORT,KEY1_PIN)==KEY_ON){while(Key_Scan(KEY1_GPIO_PORT,KEY1_PIN)==KEY_ON);//等待按键放开xSemaphoreGive(Binary_USART);}if(Key_Scan(KEY2_GPIO_PORT,KEY2_PIN)==KEY_ON){while(Key_Scan(KEY2_GPIO_PORT,KEY2_PIN)==KEY_ON);//等待按键放开xSemaphoreGive(Binary_USART);}vTaskDelay(30);//阻塞}}//任务汇总void APPTaskCreate(void){xTaskCreate(vTaskCMD,"Task USART CMD",300,NULL,6,&xHandleUSART);//USART 二值量控制LEDxTaskCreate(vTaskKey,"Task Key",512,NULL,5,&xHandleTaskKey);//key任务}stm32f4xx_it.c
extern TaskHandle_t xHandleTaskLed1;extern SemaphoreHandle_t Binary_USART;//USART2中断函数void USART2_IRQHandler (void){static BaseType_t xHigherPriorityTaskWoke;//保存是否有更高优先级任务就绪if((USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET))//收到数据{ucTemp=USART_ReceiveData(USART2);xSemaphoreGiveFromISR(Binary_USART,&xHigherPriorityTaskWoke);//给信号}portYIELD_FROM_ISR( xHigherPriorityTaskWoke );//注意要放在if外,放在if内会死掉, 这里硬件的中断优先级必须设置为大于5,这与FreeRTOSConfig.h的配置有关}注意:
CORTEX内核的优先级是数值越小优先级越高,即0是最高优先级。FreeRTOS正好相反,为了满足某些应用对中断实时性要求高的需求,使得中断优先级高于某个值之后,就不能调用操作系统的内核函数来提高实时性。而我们经常所犯的错误就是将中断的优先级设置的高于这个值,却还在中断中调用操作系统提供的API引起的。当把优先级改小也就是数值改大之后,程序能够正常运行。
注意点: 1:首先要将中断的嵌套设置为抢占优先级。 2:将freertos系统内核中断(configKERNEL_INTERRUPT_PRIORITY)的优先级设置成最低。3:将freertos的最大系统调用中断优先级(configMAX_SYSCALL_INTERRUPT_PRIORITY)设置为合适的优先级,比如11。 4:如果有用户中断函数调用到freertos提供的系统函数,一定要使用带FromISR的freertos提供的系统函数,并且这个用户中断的优先级一定要在configKERNEL_INTERRUPT_PRIORITY和configMAX_SYSCALL_INTERRUPT_PRIORITY的优先级之间。 5:其他没有调用到freertos提供的系统函数的中断的优先级没有特别要求。 FREERTOS中的优先级数值设定的参数是configMAX_SYSCALL_INTERUPT_PRORITY。默认值是5,所以要想在中断中使用操作系统函数需要将中断的优先级设置的大于等于5.