我们假定现在创建了3个工作线程,这3个线程都需要通过串口输出日志或内容。很简单的认为,我们可以做到各个任务连续输出自己线程的内容,实际上不是的,这涉及到了多线程原理,有时间跟大家说说,或者大家可以去了解了解。
在这篇文章只涉及题目提出的问题解决方法。
请自行搭建最基础的开发环境,能做到如下几点就可以了:
我们先复现一下 标题 中描述的问题:
配置USART1作为我们接下来例子中的使用串口
// printf 重定向代码 STM32H743
int __io_putchar(int ch){
while(! (USART1->ISR & ((0x1UL << (6U)))));
USART1->TDR = ch;
return ch;
}
配置FreeRTOS,我们先添加3个线程,cubemx的图我就不放了(如果有需要,请在评论栏留言,我会找时间放图的),这里只放生成的代码。
/* 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,
};
/* 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);
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);
}
}
Normal01 Task Start ...
Normal02 Task Start ...
Normal03 Task Start ...
实际上却是上面图片的结果,造成这样的原因就是多线程同时访问一个共享资源而造成资源的抢夺现象。
新增一个串口守护线程,人为禁止其他线程访问串口资源,其他线程需要串口发送的内容则通过消息队列传递到串口守护线程统一进行输出。而消息队列先进先出的特性保证了我们串口输出不会乱序。
/* 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];
PrintTaskHandle = osThreadNew(StartPrintTask, NULL, &PrintTask_attributes);
PrintQueueHandle = osMessageQueueNew (16, sizeof(getPrintBufFromQueue), &PrintQueue_attributes);
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 */
}
#define fun_print(format, ...){ \
char tempBuffer[32]; \
memset(tempBuffer, 0, 32); \
sprintf(tempBuffer, format, ##__VA_ARGS__); \
osMessageQueuePut(PrintQueueHandle, tempBuffer, 0, 0); \
}
#define mt_log_mutex myMutex01Handle
#define mt_log(format, ...) \
osMutexAcquire(mt_log_mutex, osWaitForever); \
printf(format, ##__VA_ARGS__); \
osMutexRelease(mt_log_mutex);
(上述代码仅供交流使用,请勿直接用在生产环境,如有错误,请在评论栏提出讨论)