综合多篇文章对实现printf进行总结,本篇博客包含内容如下:
- 使用MicroLIB实现printf(Windows环境下和Linux环境下)
- 使用C标准库实现printf(也就是不需要配置工程打开MicroLIB)
- 结合DMA实现printf(针对大量数据、OS任务间使用printf冲突)
- 多串口通信(当有多个串口通信设备时)
- printf使用常见问题
如果本篇博客对你有帮助的话,记得点个赞哦!谢谢大家!
如果不打开MicroLIB,可能会在stm32进行调试时,进入 LDR R0, =SystemInit卡死。参考文章
在usart.c头文件定义段加入stdio.h
#include
这步不要也行,我把它注释掉后,照样能够正常输出汉字、整数、浮点数、字符、字符串。
现在自己知识不够,解释不了这个结构体的意义,后面知道了回来把这个坑补上。
struct FILE {
int handle;
};
int fputc(int ch, FILE * f){
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);///<普通串口发送数据
while(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TC) == RESET){}///<等待发送完成
return ch;
}
int fgetc(FILE * F) {
uint8_t ch = 0;
HAL_UART_Receive(&huart1,&ch, 1, 0xffff);///<普通串口接收数据
while(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TC) == RESET){}///<等待发送完成
return ch;
}
切记将这些内容写在沙盒段(USER BEGIN 和 USER END)之间
在需要printf的c文件#include
,然后就可以使用printf了。
#include /* 包含头文件!!!!! */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xFFFF);//阻塞方式打印
return ch;
}
有的同志可能发现了方案二其实和方案一一样,确实,如果你在Windows上使用keil5方案一和方案二没什么区别,因为keil5默认使用ARM Compiler 5编译器,最终都是重定向了fputs函数。但如果你在Linux上使用GNUC编译器进行编译,那么这两种方案就有区别了。
经过验证,可用
#include
/* 告知连接器不从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;
}
与前面使用方法相同,不再赘述。
详情链接
添加以下代码
#include
void printf_DMA(UART_HandleTypeDef *husart, char *fmt,...)
{
uint8_t n=0;
for(int i=0;i<sizeof(Sendbuf);i++) //清空发送缓存
{
Sendbuf[i]=0;
}
va_list arg;
va_start(arg,fmt);//将...中的输入与fmt初始化到arg
vsprintf((char*)Sendbuf,fmt,arg);//将输出存到发送缓存中
va_end(arg);
for(int i=0;i<sizeof(Sendbuf);i++) //计算发送缓存中的非零字符数
{
if(Sendbuf[i]!=0) n++;
}
HAL_UART_Transmit_DMA(husart,(uint8_t*)&Sendbuf,n); //通过DMA发送字符串
}
void printf_DMA(UART_HandleTypeDef *husart, char *fmt,...);
添加以下代码到usart.c中,注意将代码放到
USER段
#include
#include
#include
void usartPrintf(UART_HandleTypeDef USARTx, char *fmt,...)
{
unsigned char UsartPrintfBuf[296];
va_list ap;
unsigned char *pStr = UsartPrintfBuf;
va_start(ap, fmt);
vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap); //格式化
va_end(ap);
while(*pStr != NULL)
{
HAL_UART_Transmit (&USARTx ,(uint8_t *)pStr++,1,HAL_MAX_DELAY );
}
}
添加以下代码到usart.h中
void usartPrintf(UART_HandleTypeDef USARTx, char *fmt,...);
与前面不同的是,如果这里要使用usartPrintf()包含的头文件是usart.h,不是stdio.h,usartPrintf的用法和printf一样。
地址:
#include
uint8_t XL_Printf(UART_HandleTypeDef *huart,const char *format, ...)
{
char buf[512]; //定义临时数组,根据实际发送大小微调
va_list args;
va_start(args, format);
uint16_t len = vsnprintf((char *)buf, sizeof(buf), (char *)format, args);
va_end(args);
return HAL_UART_Transmit(huart,buf,len,1000); //串口打印函数,可以更换为中断发送或者DMA发送
}
在usart.h中添加以下代码
uint8_t XL_Printf(UART_HandleTypeDef *huart,const char *format, ...);
包含头文件#include
这个我没遇到过,是在浏览相关文章的时候看到的,有点没看明白,他说的解决方法我没看到,感觉没啥变化。
文章地址
有的串口助手在接收字符串时必须以\r\n结尾才会显示,否者不会在屏幕上显示。例如我的就是在vofa+上无法显示,在串口调试助手上就能正常显示。
这个是因为没有打开微库MicroLIB的原因造成的。
这个是我在使用FreeRTOS时遇到的问题,如果任务中出现加上printf就卡死,那么就很有可能时任务堆栈不足。
利用sprintf完成字符格式化,在通过HAL_UART_Transmit发送。
具体可以参考:在HAL库中的使用printf()函数和sprintf()函数