STM32 HAL库:使用C标准库或MicroLIB实现printf && 多串口使用printf输出 && 结合DMA实现printf && 常见问题总结

综合多篇文章对实现printf进行总结,本篇博客包含内容如下:

  • 使用MicroLIB实现printf(Windows环境下和Linux环境下)
  • 使用C标准库实现printf(也就是不需要配置工程打开MicroLIB)
  • 结合DMA实现printf(针对大量数据、OS任务间使用printf冲突)
  • 多串口通信(当有多个串口通信设备时)
  • printf使用常见问题

如果本篇博客对你有帮助的话,记得点个赞哦!谢谢大家!

文章目录

  • 一、需打开MicroLIB版
    • 打开MicroLIB,后面的步骤基于此(重点!!!!!!!)
    • 1.1 方案一
      • 1.1.1 修改usart.c
        • Ⅰ添加头文件stdio.h
        • Ⅱ 定义结构体FILE
        • Ⅲ 重写fputc() & fgetc函数
        • Ⅳ 修改后usart.c
      • 1.1.2 使用演示
    • 1.2 方案二(Linux环境下有用)
      • 1.2.1 添加头文件并修改usart.c
      • 1.2.2 使用演示
  • 二、标准库(不需要打开MicroLIB)
    • 2.1 修改usart.c文件
    • 2.2 使用演示
  • MicroLIB库和C标准库有什么区别?
  • 三、大量数据传输(DMA)
    • 3.1 什么时候需要
    • 3.2 Cube配置DMA
    • 3.3 代码移植
      • 3.3.1 修改usart.c
      • 3.3.2 修改usart.h
  • 四、多串口通信(满足与ESP8266、ESP32等进行通信)
    • 4.1写法一
      • 4.1.1 修改usart.c
      • 4.1.2修改usart.h
      • 4.1.3 使用演示
    • 4.2 写法二
      • 4.2.1 修改usart.c
      • 4.2.2 修改usart.h
      • 4.2.3 使用演示
  • 五、关于printf的问题总结
    • 5.1 不加\r\n串口助手不显示(两种情况)
      • 5.1.1 串口不发送
      • 5.1.2 串口助手程序的问题
    • 5.2 debug时卡死在LDR R0, =SystemInit
    • 5.3 FreeRTOS堆栈不足,导致printf函数无法使用
    • 5.4 不修改文件实现printf的效果

一、需打开MicroLIB版

打开MicroLIB,后面的步骤基于此(重点!!!!!!!)

鉴于很多作者忘记在文章中提醒读者打开MicroLIB,所以我在最开始就把这个步骤列出来。
STM32 HAL库:使用C标准库或MicroLIB实现printf && 多串口使用printf输出 && 结合DMA实现printf && 常见问题总结_第1张图片

如果不打开MicroLIB,可能会在stm32进行调试时,进入 LDR R0, =SystemInit卡死。参考文章

1.1 方案一

1.1.1 修改usart.c

Ⅰ添加头文件stdio.h

在usart.c头文件定义段加入stdio.h

#include 
Ⅱ 定义结构体FILE

这步不要也行,我把它注释掉后,照样能够正常输出汉字、整数、浮点数、字符、字符串。

现在自己知识不够,解释不了这个结构体的意义,后面知道了回来把这个坑补上。

struct FILE { 
	int handle; 
}; 
Ⅲ 重写fputc() & fgetc函数
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; 
}
Ⅳ 修改后usart.c

切记将这些内容写在沙盒段(USER BEGIN 和 USER END)之间
STM32 HAL库:使用C标准库或MicroLIB实现printf && 多串口使用printf输出 && 结合DMA实现printf && 常见问题总结_第2张图片

1.1.2 使用演示

在需要printf的c文件#include ,然后就可以使用printf了。

测试输出整数、浮点数、字符、字符串
STM32 HAL库:使用C标准库或MicroLIB实现printf && 多串口使用printf输出 && 结合DMA实现printf && 常见问题总结_第3张图片

1.2 方案二(Linux环境下有用)

1.2.1 添加头文件并修改usart.c

#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编译器进行编译,那么这两种方案就有区别了。

1.2.2 使用演示

经过验证,可用

二、标准库(不需要打开MicroLIB)

2.1 修改usart.c文件

#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;
}

2.2 使用演示

与前面使用方法相同,不再赘述。

MicroLIB库和C标准库有什么区别?

详情链接

三、大量数据传输(DMA)

3.1 什么时候需要

  • 需要传输大量数据时,为了减缓CPU的压力
  • FreeRTOS多任务之间使用printf冲突

3.2 Cube配置DMA

STM32 HAL库:使用C标准库或MicroLIB实现printf && 多串口使用printf输出 && 结合DMA实现printf && 常见问题总结_第4张图片

3.3 代码移植

3.3.1 修改usart.c

添加以下代码

#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发送字符串
}

3.3.2 修改usart.h

void printf_DMA(UART_HandleTypeDef *husart, char *fmt,...);

四、多串口通信(满足与ESP8266、ESP32等进行通信)

4.1写法一

4.1.1 修改usart.c

添加以下代码到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 );		
	}
 
}

4.1.2修改usart.h

添加以下代码到usart.h中

void usartPrintf(UART_HandleTypeDef USARTx, char *fmt,...);

4.1.3 使用演示

与前面不同的是,如果这里要使用usartPrintf()包含的头文件是usart.h,不是stdio.h,usartPrintf的用法和printf一样。

STM32 HAL库:使用C标准库或MicroLIB实现printf && 多串口使用printf输出 && 结合DMA实现printf && 常见问题总结_第5张图片

4.2 写法二

地址:

4.2.1 修改usart.c

#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发送
}

4.2.2 修改usart.h

在usart.h中添加以下代码

uint8_t XL_Printf(UART_HandleTypeDef *huart,const char *format, ...);

4.2.3 使用演示

包含头文件#include

STM32 HAL库:使用C标准库或MicroLIB实现printf && 多串口使用printf输出 && 结合DMA实现printf && 常见问题总结_第6张图片

五、关于printf的问题总结

5.1 不加\r\n串口助手不显示(两种情况)

5.1.1 串口不发送

这个我没遇到过,是在浏览相关文章的时候看到的,有点没看明白,他说的解决方法我没看到,感觉没啥变化。

文章地址

5.1.2 串口助手程序的问题

有的串口助手在接收字符串时必须以\r\n结尾才会显示,否者不会在屏幕上显示。例如我的就是在vofa+上无法显示,在串口调试助手上就能正常显示。

5.2 debug时卡死在LDR R0, =SystemInit

这个是因为没有打开微库MicroLIB的原因造成的。

5.3 FreeRTOS堆栈不足,导致printf函数无法使用

这个是我在使用FreeRTOS时遇到的问题,如果任务中出现加上printf就卡死,那么就很有可能时任务堆栈不足。

5.4 不修改文件实现printf的效果

利用sprintf完成字符格式化,在通过HAL_UART_Transmit发送。

具体可以参考:在HAL库中的使用printf()函数和sprintf()函数

你可能感兴趣的:(STM32Cubemx,STM32,stm32,c语言)