在RTOS中验证二值信号量与计数型信号量

       我们在stm32f103c8t6单片机上验证RTOS二值信号量与计数信号量,利用stm32cube进行RTOS的配置。裸机的时钟源默认是 SysTick,但是开启 FreeRTOS 后,FreeRTOS会占用 SysTick (用来生成1ms 定时,用于任务调度),所以我们开启TIM2当做裸机的时钟源,为其他总线提供另外的时钟源。

验证的功能比较简单,选择V1 版本的内核完全够用。

一、验证的思路及需要使用的函数及二值信号量的说明

1.验证思路

创建一个二值信号量,按下 KEY1 则放入信号量,按下 KEY2 获取信号量

2.需要函数

创建二值信号量 SemaphoreHandle_t xSemaphoreCreateBinary( void )

参数:无 返回值: 成功,返回对应二值信号量的句柄; 失败,返回 NULL 。

释放二值信号量 BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore )

参数: xSemaphore:要释放的信号量句柄

返回值: 成功,返回 pdPASS ; 失败,返回 errQUEUE_FULL。

获取二值信号量 BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );

参数:xSemaphore:要获取的信号量句柄

xTicksToWait:超时时间,0 表示不超时,portMAX_DELAY表示卡死等待;

返回值:成功,返回 pdPASS ;失败,返回 errQUEUE_FULL 。

3. 二值信号量的说明

二值信号量相当于一个长度为1,大小为零的队列,只有0和1两种状态,通常情况下,我们用它来进行互斥访问或任务同步。

互斥访问:我的理解是,比如说信号量是一个玩具,A拿走了,B就没得玩了;A要是把玩具放回去,B才可以拿着玩。

任务同步:我的理解,就是因为什么,所以什么。比如你去电影院,要先去买票才可以看电影。只有A的发生才可以导致B的发生。

二、二值信号stm32cube的配置

SYS

RCC

在RTOS中验证二值信号量与计数型信号量_第1张图片

GPIO

PA0对应按键1,PA1对应按键2。

RTOS RTOS版本选择

在RTOS中验证二值信号量与计数型信号量_第2张图片

在Tasks and Queues中配置我们的2个任务

其实就是相当于stm32cube帮我们调用了并封装了生成任务函数xTaskCreate(),生成队列函数xQueueCreate函数。

两个任务的名字分别是

TaskGive,TaskTake

两个任务的入口函数名字分别是

StartTaskGive, StartTaskTake;

各参数的配置相同,如下图

任务1和2除了任务名字和入口函数不同,其余的都相同

在RTOS中验证二值信号量与计数型信号量_第3张图片

在Times and Semapores里

选择Binary semaphores(二值信号),生成二值信号配置如下图所示

在RTOS中验证二值信号量与计数型信号量_第4张图片

三、二值信号代码部分

usart.c

#include "stdio.h"

int fputc(int ch, FILE *f)

{

unsigned char temp[1]={ch};

HAL_UART_Transmit(&huart1,temp,1,0xffff);

return ch;

}

同时打开“魔术棒”,勾选Use MicroLIB,点击OK。这样就可以进行串口打印了。

freertos.c

#include "FreeRTOS.h"

#include "task.h"

#include "main.h"

#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/

/* USER CODE BEGIN Includes */

# include "stdio.h"

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/

/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/

/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/

/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN Variables */

/* USER CODE END Variables */

osThreadId TaskGiveHandle;

osThreadId TaskTakeHandle;

osSemaphoreId myBinarySemHandle;

/* Private function prototypes -----------------------------------------------*/

/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartTaskGive(void const * argument);

void StartTaskTake(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */

static StaticTask_t xIdleTaskTCBBuffer;

static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )

{

  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;

  *ppxIdleTaskStackBuffer = &xIdleStack[0];

  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;

  /* place for user code */

}

/* USER CODE END GET_IDLE_TASK_MEMORY */

/**

  * @brief  FreeRTOS initialization

  * @param  None

  * @retval None

  */

void MX_FREERTOS_Init(void) {

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */

  /* add mutexes, ... */

  /* USER CODE END RTOS_MUTEX */

  /* Create the semaphores(s) */

  /* definition and creation of myBinarySem */

  osSemaphoreDef(myBinarySem);

  myBinarySemHandle = osSemaphoreCreate(osSemaphore(myBinarySem), 1);

  /* USER CODE BEGIN RTOS_SEMAPHORES */

  /* add semaphores, ... */

  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */

  /* start timers, add new ones, ... */

  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */

  /* add queues, ... */

  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */

  /* definition and creation of TaskGive */

  osThreadDef(TaskGive, StartTaskGive, osPriorityNormal, 0, 128);

  TaskGiveHandle = osThreadCreate(osThread(TaskGive), NULL);

  /* definition and creation of TaskTake */

  osThreadDef(TaskTake, StartTaskTake, osPriorityNormal, 0, 128);

  TaskTakeHandle = osThreadCreate(osThread(TaskTake), NULL);

  /* USER CODE BEGIN RTOS_THREADS */

  /* add threads, ... */

  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartTaskGive */

/**

  * @brief  Function implementing the TaskGive thread.

  * @param  argument: Not used

  * @retval None

  */

/* USER CODE END Header_StartTaskGive */

void StartTaskGive(void const * argument)

{

  /* USER CODE BEGIN StartTaskGive */

  /* Infinite loop */

  for(;;)

  {

    if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)

{

osDelay(20);

if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)

{

if (xSemaphoreGive(myBinarySemHandle) == pdTRUE)

printf("二值信号量放入成功\r\n");

else

printf("二值信号量放入失败\r\n");

}

while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);

}

osDelay(10);

  }

  /* USER CODE END StartTaskGive */

}

/* USER CODE BEGIN Header_StartTaskTake */

/**

* @brief Function implementing the TaskTake thread.

* @param argument: Not used

* @retval None

*/

/* USER CODE END Header_StartTaskTake */

void StartTaskTake(void const * argument)

{

  /* USER CODE BEGIN StartTaskTake */

  /* Infinite loop */

  for(;;)

  {

                     if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)

       {

       osDelay(20);

       if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)

       {

       if (xSemaphoreTake(myBinarySemHandle, portMAX_DELAY ) == pdTRUE)

       printf("二值信号量取出成功\r\n");

       else

       printf("二值信号量取出失败\r\n");

       }

       while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);

       }

       osDelay(10);

  }

  /* USER CODE END StartTaskTake */

}

需要说明的是

osSemaphoreDef(myBinarySem);

myBinarySemHandle = osSemaphoreCreate(osSemaphore(myBinarySem), 1);

这两个函数相当于stm32cube封装了创建二值信号量函数SemaphoreHandle_t xSemaphoreCreateBinary( void ),代表着二值信号量创建成功,同时该他在开始的时候还给二值信号量赋值唯一。

来看看以下运行的结果

开始的时候,我按下复位键,然后按key1,接着按key2

在RTOS中验证二值信号量与计数型信号量_第5张图片

可以发现,按下key1后,信号写入失败,为什么呢?

这是因为我们在前面就说过,二值信号量其实就是一个长度为1,大小为零的队列,只有0和1两种状态。而且由于myBinarySemHandle = osSemaphoreCreate(osSemaphore(myBinarySem), 1);的写入,导致在一开始的时候,二值信号量就自动被赋值为1,作为队列为1的二值信号量已经写满了,无法再写进去了。所以按下key1,写入失败,但按下key2写入成功。

那么又什么解决办法吗?当然有了

把myBinarySemHandle = osSemaphoreCreate(osSemaphore(myBinarySem), 1);这个语句换一下,换成myBinarySemHandle = xSemaphoreCreateBinary();就可以了,这样就不会自动赋值1了。

下图是替换过后按下key1和key2的图

在RTOS中验证二值信号量与计数型信号量_第6张图片

再来分析一下xSemaphoreTake(myBinarySemHandle, portMAX_DELAY )

portMAX_DELAY表示卡死等待,啥意思呢?就是说,你给我下了取出信号的命令,但是我去取的时候发现没有信号,咋办呢?我就等,等到你下一次放入信号,不用你同意,我直接拿走,等你想在再用按键取走的时候,不好意思,我已经拿走了,你没法拿了,除非你在放入信号。

先按下key2,取走信号,因为开始没有信号,所以死等。等你按下key1放入信号我就直接取走。看似你存了信号,实际已经没了,所以你还可以在存储信号。

在RTOS中验证二值信号量与计数型信号量_第7张图片

四、验证的思路及需要使用的函数及计数信号量的说明

1. 验证的思路

按下 KEY1 则放入信号量,按下 KEY2 获取信号量。

2.所需函数

创建计数信号量SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);

uxMaxCount:可以达到的最大计数值

uxInitialCount:创建信号量时分配给信号量的计数值

返回值: 成功,返回对应计数型信号量的句柄; 失败,返回 NULL 。

释放计数信号量 BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore )

参数: xSemaphore:要释放的信号量句柄

返回值: 成功,返回 pdPASS ; 失败,返回 errQUEUE_FULL。

获取计数信号量 BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );

参数:xSemaphore:要获取的信号量句柄

xTicksToWait:超时时间,0 表示不超时,portMAX_DELAY表示卡死等待;

返回值:成功,返回 pdPASS ;失败,返回 errQUEUE_FULL 。

3. 计数信号量的说明

计数信号量相当于队列长度大于1 的队列,长度具体为多少,是你自己设定的

同样得到了,他所放的信号数值也不能超过你设置的最大值。用法上和二值信号量很相似。这里我们所用的释放和获取计数信号量函数和二值信号量那里一样。

五、计数信号量stm32cube的配置

SYS

RCC

在RTOS中验证二值信号量与计数型信号量_第8张图片

GPIO

PA0对应按键1,PA1对应按键2。

RTOS

在RTOS中验证二值信号量与计数型信号量_第9张图片

在Tasks and Queues中配置我们的2个任务

其实就是相当于stm32cube帮我们调用了并封装了生成任务xTaskCreate(),生成队列xQueueCreate函数。

两个任务的名字分别是

TaskGive,TaskTake

两个任务的入口函数名字分别是

StartTaskGive, StartTaskTake;

各参数的配置相同,如下图

任务1和2除了任务名字和入口函数不同,其余的都相同

在RTOS中验证二值信号量与计数型信号量_第10张图片

在Config parameters里,将USE_COUNTING_SEMAPHORES配置为Enablde,否则无法开启计数量

在RTOS中验证二值信号量与计数型信号量_第11张图片

接着在Times and Semapores里

选择Counting semaphores(二值信号),配置计数最大值为3,如下图所示

在RTOS中验证二值信号量与计数型信号量_第12张图片

六、计数信号亮代码部分

usart.c

#include "stdio.h"

int fputc(int ch, FILE *f)

{

unsigned char temp[1]={ch};

HAL_UART_Transmit(&huart1,temp,1,0xffff);

return ch;

}

同时打开“魔术棒”,勾选Use MicroLIB,点击OK。这样就可以进行串口打印了。

freertos.c

#include "FreeRTOS.h"

#include "task.h"

#include "main.h"

#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/

/* USER CODE BEGIN Includes */

# include "stdio.h"

osThreadId TaskGiveHandle;

osThreadId TaskTakeHandle;

osSemaphoreId myCountingSemHandle;

/* Private function prototypes -----------------------------------------------*/

/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartTaskGive(void const * argument);

void StartTaskTake(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */

static StaticTask_t xIdleTaskTCBBuffer;

static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )

{

  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;

  *ppxIdleTaskStackBuffer = &xIdleStack[0];

  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;

  /* place for user code */

}

/* USER CODE END GET_IDLE_TASK_MEMORY */

/**

  * @brief  FreeRTOS initialization

  * @param  None

  * @retval None

  */

void MX_FREERTOS_Init(void) {

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */

  /* add mutexes, ... */

  /* USER CODE END RTOS_MUTEX */

  /* Create the semaphores(s) */

  /* definition and creation of myCountingSem */

  osSemaphoreDef(myCountingSem);

//  myCountingSemHandle = osSemaphoreCreate(osSemaphore(myCountingSem), 3);

 myCountingSemHandle = xSemaphoreCreateCounting(3, 0);

  /* USER CODE BEGIN RTOS_SEMAPHORES */

  /* add semaphores, ... */

  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */

  /* start timers, add new ones, ... */

  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */

  /* add queues, ... */

  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */

  /* definition and creation of TaskGive */

  osThreadDef(TaskGive, StartTaskGive, osPriorityNormal, 0, 128);

  TaskGiveHandle = osThreadCreate(osThread(TaskGive), NULL);

  /* definition and creation of TaskTake */

  osThreadDef(TaskTake, StartTaskTake, osPriorityNormal, 0, 128);

  TaskTakeHandle = osThreadCreate(osThread(TaskTake), NULL);

  /* USER CODE BEGIN RTOS_THREADS */

  /* add threads, ... */

  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartTaskGive */

/**

  * @brief  Function implementing the TaskGive thread.

  * @param  argument: Not used

  * @retval None

  */

/* USER CODE END Header_StartTaskGive */

void StartTaskGive(void const * argument)

{

  /* USER CODE BEGIN StartTaskGive */

  /* Infinite loop */

  for(;;)

  {

    if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)

{

osDelay(20);

if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)

{

if (xSemaphoreGive(myCountingSemHandle) == pdTRUE)

printf("计数信号量放入成功\r\n");

else

printf("计数信号量放入失败\r\n");

}

while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);

}

osDelay(10);

  }

  /* USER CODE END StartTaskGive */

}

/* USER CODE BEGIN Header_StartTaskTake */

/**

* @brief Function implementing the TaskTake thread.

* @param argument: Not used

* @retval None

*/

/* USER CODE END Header_StartTaskTake */

void StartTaskTake(void const * argument)

{

  /* USER CODE BEGIN StartTaskTake */

  /* Infinite loop */

  for(;;)

  {

                     if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)

       {

       osDelay(20);

       if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)

       {

       if (xSemaphoreTake(myCountingSemHandle, 0 ) == pdTRUE)

       printf("计数信号量取出成功\r\n");

       else

       printf("计数信号量取出失败\r\n");

       }

       while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);

       }

       osDelay(10);

  }

  /* USER CODE END StartTaskTake */

}

这里要说一下,如果是我们在生成二值计数量的时候,完全依赖stm32cube,那么生成的函数就有这么一行代码

myCountingSemHandle = osSemaphoreCreate(osSemaphore(myCountingSem), 3);

意思就是,这个计数量函数我生成,返回值是句柄,同时我把信号量给你填满了(因为我们配置的时候最大计数值就是3,因此这里填满了就是3个),所以你这个时刻就不能再进入信号量了,你只能取出,等有空位了,你再进。

所以我们进行了改写,把这这局程序替换了一下,让他从0开始,一开始就不给他信号量

myCountingSemHandle = xSemaphoreCreateCounting(3, 0);

下图是我用替换后的代码,并连续按了四下key1和四下key2的结果。从图中我们可以看出来,因为最多只能输入3个信号量,所以当我按下连续按四下key1时,第四次是没法放入信号的。同理,因为最多只能储存3个信号量,所以当我按下连续按四下key2时,第四次是没法取出信号的。

在RTOS中验证二值信号量与计数型信号量_第13张图片

你可能感兴趣的:(RTOS,stm32cube,二值信号量与计数型信号量)