实现printf重定向的方式有两种,第一种是使用宏定义,第二种是重定向fputc
STM32F103C8T6,STM32CubeMX
1、HAL库,宏定义实现
直接上代码,我直接在HAL生成的main.c里面添加如下代码
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "stdint.h"
/* USER CODE END Includes */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define printf1(...) HAL_UART_Transmit( &huart1,\
(uint8_t *)u_buf,\
sprintf((char *)u_buf,__VA_ARGS__),\
0xffff)
/* USER CODE END PD */
/* USER CODE BEGIN PV */
uint8_t u_buf[256];
/* USER CODE END PV */
解释:
__VA_ARGS__
是一个可变参数的宏,很少人知道这个宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持)。实现思想就是宏定义中参数列表的最后一个参数为省略号(也就是三个点)。参考:#、##、__VA_ARGS__和##__VA_ARGS__的作用。
传入函数会先执行,所以sprintf会先被执行,将(...)
中的参数值写入u_buf中,该函数的返回值为写入字符串的长度,刚刚好填充了HAL_UART_Transmit
函数的两个参数。
注意:是printf1而不是printf,如需使用printf请看2
2、重定向fputc
在usart.c中添加代码,在Options for Target, Target, Code Generation, Use MicroLIB的Check Box要勾选上(一般默认是勾选的)
上代码(其中的注释是参考了另一篇文章的,等找到了再补上):
/* USER CODE BEGIN 1 */
#if 1
/* 冲定义fputc需要包含头文件stdio.h */
#include <stdio.h>
/* 告知连接器不从C库链接使用半主机的函数 */
#pragma import(__use_no_semihosting)
/* 定义 _sys_exit() 以避免使用半主机模式 */
void _sys_exit(int x)
{
x = x;
}
/* 标准库需要的支持类型 */
struct __FILE
{
int handle;
};
FILE __stdout;
int fputc(int ch, FILE *stream)
{
/* 堵塞判断串口是否发送完成 */
while((USART1->SR & 0X40) == 0);
/* 串口发送完成,将该字符发送 */
USART1->DR = (uint8_t) ch;
return ch;
}
#endif
/* USER CODE END 1 */
在main函数中就可以直接使用printf("内容")
来发送内容到串口了。