700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > c++重定向后恢复_【STM32CubeIDE】重定向printf

c++重定向后恢复_【STM32CubeIDE】重定向printf

时间:2023-06-02 17:54:16

相关推荐

c++重定向后恢复_【STM32CubeIDE】重定向printf

文章导读

本文主要内容为在CubeIDE中(也可以说基于HAL库或CubeMX)实现printf函数重定向问题。具体包括重定向printf的重要性、原理、方法、可能遇到的问题等。

关键词:HAL库,STM32CubeIDE,重定向printf到串口,浮点数%f无法打印,可变参数宏封装printf函数,非阻塞模式下的DMA辅助串口自动发送。

为什么写此文

接触过嵌入式的小伙伴对于STM32必定不陌生,在STM32Cube生态系统推出之前,大家更多地使用MDK平台来开发STM32,当然现在也有很多人在继续使用MDK,因此很多开发中遇到的问题以及解决方法不能在其他平台完美解决。

ST极力推广STM32Cube生态系统,致力于使STM32的开发变的更简单;ST发布了HAL库和LL库并宣布停止维护STD库,可见以后ST的决心和日后的发展方向。

正文

一、重定向printf的重要性

printf大法好 ,printf函数可以输出各种格式化信息,xio习C语言的第一课就是printf("Hello Worldrn");这句话一直印象深刻啊...我们在开发STM32中也同样需要输入输出很多信息,为了方便调试,有时候printf暴力输出也不是一个很差的方法。

二、重定向printf的原理

C语言中不同库的函数可以同名,而printf函数在系统库中,C语言链接库时先链接到哪个函数就用哪个,因此我们重定向一个函数就相当于重写一遍函数的实现。而重新实现printf比较麻烦,一般不可取,所以我们找到了它的“儿子”,就是基本字符输入输出函数,如putchar,_write等,只负责字符的读写,不管其具体格式化过程。

三、重定向printf的方法

本文是基于HAL库和STM32CubeIDE来实现重定向printf的,基于STD库和MDK平台可以参考别人的文章,链接如下:

mculover666:【STM32Cube_09】重定向printf函数到串口输出的多种方法​

上述链接的文章末尾表示,重写putchar函数在GCC编译器中不可行,而是需要重写_write函数或者__io_putchar函数。如何判断自己的编译器是不是GCC,可以用下面代码做判断

#ifdef __GNUC__#endif

因此重定向printf的代码框架我们可以写成

#include <stdio.h>#ifdef __GNUC__int __io_putchar(int ch){return ch;}#elseint fputc(int ch, FILE *stream){return ch;}#endif

其实重定向printf应该被称为重定向putchar。到这就为止就可以完全走自己的路了,我们可以重定printf函数向到串口,到DMA,到任何内存空间或IO设备。

通常我们只需要重定向输出到串口,为了实现起来简单易懂,很多方法都是直接在putchar函数体中把字符发出去,代码可参考下面链接文章

ZHOU:STM32 HAL库 添加printf 串口重定向​

到此可以解决大部分人重定向printf输出到串口的方法。

下面我分享一个我遇到的问题:

在STM32CubeIDE中使用重定向的printf输出,测试%d,%x等都很正常,但唯独%f没有任何输出,也不报错或者乱码,查了很多网页,终于有人解答了这个浮点数打印问题,就一张图片我就明白了一切...真是气死了。图片我放出来了,链接也在下面。

STM32CubeIDE使用sprintf/printf发送浮点数失败 - ST MCU单片机论坛 - ST(意法半导体)MCU官方技术论坛 - 中国电子网技术论坛​

其实这才是我写这篇文章的主要原因...我在知乎上查找了很多也没找到关于这个问题的回答和文章,所以我现在解决了也顺便分享一下,这对于刚接触STM32CubeIDE的人真的一点也不知道还有这两个选项!!!

此外,我想分享一下我个人重定向printf到串口的方法,注意不是重定向printf的方法;

上述重定向printf到串口是直接把字符一个一个地发出去,这貌似在HAL库中只能使用阻塞模式发送,即使用HAL_UART_Transmit函数。我jio得此方法效率略低,因为printf函数必须等待串口一个一个地把字符发出去才能让后面地代码执行。

我想用DMA辅助串口自动发送可行?当然!但是需要初始化外设和DMA,完整代码如下

/** uart_debug.h** Created on: Mar 9, *Author: AngBluCat*/#ifndef INC_UART_DEBUG_H_#define INC_UART_DEBUG_H_#include "main.h"void UART_Debug_Printf(const char* format, ...);void UART_Debug_Remap(UART_HandleTypeDef *huart);void UART_Debug_HAL_Status(HAL_StatusTypeDef status);#endif /* INC_UART_DEBUG_H_ */

如果采用非阻塞模式发送,就要考虑用可变参数宏封装printf一下:执行格式化字符串解析后,再一同把解析后的ASCII码串 用串口发出去。

/** uart_debug.c** Created on: Mar 9, *Author: AngBluCat*/#include "uart_debug.h"/* COED BEGIN UART_DEBUG */#include "usart.h"#include "stdio.h"#include "stdarg.h"/* UART 发送缓冲 */#define UART_DEBUG_BUFF_SIZE 500static uint8_t UART_Debug_Buff[UART_DEBUG_BUFF_SIZE];static uint32_t UART_Debug_BuffIndex;/* 重定向printf */#ifdef __GNUC__#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)#else#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *stream)#endifPUTCHAR_PROTOTYPE{UART_Debug_Buff[UART_Debug_BuffIndex] = (uint8_t) ch;UART_Debug_BuffIndex ++;return ch;}/* UART句柄,用于DEBUG输出,需要初始化映射,调用UART_Debug_Remap函数 */UART_HandleTypeDef *UART_Debug_Handle = &huart1;/* 在此处修改,如果使用UART传输的其他API *///#define UART_DEBUG_TRANSMIT() HAL_UART_Transmit(UART_Debug_Handle, UART_Debug_Buff, UART_Debug_BuffIndex, 0xFFFF)#define UART_DEBUG_TRANSMIT() HAL_UART_Transmit_DMA(UART_Debug_Handle, UART_Debug_Buff, UART_Debug_BuffIndex)//#define UART_DEBUG_TRANSMIT() HAL_UART_Transmit_IT(UART_Debug_Handle, UART_Debug_Buff, UART_Debug_BuffIndex)/** @brief 串口调试输出API,用法类似printf* @param format 格式化字串* @param ... 可变参数* @retval 无*/void UART_Debug_Printf(const char *format, ...){/* 等待串口状态 */while(UART_Debug_Handle->gState != HAL_UART_STATE_READY) ;/* 初始缓存下标 */UART_Debug_BuffIndex = 0;/* 调用vprintf解析格式化字符串 */va_list args_list;va_start(args_list, format);vprintf(format, args_list);va_end(args_list);UART_DEBUG_TRANSMIT();}/** @brief 调试输出的串口映射* @param 串口句柄* @retval 无*/inline void UART_Debug_Remap(UART_HandleTypeDef *huart){UART_Debug_Handle = huart;}/** @brief 串口调试输出HAL status值* @param HAL status* @retval 无*/void UART_Debug_HAL_Status(HAL_StatusTypeDef status){switch (status) {case HAL_OK:UART_Debug_Printf("HAL_OKrn");break;case HAL_ERROR:UART_Debug_Printf("HAL_ERRORrn");break;case HAL_BUSY:UART_Debug_Printf("HAL_BUSYrn");break;case HAL_TIMEOUT:UART_Debug_Printf("HAL_TIMEOUTrn");break;default:UART_Debug_Printf("HAL_UNDEFINEDrn");}}/* COED END UART_DEBUG */

第一次写文章,如有错误欢迎大家指正...

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。