实验三、二值信号量
本节我们主要介绍的是Freertos的二值信号量 1、信号量介绍信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件,外部设备)来实现进程间通信,它本身只是一种外部资源的标识。信号量在此过程中负责数据操作的互斥、同步等功能。当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可用。如果大于0则资源可以请求,等于0则无资源可用,进程会进入睡眠状态直至资源可用。 Freertos提供了两种信号量:二值信号量和计数信号量。 二值信号量只有两种状态即0和1,当任务调用P(Take-信号量减1)操作判断信号量是否为1,若不为1则获取失败进入阻塞,等待信号量为1,当其他任务或中断进行V(Give-信号量加1)操作,此时信号量为1,使得阻塞的任务解除阻塞P操作成功对信号量减1后为0。如下图是实现任务与中断同步: 在中断以相对较慢的频率发生的情况下,上面描述的流程是足够而完美的。如果在延迟处理任务完成上一个中断事件的处理之前,新的中断事件又发生了,等效于将新的事件锁存在二值信号量中,使得延迟处理任务在处理完上一个事件之后,立即就可以处理新的事件。也就是说,延迟处理任务在两次事件处理之间,会有进入阻塞态的机会,因为信号量中锁存有一个事件,所以当SempaphoreTake()调用时,信号量立即有效。 一个二值信号量只可以锁存一个中断事件,在锁存的事件还未被处理之前,如果还有中断事件发生,那么后续发生的中断事件将会丢失。如果用计数信号量代替二值信号量,那么,这种丢中断的情形将可以避免。如下图是计数信号量对事件“计数(Count)”: 2、信号量操作Freertos提供了一系列函数对信号量进行创建、获取和给出等。 1)创建信号量 void vSemaphoreCreateBinary( xSemaphoreHandle xSemaphore );//二值信号量 xSemaphoreHandle xSemaphoreCreateCounting( unsigned portBASE_TYPE uxMaxCount, unsigned portBASE_TYPE uxInitialCount );//计数信号量 xSemaphore 信号量的句柄 uxMaxCount 最大计数值 uxInitialCount 信号量的初始计数值 返回值 NULL创建失败 否则信号量的句柄 2)信号量获取xSemaphoreTake( ) portBASE_TYPE xSemaphoreTake( xSemaphoreHandle xSemaphore, portTickType xTicksToWait ) xSemaphore 信号量的句柄 xTicksToWait 阻塞超时时间。任务进入阻塞态以等待信号量有效的最长时间。如果xTicksToWait为0,则xSemaphoreTake()在信号量无效时会立即返回。如果设置为portMAX_DELAY,并且在FreeRTOSConig.h中设定INCLUDE_vTaskSuspend为1,那么阻塞等待将没有超时限制。 返回值 pdPASS获取成功 pdFALSE获取失败 3)信号量给出xSemaphoreGiveFromISR()和xSemaphoreGive() portBASE_TYPE xSemaphoreGiveFromISR( xSemaphoreHandle xSemaphore, portBASE_TYPE *pxHigherPriorityTaskWoken ) //中断模式下 xSemaphoreGive(xSemaphoreHandle xSemaphore ) //任务模式下 xSemaphore 信号量的句柄 pxHigherPriorityTaskWoken pdTRUE:在中断退出前应当进行一次上下文切换,pdFALSE:若给出信号量是的解除阻塞任务优先级高于当前任务优先级(被中断的任务)则,函数内部将该值设为pdTRUE。 返回值 pdPASS给出成功 pdFAIL 无法给出。 3、实验分析本实验分为中断管理的二值信号量同步和计数信号量同步,在工程中创建两个任务,阻塞在获取信号量,分别使用两个外部中断给出信号量使得程序继续运行,其源代码如下(二值信号量和计数信号量创建不同): /*****主函数*****/ int main(void) { USART_Configuration(); //串口初始化 LED_Configuration(); vSemaphoreCreateBinary( xBinarySemaphore1 );//创建二值信号量 vSemaphoreCreateBinary( xBinarySemaphore2 );//创建二值信号量 xSemaphoreTake(xBinarySemaphore1,portMAX_DELAY) ; //由于创建时进行了一次V操作,因此这里进行一次P操作 xSemaphoreTake(xBinarySemaphore2,portMAX_DELAY) ; NVIC_Configuration(); GpioLed_Init(); //LED灯初始化 USART1_Configuration(); printf("\r\n"); printf("\r\n二值信号量实验"); xTaskCreate( vLED1Task, ( signed portCHAR * ) "LED", configMINIMAL_STACK_SIZE, NULL, vLED1Task_PRIORITY, NULL ); //创建一个让LED1闪烁的任务 xTaskCreate( vLED2Task, ( signed portCHAR * ) "LED", configMINIMAL_STACK_SIZE, NULL, vLED2Task_PRIORITY, NULL ); //创建一个让LED2闪烁的任务 vTaskStartScheduler(); //开始任务调度 while(1); } //LED1闪烁任务 void vLED1Task( void *pvParameters ) { while(1) { xSemaphoreTake(xBinarySemaphore1,portMAX_DELAY) ; printf("\r\nvLED1Task"); } } //LED2闪烁任务 void vLED2Task( void *pvParameters ) { while(1) { xSemaphoreTake(xBinarySemaphore2,portMAX_DELAY) ; printf("\r\nvLED2Task"); } } void EXTI9_5_IRQHandler(void) { static portBASE_TYPE xHigherPriorityTaskWoken1; xHigherPriorityTaskWoken1 = pdFALSE; if(EXTI_GetITStatus(EXTI_Line8)!= RESET) { EXTI_ClearITPendingBit(EXTI_Line8); xSemaphoreGiveFromISR( xBinarySemaphore1, &xHigherPriorityTaskWoken1 ); } } void EXTI3_IRQHandler(void) { static portBASE_TYPE xHigherPriorityTaskWoken2; xHigherPriorityTaskWoken2 = pdFALSE; if(EXTI_GetITStatus(EXTI_Line3)!= RESET) { EXTI_ClearITPendingBit(EXTI_Line3); xSemaphoreGiveFromISR( xBinarySemaphore2, &xHigherPriorityTaskWoken2 ); } } 程序使用按键USER1和USER2连接到外部中断线,USER1控制向xBinarySemaphore1给出信号,USER2控制向xBinarySemaphore2给出信号,如下图是按下USER1和USER2后的串口输出结果:
|