网站建设项目策划书格式/深圳网站制作
1. 计数信号量是二值信号量的扩展,可以用来记录中断发生的次数
2. 计数信号量
如果在延迟处理任务完成上一个中断事件的处理之前,新的中断事件又发生了,等效于将新的事件锁存在二值信号量中,使得延迟处理任务在处理完上一个事件之后,立即就可以处理新的事件。也就是说,延迟处理任务在两次事件处理之间,不会有进入阻塞态的机会,因为信号量中锁存有一个事件,所以当 xSempaphoreTake()调用时,信号量立即有效。
一个二值信号量最多只可以锁存一个中断事件。在锁存的事件还未被处理之前,如果还有中断事件发生,那么后续发生的中断事件将会丢失。如果用计数信号量代替二值信号量,那么,这种丢中断的情形将可以避免。
就如同我们可以把二值信号量看作是只有一个数据单元的队列一样,计数信号量可以看作是深度大于 1 的队列。任务其实对队列中存储的具体数据并不感兴趣——其只关心队列是空还是非空。
计数信号量每次被给出(Given),其队列中的另一个空间将会被使用。队列中的有效数据单元个数就是信号量的”计数(Count)”值。
3. 计数信号量用作事件计数
在这种用法中,每次事件发生时,中断服务例程都会“给出(Give)”信号量——信号量在每次被给出时其计数值加 1。延迟处理任务每处理一个任务都会”获取(Take)”一次信号量——信号量在每次被获取时其计数值减 1。信号量的计数值其实就是已发生事件的数目与已处理事件的数目之间的差值。
用于事件计数的计数信号量,在被创建时其计数值被初始化为 0。
4. 计数信号量用作资源管理
信号量的计数值用于表示可用资源的数目。一个任务要获取资源的控制权,其必须先获得信号量——使信号量的计数值减 1。当计数值减至 0,则表示没有可用资源。当任务利用资源完成工作后,将给出(归还)信号量——使信号量的计数值加 1。用于资源管理的信号量,在创建时其计数值被初始化为可用资源总数。
5. 创建信号量 xSemaphoreCreateCounting( uxMaxCount, uxInitialCount )
FreeRTOS 中所有种类的信号量句柄都由声明为 xSemaphoreHandle 类型的变量保存。
信号量在使用前必须先被创建。使用 xSemaphoreCreateCounting() API 函数来创建一个计数信号量。
6. 利用计数信号量对任务和中断进行同步
6.1 中断服务函数
/* 在信号量使用之前必须先创建。本例中创建了一个计数信号量。此信号量的最大计数值为10,初始计数值为0 */
xCountingSemaphore = xSemaphoreCreateCounting( 10, 0 );static void __interrupt __far vExampleInterruptHandler( void )
{static portBASE_TYPE xHigherPriorityTaskWoken;xHigherPriorityTaskWoken = pdFALSE;/* 多次给出信号量。第一次给出时使得延迟处理任务解除阻塞。后续给出用于演示利用被信号量锁存事件,以便延迟处理任何依序对这些中断事件进行处理而不会丢中断。用这种方式来模拟处理器产生多个中断,尽管这些事件只是在单次中断中模拟出来的 */xSemaphoreGiveFromISR( xCountingSemaphore, &xHigherPriorityTaskWoken );xSemaphoreGiveFromISR( xCountingSemaphore, &xHigherPriorityTaskWoken );xSemaphoreGiveFromISR( xCountingSemaphore, &xHigherPriorityTaskWoken );if( xHigherPriorityTaskWoken == pdTRUE ){portYIELD();}
}
6.2 中断延迟服务函数
static void vHandlerTask( void *pvParameters )
{for( ;; ){/* P operation, 使用信号量等待一个事件。任务被无超时阻塞, 该任务运行到这里会被挂起 vHandlerTask, 后面的不会执行了, 直到等到了信号量才会再次运行。此处也没有必要检测返回值 */xSemaphoreTake(xBinarySemaphore, portMAX_DELAY);/* 程序运行到这里时,事件必然已经发生。本例的事件处理只是简单地打印输出一个信息 */printf("Handler task - Processing event.\r\n");}
}
6.3 中断触发
static void vPeriodicTask(void *pvParameters)
{for(;;){/* 此任务通过每500毫秒产生一个软件中断来”模拟”中断事件 */vTaskDelay( 500 / portTICK_RATE_MS );/* 产生中断,并在产生之前和之后输出信息,以便在执行结果中直观直出执行流程 */printf("Periodic task - About to generate an interrupt.\r\n");NVIC_SetPendingIRQ(IRQn); // creat a interrupt by stm32fxx mcuprintf("Periodic task - Interrupt generated.\r\n\r\n\r\n");}
}
6.4 在主函数创建计数信号量 & 任务
SemaphoreHandle_t xCountingSemaphore = NULL;int main(void)
{xCountingSemaphore = xSemaphoreCreateCounting( 10, 0 ); // create a semaphoreNVIC_SetPriority(IRQn, priority); // set a soft interrupt for stm32fxx mcu// check wether the semaphore has been created successif(xBinarySemaphore != NULL){/* 创建延迟处理任务。此任务将与中断同步。延迟处理任务在创建时使用了一个较高的优先级,以保证中断退出后会被立即执行。在本例中,为延迟处理任务赋予优先级3 */xTaskCreate(vHandlerTask, "Handler", 1000, NULL, 3, NULL);/* 创建一个任务用于周期性产生软件中断。此任务的优先级低于延迟处理任务。每当延迟处理任务切出阻塞态,就会抢占周期任务*/xTaskCreate(vPeriodicTask, "Periodic", 1000, NULL, 1, NULL);vTaskStartScheduler();}for(;;){}
}