FreeRTOS学习 - 互斥量学习记录
Posted 晨少的博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FreeRTOS学习 - 互斥量学习记录相关的知识,希望对你有一定的参考价值。
了解互斥量
例如多个任务对同一个数组或者变量进行操作时,往往会发生冲突,可能任务A访问arr数组的过程中被任务B打断,任务B恰好也对arr数组进行了操作,当任务A继续执行时,此时的arr数组已经不是当时的arr数组了,这就造成了访问冲突。
即非原子化访问,也就是操作的过程可能被打断
为了避免此问题,可以在访问变量前,上锁,访问之后,开锁
- 即A使用变量前上个锁
- 使用完A再开锁
- 再此期间B必须等A开锁后才能使用该变量,这样就不会产生访问冲突了
- 但实际上FreeRTOS的代码并没有实现谁上的锁只能由谁来开
- 所以A上锁后,B实际上也是可以已开锁的
- 所以谁上锁,谁才能开锁需要由程序员自己约定了
- 这个锁就是我们说的互斥量,互斥量是一种特殊的二进制信号量。
使用互斥量
要想使用互斥量,需要在配置文件FreeRTOSConfig.h中定义:
#define configUSE_MUTEXES 1
一、创建互斥量
QueueHandle_t xSemaphoreMutex; //互斥量句柄
/* 创建互斥量,返回它的句柄 */
xSemaphoreMutex = xSemaphoreCreateMutex();
二、互斥量的操作函数(获取、释放、删除)
/*
* xSemaphore: 信号量句柄,你要删除哪个信号量, 互斥量也是一种信号量
*/
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
/* 释放 */
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
/* 释放(ISR版本) */
BaseType_t xSemaphoreGiveFromISR(
SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken
);
/* 获取 */
BaseType_t xSemaphoreTake(
SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait
);
/* 获取 (ISR版本) */
xSemaphoreGiveFromISR(
SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken
);
三、互斥量使用实例
创建两个任务
- 任务1一直发送0~99数字
- 任务2一直发送100~199数字
1. 不使用互斥量的情况(USE_Mutex_Flag 置0)
实验结果,发送数据混乱在一起:
2. 使用互斥量的情况(USE_Mutex_Flag 置1)
实验结果,发送数据不产生错乱了:
代码如下:
void systemInit(void); //各模块初始化
QueueHandle_t xSemaphoreMutex; //互斥量句柄
void systemInit(void); //各模块初始化
QueueHandle_t xSemaphoreMutex; //互斥量句柄
/* 使用互斥量标志 */
#define USE_Mutex_Flag 0
/*
* 任务1一直发送0~99数字
*/
void vSemphrMutex_Task1(void *pvParameters)
const TickType_t xDelay = 50 / portTICK_PERIOD_MS;
BaseType_t result;
for(;;)
#if (USE_Mutex_Flag == 1)
/* 上锁 */
xSemaphoreTake(xSemaphoreMutex, portMAX_DELAY);
#endif
/* 打印 0 ~ 99 */
for(int i=0; i < 100 ; i++)
printf("%d ", i);
printf("\\r\\n");
#if (USE_Mutex_Flag == 1)
/* 开锁 */
xSemaphoreGive(xSemaphoreMutex);
#endif
vTaskDelay(xDelay);
/*
* 任务2一直发送100~199数字
*/
void vSemphrMutex_Task2(void *pvParameters)
const TickType_t xDelay = 50 / portTICK_PERIOD_MS;
BaseType_t result; //获取信号量结果变量
for(;;)
#if (USE_Mutex_Flag == 1)
/* 上锁 */
xSemaphoreTake(xSemaphoreMutex, portMAX_DELAY);
#endif
/* 打印 100 ~ 199 */
for(int i=100; i < 200 ; i++)
printf("%d ", i);
printf("\\r\\n");
#if (USE_Mutex_Flag == 1)
/* 开锁 */
xSemaphoreGive(xSemaphoreMutex);
#endif
vTaskDelay(xDelay);
int main()
systemInit(); //初始化各个模块
// 互斥量任务一
xTaskCreate(vSemphrMutex_Task1, "vSemphrMutex_Task1", 200, NULL, 2, NULL);
// 互斥量任务二
xTaskCreate(vSemphrMutex_Task2, "vSemphrMutex_Task2", 200, NULL, 2, NULL);
vTaskStartScheduler(); //开启任务调度
/*
* 初始化各个模块
*/
void systemInit(void)
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
USART1_Init(115200);
printf("串口初始化成功!\\r\\n");
/* 创建互斥量 */
xSemaphoreMutex = xSemaphoreCreateMutex();
FreeRTOS学习笔记 ——二值信号量
前言
上一个笔记学习完了FreeRTOS的消息队列,以及如何创建与使用,在本篇文章中,继续基于上一个工程来学习FreeRTOS的信号量,其中二值信号量是尤其重要的一点,比较常用,所以本篇会着重介绍二值信号量作用与使用。
FreeRTOS信号量简介
- 二值信号量通常用于互斥访问或同步,二值信号量和互斥信号量非常类似,但是还是有一 些细微的差别,互斥信号量拥有优先级继承机制,二值信号量没有优先级继承。因此二值信号 另更适合用于同步(任务与任务或任务与中断的同步),而互斥信号量适合用于简单的互斥访问。
- 二值信号量其实就是一个只有一个队列项的队列,这个特殊的队列要么是满的,要么是空 的,这不正好就是二值的吗? 任务和中断使用这个特殊队列不用在乎队列中存的是什么消息,只需要知道这个队列是满的还是空的。可以利用这个机制来完成任务与中断之间的同步或是任务与任务之间的同步。
下面进入工程创建。
CubeMx配置
MCU: STM32F103C8T6
CubeMX: STM32CubeMX 5.3.0
在上篇文章的CubeMx配置基础上,增加内容,如果有基本工程不会创建的小伙伴可以点击传送门进行学习。
在Timers and Semaphores栏里在Binary Semaphores点击add,新增一个二值信号量。
接下来就像往常那样创建并打开工程就可以了
填写项目名
配置项目生成单独的.c/.h文件
接着生成代码就ok了~ (点击后会出现警告,直接点yes即可)
打开工程,freertos.c中会发现多出这段代码
osSemaphoreDef(myBinarySem01);
myBinarySem01Handle = osSemaphoreCreate(osSemaphore(myBinarySem01), 1);
也就是创建二值信号量的代码。
下面的测试demo与上篇文章差不多,在原有基础上,增加二值信号量,使得两个任务可以同步起来,第一个任务把信息放入队列里后并产生一个随机的延时数(用于测试),然后将二值信号量释放出去;任务二无限时长等待信号量,如果获取信号量成功,则将队列里的信息出队,并打印出来。
下面为演示代码。
=加入头文件以及变量=
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <stdlib.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef struct
uint8_t mid;
char mDate[20];
MSG;
MSG myDate;
uint16_t cnt=1;
uint16_t d_time=0;
/* USER CODE END PTD */
再编写task里的代码
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void const * argument)
/* USER CODE BEGIN StartDefaultTask */
MSG *TXMSG;
uint8_t i;
TXMSG = &myDate;
/* Infinite loop */
for(;;)
TXMSG->mid = cnt++;
for(i=0; i<20; i++)
TXMSG->mDate[i] = rand()%255;
if(xQueueSend(myQueue02Handle,&TXMSG,10) ==errQUEUE_FULL)
printf("myQueue01Handle errQUEUE_FULL\\r\\n");
d_time = rand()%500;
osDelay(d_time);
xSemaphoreGive(myBinarySem01Handle);//释放信号量
printf("\\r\\nTX-Task1 Done!\\r\\n");
/* USER CODE END StartDefaultTask */
/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void const * argument)
/* USER CODE BEGIN StartTask02 */
MSG *RXMSG;
uint8_t i;
/* Infinite loop */
for(;;)
xSemaphoreTake(myBinarySem01Handle,osWaitForever);//获取信号量
if(xQueueReceive( myQueue02Handle,&RXMSG, 10) == pdPASS)
printf("\\r\\nRXMSG->ucMessageID = %d \\r",RXMSG->mid);
printf("RXMSG->ucData[0] = ");
for(i=0;i<20;i++)
printf(" %03d",RXMSG->mDate[i]);
printf("\\r\\n");
printf("delay time: %d RX-Task2 Done!\\r\\n",d_time);
/* USER CODE END StartTask02 */
打开串口工具进行测试
可以发现首先Task1先进行信息推进队列里,并随机产生几百毫秒的延时,随后Task2才开始工作,说明二值信号量使得它们同步了。
关于信号量的内容还有计数信号量、互斥信号量和递归互斥信号量,这里就不做解释与实验了。
以上内容记录下来,希望能帮助到有需要的伙伴。
该文章如有不对地地方,欢迎指出。
以上是关于FreeRTOS学习 - 互斥量学习记录的主要内容,如果未能解决你的问题,请参考以下文章
STM32CubeMX学习笔记(31)——FreeRTOS实时操作系统使用(互斥量)
FreeRTOSFreeRTOS学习笔记(13)— FreeRTOS创建任务和任务管理(原生API)