IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 使用消息队列解决RTOS多线程同时打印串口乱序问题 -> 正文阅读

[嵌入式]使用消息队列解决RTOS多线程同时打印串口乱序问题

背景

我们假定现在创建了3个工作线程,这3个线程都需要通过串口输出日志或内容。很简单的认为,我们可以做到各个任务连续输出自己线程的内容,实际上不是的,这涉及到了多线程原理,有时间跟大家说说,或者大家可以去了解了解。
在这篇文章只涉及题目提出的问题解决方法。

准备工作

  1. STM32 Cube系列软件开发工具
  2. 一块可以使用的STM32单片机
  3. 一个ST LINK 下载器 和 一个 USB 转 TTL
  4. 一个可以正常工作开发程序的电脑

开发环境

请自行搭建最基础的开发环境,能做到如下几点就可以了:

  1. 可以编写STM32程序并下载到单片机
  2. 重定向 printf 到串口
  3. 可以在串口看到正确的 你想要的代码输出结果
  4. FreeRTOS 使用 CMSIS V2 接口

问题复现

我们先复现一下 标题 中描述的问题:

使用cubemx配置我们初始化代码

配置USART1作为我们接下来例子中的使用串口

// printf 重定向代码 STM32H743
int __io_putchar(int ch){
	while(! (USART1->ISR & ((0x1UL << (6U)))));
	USART1->TDR = ch;
	return ch;
}

配置FreeRTOS,我们先添加3个线程,cubemx的图我就不放了(如果有需要,请在评论栏留言,我会找时间放图的),这里只放生成的代码。

  1. 线程ID 和 参数定义
/* Definitions for NormalTask01 */
osThreadId_t NormalTask01Handle;
const osThreadAttr_t NormalTask01_attributes = {
  .name = "NormalTask01",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for NormalTask03 */
osThreadId_t NormalTask03Handle;
const osThreadAttr_t NormalTask03_attributes = {
  .name = "NormalTask03",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for NormalTask02 */
osThreadId_t NormalTask02Handle;
const osThreadAttr_t NormalTask02_attributes = {
  .name = "NormalTask02",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};
  1. 在main中激活线程
/* creation of NormalTask01 */
NormalTask01Handle = osThreadNew(StartNormalTask01, NULL, &NormalTask01_attributes);
/* creation of NormalTask03 */
NormalTask03Handle = osThreadNew(StartNormalTask03, NULL, &NormalTask03_attributes);
/* creation of NormalTask02 */
NormalTask02Handle = osThreadNew(StartNormalTask02, NULL, &NormalTask02_attributes);
  1. 线程方法实现
void StartNormalTask01(void *argument)
{
  printf("Normal01 Task Start ... \r\n");
  for(;;)
  {
    osDelay(100)}
}
void StartNormalTask02(void *argument)
{
  printf("Normal02 Task Start ... \r\n");
  for(;;)
  {
    osDelay(1);
  }
}
void StartNormalTask03(void *argument)
{
  printf("Normal03 Task Start ... \r\n");
  for(;;)
  {
    osDelay(1);
  }
}
  1. 查看串口输出结果
    串口输出结果是不是跟我们想象中的结果不一样,我们想的是它会按照顺序输出
Normal01 Task Start ... 
Normal02 Task Start ... 
Normal03 Task Start ... 

实际上却是上面图片的结果,造成这样的原因就是多线程同时访问一个共享资源而造成资源的抢夺现象。

解决方法

新增一个串口守护线程,人为禁止其他线程访问串口资源,其他线程需要串口发送的内容则通过消息队列传递到串口守护线程统一进行输出。而消息队列先进先出的特性保证了我们串口输出不会乱序。

代码实现

  1. 新增一个串口输出线程一个消息队列,消息队列作为其他线程传输内容到串口打印线程的媒介
/* Definitions for PrintTask */
osThreadId_t PrintTaskHandle;
const osThreadAttr_t PrintTask_attributes = {
  .name = "PrintTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for PrintQueue */
osMessageQueueId_t PrintQueueHandle;
const osMessageQueueAttr_t PrintQueue_attributes = {
  .name = "PrintQueue"
};
static char getPrintBufFromQueue[32];
  1. 在main中激活线程 和 初始化消息队列
PrintTaskHandle = osThreadNew(StartPrintTask, NULL, &PrintTask_attributes);
PrintQueueHandle = osMessageQueueNew (16, sizeof(getPrintBufFromQueue), &PrintQueue_attributes);
  1. 打印线程方法实现
void StartPrintTask(void *argument)
{
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
  osStatus_t queueReturn = osOK;
  for(;;)
  {
	// 等待消息队列中的消息,timeout指定为osWaitForever
	queueReturn = osMessageQueueGet(PrintQueueHandle, getPrintBufFromQueue, NULL, osWaitForever);
	while(queueReturn == osOK){
		// 打印从消息队列中获取的字符串缓冲
		printf("%s", getPrintBufFromQueue);
		// 这里timeout指定为0,因为程序不需要阻塞在这里
		queueReturn = osMessageQueueGet(PrintQueueHandle, getPrintBufFromQueue, NULL, 0);
	}
  }
  /* USER CODE END 5 */
}
  1. 打印方法实现,注: 此处tempBuffer大小和消息队列的Item大小一致
#define fun_print(format, ...){ \
		char tempBuffer[32]; \
		memset(tempBuffer, 0, 32); \
		sprintf(tempBuffer, format, ##__VA_ARGS__); \
		osMessageQueuePut(PrintQueueHandle, tempBuffer, 0, 0); \
	}
  1. 此时在查看我们的串口输出结果,我们可以看到不会出现第一张图中的乱序
    串口输出结果

使用互斥量解决

#define mt_log_mutex myMutex01Handle
#define mt_log(format, ...) \
  osMutexAcquire(mt_log_mutex, osWaitForever); \
  printf(format, ##__VA_ARGS__); \
  osMutexRelease(mt_log_mutex);

(上述代码仅供交流使用,请勿直接用在生产环境,如有错误,请在评论栏提出讨论)

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-09-04 01:27:45  更:2022-09-04 01:28:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 12:56:07-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码