04_FreeRTOS 计数信号量
本文将介绍:
a. 使用CMSIS API ,介绍FreeRTOS中计数信号量
b. 不使用CMSIS API,直接使用FreeRTOS函数
-
简介
计数信号量可用于控制对资源的访问。要获得对资源的控制,任务必须首先获得信号量。因此减少了信号量计数值。当计数值达到零时,将没有可用资源。当任务使用资源完成时,它将“give”信号量,从而增加信号量计数值。 -
设置部分
CubeMX中启用计数信号量:
-
FreeRTOS中计数信号量
3.1 使用CMSIS API ,介绍FreeRTOS中计数信号量
【在上一篇中修改过来】
声明任务函数和计数信号量、定义变量osThreadId Task1Handle; osThreadId Task2Handle; osThreadId Task3Handle; osThreadId Task4Handle; osSemaphoreId BinarySem01Handle; /* USER CODE BEGIN PV */ osSemaphoreId CountingSem; int resourve[3] = {111,222,333}; uint8_t indx = 0; // uart receive uint8_t rx_data = 0;
定义创建,线程的优先级由高到低:
```c++
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
CountingSem = xQueueCreateCountingSemaphore(3,0);
if (CountingSem == NULL) printf( "Unable to Create Semaphore\n\n");
else printf( "Counting Semaphore created successfully\n\n");
/* Create the thread(s) */
/* definition and creation of Task1 */
osThreadDef(Task1, Task1_init, osPriorityRealtime, 0, 512);
Task1Handle = osThreadCreate(osThread(Task1), NULL);
/* definition and creation of Task2 */
osThreadDef(Task2, Task2_init, osPriorityHigh, 0, 512);
Task2Handle = osThreadCreate(osThread(Task2), NULL);
/* definition and creation of Task3 */
osThreadDef(Task3, Task3_init, osPriorityAboveNormal, 0, 512);
Task3Handle = osThreadCreate(osThread(Task3), NULL);
/* definition and creation of Task4 */
osThreadDef(Task4, Task4_init, osPriorityNormal, 0, 512);
Task4Handle = osThreadCreate(osThread(Task4), NULL);
```
首先初始化接受HAL_UART_Receive_IT(&huart1, &rx_data, 1);
函数。再把HAL_UART_Receive_IT
放在串口全局中断里,可以接收指定长度的字符串,并在接收完成之后产生中断。当给串口一输入字符串g后,就能give 3张令牌。
```c++
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_UART_Receive_IT(huart, &rx_data, 1);
if (rx_data == 'g')
{
// release the semaphore here
/* The xHigherPriorityTaskWoken parameter must be initialized to pdFALSE as
it will get set to pdTRUE inside the interrupt safe API function if a
context switch is required. */
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(CountingSem, &xHigherPriorityTaskWoken); // ISR SAFE VERSION
xSemaphoreGiveFromISR(CountingSem, &xHigherPriorityTaskWoken); // ISR SAFE VERSION
xSemaphoreGiveFromISR(CountingSem, &xHigherPriorityTaskWoken); // ISR SAFE VERSION
/* Pass the xHigherPriorityTaskWoken value into portEND_SWITCHING_ISR(). If
xHigherPriorityTaskWoken was set to pdTRUE inside xSemaphoreGiveFromISR()
then calling portEND_SWITCHING_ISR() will request a context switch. If
xHigherPriorityTaskWoken is still pdFALSE then calling
portEND_SWITCHING_ISR() will have no effect */
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
}
}
```
```c++
/* USER CODE END Header_Task1_init */
void Task1_init(void const * argument)
{
/* USER CODE BEGIN 5 */
int semcount = 0;
/* Infinite loop */
printf("Give 3 semaphores at the beginning\n");
xSemaphoreGive(CountingSem);
xSemaphoreGive(CountingSem);
xSemaphoreGive(CountingSem);
for(;;)
{
//test
printf("Enter Task1 to get the Semaphore\n");
semcount = uxSemaphoreGetCount(CountingSem);
printf("Tokens available are : %d\n",semcount);
xSemaphoreTake(CountingSem,portMAX_DELAY);
printf("Leaving Task1 , Data ACCESSED is %d\n",resourve[indx]);
printf("------------------------------------------\n");
indx++;
if(indx>2)
indx = 0;
vTaskDelay(3000);
}
/* USER CODE END 5 */
}
/* USER CODE BEGIN Header_Task2_init */
/**
* @brief Function implementing the Task2 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_Task2_init */
void Task2_init(void const * argument)
{
/* USER CODE BEGIN Task2_init */
int semcount = 0;
/* Infinite loop */
for(;;)
{
printf("Enter Task2 to get the Semaphore\n");
semcount = uxSemaphoreGetCount(CountingSem);
printf("Tokens available are : %d\n",semcount);
xSemaphoreTake(CountingSem,portMAX_DELAY);
printf("Leaving Task2 , Data ACCESSED is %d\n",resourve[indx]);
printf("------------------------------------------\n");
indx++;
if(indx>2)
indx = 0;
vTaskDelay(2000);
}
/* USER CODE END Task2_init */
}
/* USER CODE BEGIN Header_Task3_init */
/**
* @brief Function implementing the Task3 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_Task3_init */
void Task3_init(void const * argument)
{
/* USER CODE BEGIN Task3_init */
int semcount = 0;
/* Infinite loop */
for(;;)
{
printf("Enter Task3 to get the Semaphore\n");
semcount = uxSemaphoreGetCount(CountingSem);
printf("Tokens available are : %d\n",semcount);
xSemaphoreTake(CountingSem,portMAX_DELAY);
printf("Leaving Task3 , Data ACCESSED is %d\n",resourve[indx]);
printf("------------------------------------------\n");
indx++;
if(indx>2)
indx = 0;
vTaskDelay(1000);
}
/* USER CODE END Task3_init */
}
/* USER CODE BEGIN Header_Task4_init */
/**
* @brief Function implementing the Task4 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_Task4_init */
void Task4_init(void const * argument)
{
/* USER CODE BEGIN Task4_init */
int semcount = 0;
/* Infinite loop */
for(;;)
{
printf("Enter Task4 to get the Semaphore\n");
semcount = uxSemaphoreGetCount(CountingSem);
printf("Tokens available are : %d\n",semcount);
xSemaphoreTake(CountingSem,portMAX_DELAY);
printf("Leaving Task4 , Data ACCESSED is %d\n",resourve[indx]);
printf("------------------------------------------\n");
indx++;
if(indx>2)
indx = 0;
vTaskDelay(500);
}
/* USER CODE END Task4_init */
}
```
通过xSemaphoreTake
获取令(消耗)牌继续执行任务。通过xSemaphoreGiveFromISR
生产令牌。通过uxSemaphoreGetCount查看令牌剩余个数。然后再按照优先级执行。
3.2 结果:
初始化给出三个令牌:
通过中断给出三个令牌:
3.3 不使用CMSIS API 介绍计数器信号量
注释掉#include“ cmsis_os.h”
手动包含与FreeRTOS相关的文件。
```c++
#include“ FreeRTOS.h”
#include“ task.h”
#include“ timers.h”
#include“ queue.h”
#include“ semphr.h”
#include“ event_groups.h”
#include“ stdlib.h”
#include“ string.h”
```
2.2 定义Task函数与创建信号量
定义四个不同的人物处理程序和计数信号量处理程序
```c++
//创建任务定义
TaskHandle_t Task1handler;
void Task1_Init(void * pvParameters);
TaskHandle_t Task2handler;
void Task2_Init(void * pvParameters);
TaskHandle_t Task3handler;
void Task3_Init(void * pvParameters);
TaskHandle_t Task4handler;
void Task4_Init(void * pvParameters);
//与信号量相关
SemaphoreHandle_t CountingSem;
//资源相关的
int resource[ 3 ] = { 111,222,333 };
int indx = 0 ;
// uart相关的
uint8_t rx_data = 0 ;
```
在main函数内部,首先需要创建计数信号量。首先需要创造信号量CountingSem = xSemaphoreCreateCounting(3,0)
,其中3为最大信号量,0为初始计数。如果有错误,并且无法创建信号量,它将返回NULL,否则将返回其他值。
```c++
CountingSem = xSemaphoreCreateCounting(3,0);
if (CountingSem == NULL) HAL_UART_Transmit(&huart2, (uint8_t *) "Unable to Create Semaphore\n\n", 28, 100);
else HAL_UART_Transmit(&huart2, (uint8_t *) "Counting Semaphore created successfully\n\n", 41, 1000);
```
//创建任务
xTaskCreate(Task1_Init,“Task1” ,128,NULL,3,& Task1handler);
xTaskCreate(Task2_Init,“Task2” ,128,NULL,2,& Task2handler);
xTaskCreate(Task3_Init,“Task3” ,128,NULL,1,& Task3handler);
xTaskCreate(Task4_Init,“Task4” ,128,NULL,0,& Task4handler);
vTaskStartScheduler();
```
数据接收函数:
```c++
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_UART_Receive_IT(huart, &rx_data, 1);
if (rx_data == 'r')
{
// release the semaphore here
/* The xHigherPriorityTaskWoken parameter must be initialized to pdFALSE as
it will get set to pdTRUE inside the interrupt safe API function if a
context switch is required. */
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(CountingSem, &xHigherPriorityTaskWoken); // ISR SAFE VERSION
xSemaphoreGiveFromISR(CountingSem, &xHigherPriorityTaskWoken); // ISR SAFE VERSION
xSemaphoreGiveFromISR(CountingSem, &xHigherPriorityTaskWoken); // ISR SAFE VERSION
/* Pass the xHigherPriorityTaskWoken value into portEND_SWITCHING_ISR(). If
xHigherPriorityTaskWoken was set to pdTRUE inside xSemaphoreGiveFromISR()
then calling portEND_SWITCHING_ISR() will request a context switch. If
xHigherPriorityTaskWoken is still pdFALSE then calling
portEND_SWITCHING_ISR() will have no effect */
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
}
}
其他部分一样,结果也一样。