FreeRTOS学习笔记-------信号量

本文详细介绍了信号量在FreeRTOS中的实现,将其与队列关联,讨论了二进制信号量和计数型信号量的区别,以及如何使用它们进行同步和互斥操作,通过示例代码展示了动态创建信号量和使用xSemaphoreGive/take函数的应用。
摘要由CSDN通过智能技术生成

信号量介绍

队列可以用于传输数据,但不需要传输数据时,可以使用信号量,信号量传输的是状态。信号量的示意图如下所示:

在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();
	}
}

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值