信号量介绍
队列可以用于传输数据,但不需要传输数据时,可以使用信号量,信号量传输的是状态。信号量的示意图如下所示:
在FreeRTOS中,信号量本质就是队列,是一个只关心队列长度,不关心队列数据内容的队列。官方源码如下所示:
QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount,
const UBaseType_t uxInitialCount )
{
QueueHandle_t xHandle = NULL;
if( ( uxMaxCount != 0 ) &&
( uxInitialCount <= uxMaxCount ) )
{
xHandle = xQueueGenericCreate( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE );
if( xHandle != NULL )
{
( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount;
traceCREATE_COUNTING_SEMAPHORE();
}
else
{
traceCREATE_COUNTING_SEMAPHORE_FAILED();
}
}
else
{
configASSERT( xHandle );
mtCOVERAGE_TEST_MARKER();
}
return xHandle;
}
由官方源码可知, 创建信号量时实际创建的是uxItemSize为0的队列,使用uxMessagesWaiting表示计数值。
当计数值为0时获取信号量,任务将进入阻塞状态。当计数值达到最大时释放信号量,将返回错误(队列是数据满时再写入数据,如果没有超时,则阻塞。有关队列内容可参考文章FreeRTOS学习笔记---------队列)。
信号量有两种:二进制信号量和计数型信号量。
信号量的计数值都有限制:限定了最大值。如果最大值为1,则是二进制信号量;否则就是计数型信号量。两者的区别如下表所示:
二进制信号量 | 计数型信号量 |
被创建时初始值为0 | 被创建时初始值可以设定 |
信号量的使用
信号量的创建方式可分为动态和静态,相关API如下所示:
使用xSemaphoreGive函数使计数值加1,使用xSemaphoreTake函数使计数值减1(当计数值为0时调用此函数,将会阻塞),函数原型如下:
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait
);
使用信号量实现同步与互斥的示例代码分别如下所示:
/*
* Configure the clocks, GPIO and other peripherals as required by the demo.
*/
static void prvSetupHardware( void );
/*-----------------------------------------------------------*/
void vmainTask1Function( void * param);
void vmainTask2Function( void * param);
static int sum = 0;
/*-----------------------------------------------------------*/
int main( void )
{
SemaphoreHandle_t semaphoreHandleCal;
prvSetupHardware();
printf("Hello world!\r\n");
// 创建计数型信号量,最大值为10,初始值为0
semaphoreHandleCal = xSemaphoreCreateCounting(10, 0);
// 实现同步
xTaskCreate(vmainTask1Function, "task1", 100, (void *)semaphoreHandleCal, 1, NULL); // 动态创建任务
xTaskCreate(vmainTask2Function, "task2", 100, (void *)semaphoreHandleCal, 1, NULL); // 动态创建任务
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
/*-----------------------------------------------------------*/
/*-----------------------------------------------------------*/
static void prvSetupHardware( void )
{
/* Start with the clocks in their expected state. */
RCC_DeInit();
/* Enable HSE (high speed external clock). */
RCC_HSEConfig( RCC_HSE_ON );
/* Wait till HSE is ready. */
while( RCC_GetFlagStatus( RCC_FLAG_HSERDY ) == RESET )
{
}
/* 2 wait states required on the flash. */
*( ( unsigned long * ) 0x40022000 ) = 0x02;
/* HCLK = SYSCLK */
RCC_HCLKConfig( RCC_SYSCLK_Div1 );
/* PCLK2 = HCLK */
RCC_PCLK2Config( RCC_HCLK_Div1 );
/* PCLK1 = HCLK/2 */
RCC_PCLK1Config( RCC_HCLK_Div2 );
/* PLLCLK = 8MHz * 9 = 72 MHz. */
RCC_PLLConfig( RCC_PLLSource_HSE_Div1, RCC_PLLMul_9 );
/* Enable PLL. */
RCC_PLLCmd( ENABLE );
/* Wait till PLL is ready. */
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
/* Select PLL as system clock source. */
RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK );
/* Wait till PLL is used as system clock source. */
while( RCC_GetSYSCLKSource() != 0x08 )
{
}
/* Enable GPIOA, GPIOB, GPIOC, GPIOD, GPIOE and AFIO clocks */
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOC
| RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE );
/* SPI2 Periph clock enable */
RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );
/* Set the Vector Table base address at 0x08000000 */
NVIC_SetVectorTable( NVIC_VectTab_FLASH, 0x0 );
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
/* Configure HCLK clock as SysTick clock source. */
SysTick_CLKSourceConfig( SysTick_CLKSource_HCLK );
serialPortInit();
}
/*-----------------------------------------------------------*/
void vmainTask1Function( void * param)
{
int i = 0;
int sum_task = 0;
while(1) {
for(i = 0; i < 100000; i ++) {
sum_task ++;
}
sum = sum_task; // 使用sum暂存结果,因为轮到task2执行时,sum_task结果已经发生变化
xSemaphoreGive((SemaphoreHandle_t)param); // 信号量计数值加1
}
}
void vmainTask2Function( void * param)
{
while(1) {
xSemaphoreTake((SemaphoreHandle_t)param, portMAX_DELAY);
printf("sum = %d\r\n", sum);
}
}
/*
* Configure the clocks, GPIO and other peripherals as required by the demo.
*/
static void prvSetupHardware( void );
/*-----------------------------------------------------------*/
void vmainTask3Function( void * param);
void vmainTask4Function( void * param);
/*-----------------------------------------------------------*/
int main( void )
{
SemaphoreHandle_t semaphoreHandleUART;
prvSetupHardware();
printf("Hello world!\r\n");
// 创建二进制信号量,计数值初始值为0
semaphoreHandleUART = xSemaphoreCreateBinary();
xSemaphoreGive(semaphoreHandleUART); // 将计数值设为1
// 实现互斥
xTaskCreate(vmainTask3Function, "task3", 100, (void *)semaphoreHandleUART, 1, NULL); // 动态创建任务
xTaskCreate(vmainTask4Function, "task4", 100, (void *)semaphoreHandleUART, 1, NULL); // 动态创建任务
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
/*-----------------------------------------------------------*/
/*-----------------------------------------------------------*/
static void prvSetupHardware( void )
{
/* Start with the clocks in their expected state. */
RCC_DeInit();
/* Enable HSE (high speed external clock). */
RCC_HSEConfig( RCC_HSE_ON );
/* Wait till HSE is ready. */
while( RCC_GetFlagStatus( RCC_FLAG_HSERDY ) == RESET )
{
}
/* 2 wait states required on the flash. */
*( ( unsigned long * ) 0x40022000 ) = 0x02;
/* HCLK = SYSCLK */
RCC_HCLKConfig( RCC_SYSCLK_Div1 );
/* PCLK2 = HCLK */
RCC_PCLK2Config( RCC_HCLK_Div1 );
/* PCLK1 = HCLK/2 */
RCC_PCLK1Config( RCC_HCLK_Div2 );
/* PLLCLK = 8MHz * 9 = 72 MHz. */
RCC_PLLConfig( RCC_PLLSource_HSE_Div1, RCC_PLLMul_9 );
/* Enable PLL. */
RCC_PLLCmd( ENABLE );
/* Wait till PLL is ready. */
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
/* Select PLL as system clock source. */
RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK );
/* Wait till PLL is used as system clock source. */
while( RCC_GetSYSCLKSource() != 0x08 )
{
}
/* Enable GPIOA, GPIOB, GPIOC, GPIOD, GPIOE and AFIO clocks */
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOC
| RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE );
/* SPI2 Periph clock enable */
RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );
/* Set the Vector Table base address at 0x08000000 */
NVIC_SetVectorTable( NVIC_VectTab_FLASH, 0x0 );
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
/* Configure HCLK clock as SysTick clock source. */
SysTick_CLKSourceConfig( SysTick_CLKSource_HCLK );
serialPortInit();
}
/*-----------------------------------------------------------*/
void vmainTask3Function( void * param)
{
while(1) {
xSemaphoreTake((SemaphoreHandle_t)param, portMAX_DELAY);
printf("task3\r\n");
xSemaphoreGive((SemaphoreHandle_t)param);
taskYIELD();
}
}
void vmainTask4Function( void * param)
{
while(1) {
xSemaphoreTake((SemaphoreHandle_t)param, portMAX_DELAY);
printf("task4\r\n");
xSemaphoreGive((SemaphoreHandle_t)param);
taskYIELD();
}
}