STM32 CubeMX
STM32 CubeMX ____Freertos任务通信:队列、信号量、互斥量,事件组,任务通知
- STM32 CubeMX
- 一、STM32 CubeMX设置
- 时钟配置
- HAL时基选择TIM1(不要选择滴答定时器;滴答定时器留给OS系统做时基)
- 使用STM32 CubeMX 库,配置Freertos
- 二、实验一:消息队列
- 消息队列的作用:
- FreeRTOS 消息队列和数组 的几个区别:
- 创建消息队列
- 创建任务
- 代码部分
- 实验现象
- 三,实验二:信号量
- 信号量概念
- 二值信号量
- 计数信号量
- 四,实验三:互斥量
- 五,实验四:事件组
- 六,实验五:任务通知
学习使用Freertos第二步
在 FreeRTOS 中,任务通信可以通过以下函数来实现:
xQueueCreate()
:用于创建一个消息队列。可以设置队列长度和每个消息的大小。
xQueueSend()
:将一条消息发送到队列中。可以选择阻塞或非阻塞发送。
xQueueReceive()
:从队列中接收一条消息。可以选择阻塞或非阻塞接收。
xQueuePeek():
查看队列中的下一条消息,但不将其移除。
xQueueReset()
:清空队列中的所有消息。
2. xQueueSemaphoreTake()
和xQueueSemaphoreGive()
:用于实现二值信号量,控制任务之间的互斥访问。
3. xSemaphoreCreateMutex()
:创建一个互斥信号量,用于实现任务之间的互斥访问。
4. xTaskNotify()
和ulTaskNotifyTake()
:用于任务间的通知机制,一个任务可以通知另一个任务进行某种操作。
5. xEventGroupCreate()、xEventGroupSetBits()和xEventGroupWaitBits()
:用于创建、设置和等待事件标志组。
一、STM32 CubeMX设置
时钟配置
HAL时基选择TIM1(不要选择滴答定时器;滴答定时器留给OS系统做时基)
使用STM32 CubeMX 库,配置Freertos
选择CMISS_V1接口就可以满足Freertos接口;且代码量比CMISS_V2小(CMISS_V2支持更多的RTOS接口,所以代码量比CMISS_V1多)
二、实验一:消息队列
消息队列的作用:
- 数据传递:消息队列允许任务之间传递数据,一个任务可以将数据打包成消息发送到队列,另一个任务则可以从队列中接收该消息并处理其中的数据。这使得任务之间可以方便地进行数据交换和共享。
- 任务解耦:通过使用消息队列,任务之间的耦合度可以降低。一个任务只需要关注发送和接收消息,而不需要知道消息的具体处理细节和目标任务的实现。这样,当需要更改或替换某个任务时,只需要保证消息的格式和接口不变即可,不会对其他任务产生影响。
- 同步与协作:消息队列可以用于实现任务之间的同步和协作。例如,一个任务可以等待某个特定的消息到达队列后才继续执行,从而实现任务间的同步。另外,多个任务可以通过发送和接收消息来协调彼此的执行顺序和操作。
- 缓冲和调节:消息队列可以充当缓冲区,用于存储一定数量的消息。当发送方发送消息速度较快,而接收方处理速度较慢时,消息队列可以暂时存储未处理的消息,避免数据丢失。同时,消息队列还可以调节发送和接收任务之间的速度差异,以平衡任务负载。
FreeRTOS 消息队列和数组 的几个区别:
- 数据组织方式:消息队列是一种先进先出(FIFO)的数据结构,用于存储和传递消息。每个消息都可以包含不同类型和长度的数据。而数组是一种线性的、连续的数据结构,用于存储相同类型和长度的元素。
- 功能和用途:消息队列主要用于任务间通信,允许任务之间传递数据和进行同步。它提供了数据传递、解耦、同步和协作等功能。而数组通常用于存储一组具有相同类型的元素,可以进行遍历、访问和修改等操作。
- 大小和容量:消息队列的大小是可以动态调整的,可以根据需要增加或减少队列的容量。而数组的大小在创建时就确定,并且通常是固定的。
- 可用性和效率:消息队列可以实现多个任务之间的并发通信,提供了较高的可用性和灵活性。而数组通常在单个任务内进行操作,具有较高的效率和直接性
- 总的来说,消息队列和数组是两种不同的数据结构,适用于不同的场景和需求。消息队列主要用于任务间通信和数据传递,具有灵活性和可调节性;而数组主要用于存储相同类型的元素,并进行遍历和访问操作。选择使用哪种数据结构取决于具体的应用需求和功能要求。
创建消息队列
- Queue Name: 队列名称
- Queue Size: 队列能够存储的最大单元数目,即队列深度
- Queue Size: 队列中数据单元的长度,以字节为单位
- Allocation: 分配方式:Dynamic 动态内存创建
- Buffer Name: 缓冲区名称
- Buffer Size: 缓冲区大小
- Conrol Block Name: 控制块名称
创建任务
- Task Name: 任务名称
- Priority: 优先级,在 FreeRTOS 中,数值越大优先级越高,0 代表最低优先级
- Stack Size (Words): 堆栈大小,单位为字,在32位处理器(STM32),一个字等于4字节,如果传入128那么任务大小为128*4字节
- Entry Function: 入口函数
- Code Generation Option: 代码生成选项
- Parameter: 任务入口函数形参,不用的时候配置为0或NULL即可
- Allocation: 分配方式:Dynamic 动态内存创建
- Buffer Name: 缓冲区名称
- Conrol Block Name: 控制块名称
代码部分
void sendTask1(void const * argument)
{
/* USER CODE BEGIN sendTask1 */
BaseType_t xsatus;
uint32_t buff=9600;
/* Infinite loop */
for(;;)
{
xsatus=xQueueSendToBack(myQueue01Handle,&buff,0);
if( xsatus!=pdPASS)
{
printf("输入失败\r\n"); // printf输出字符串
}
else
{
printf("成功写入%d\r\n", buff); // printf输出字符串
}
osDelay(1000);
}
/* USER CODE END sendTask1 */
}
void readTask3(void const * argument)
{
/* USER CODE BEGIN readTask3 */
BaseType_t xsatus;
uint32_t buff=115200;
/* Infinite loop */
for(;;)
{
xsatus=xQueueReceive(myQueue01Handle,&buff,0);
if( xsatus!=pdPASS)
{
printf("读取失败\r\n"); // printf输出字符串
}
else
{
printf("成功读取%d\r\n", buff); // printf输出字符串
}
osDelay(3000);
}
/* USER CODE END readTask3 */
}
实验现象
xsatus=xQueueSendToBack(myQueue01Handle,&buff,portMAX_DELAY);//一直等待
if( xsatus!=pdPASS)
{
printf("输入失败\r\n"); // printf输出字符串
}
else
{
printf("成功写入%d\r\n", buff); // printf输出字符串
}
还一中读取方式不会删除信息
三,实验二:信号量
信号量概念
FreeRTOS中的信号量是一种用于任务间同步和资源共享的机制。它可以用来实现任务之间的互斥访问共享资源,或者在某个任务等待某个事件发生时进行阻塞。
FreeRTOS提供了两种类型的信号量:二进制信号量(Binary Semaphore)和计数信号量(Counting Semaphore)。
二进制信号量是一种简单的信号量,只有两种状态:空闲和占用。当一个任务获取到二进制信号量时,它就可以继续执行,而其他任务则会被阻塞。当任务释放二进制信号量时,其他任务可以获取到它并继续执行。
计数信号量是一种更复杂的信号量,它可以有多个资源可供获取。计数信号量可以用来实现资源池的管理,例如限制同时访问某个资源的任务数量。
在FreeRTOS中,可以使用以下函数来创建和操作信号量:
- xSemaphoreCreateBinary(): 创建二进制信号量。
- xSemaphoreCreateCounting(): 创建计数信号量。
- xSemaphoreTake(): 获取一个信号量。
- xSemaphoreGive(): 释放一个信号量。
需要注意的是,使用信号量时要确保正确的获取和释放顺序,以避免出现死锁或资源竞争的问题。
二值信号量
创建信号量
void sendTask1(void const * argument)
{
/* USER CODE BEGIN sendTask1 */
BaseType_t xsatus;
uint32_t buff=9600;
/* Infinite loop */
for(;;)
{
if( xSemaphoreTake(myBinarySem01Handle,portMAX_DELAY)!=pdPASS)
{
printf("刷新失败#信号量获取失败\r\n"); // printf输出字符串
}
else
{
printf("成功刷新#信号量获取成功\r\n"); // printf输出字符串
}
osDelay(2000);
}
/* USER CODE END sendTask1 */
}
void readTask3(void const * argument)
{
/* USER CODE BEGIN readTask3 */
BaseType_t xsatus;
uint32_t buff=115200;
/* Infinite loop */
for(;;)
{
if( xSemaphoreGive(myBinarySem01Handle)!=pdPASS)
{
printf("读取失败#信号量不能释放\r\n"); // printf输出字符串
}
else
{
printf("成功读取#信号量已经释放\r\n"); // printf输出字符串
}
osDelay(1000);
}
/* USER CODE END readTask3 */
}
计数信号量
创建计数信号量
void sendTask1(void const * argument)
{
/* USER CODE BEGIN sendTask1 */
BaseType_t xsatus;
uint32_t buff=9600;
/* Infinite loop */
for(;;)
{
if( xSemaphoreGive(myCountingSem01Handle)!=pdTRUE)
{
printf("停车已满\r\n"); // printf输出字符串
}
else
{
printf("停车成功\r\n"); // printf输出字符串
}
osDelay(2000);
}
/* USER CODE END sendTask1 */
}
void readTask3(void const * argument)
{
/* USER CODE BEGIN readTask3 */
BaseType_t xsatus;
uint32_t buff=115200;
/* Infinite loop */
for(;;)
{
if( xSemaphoreTake(myCountingSem01Handle,0)!=pdTRUE)
{
printf("无车\r\n"); // printf输出字符串
}
else
{
printf("取走车\r\n"); // printf输出字符串
}
osDelay(1000);
}
/* USER CODE END readTask3 */
}
实验现象