历史上的今天

今天是:2025年08月16日(星期六)

正在发生

2021年08月16日 | STM32 KEIL 串口打印printf使用详解

发布者:心动代码 来源: eefocus关键字:STM32  KEIL  串口打印  printf 手机看文章 扫描二维码
随时随地手机看文章

常规打印方法

在STM32的应用中,我们常常对printf进行重定向的方式来把打印信息printf到我们的串口助手。


在MDK环境中,我们常常使用MicroLIB+fputc的方式实现串口打印功能,即:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

要实现fputc函数的原因是:printf函数依赖于fputc函数,重新实现fputc内部从串口发送数据即可间接地实现printf打印输出数据到串口。


不知道大家有没有看过正点原子裸机串口相关的例程,他们的串口例程里不使用MicroLIB,而是使用标准库+fputc的方式。相关代码如:

#if 1

#pragma import(__use_no_semihosting)

//标准库需要的支持函数

struct __FILE

{

    int handle;

};


FILE __stdout;

/**

 * @brief 定义_sys_exit()以避免使用半主机模式

 * @param void

 * @return  void

 */

void _sys_exit(int x)

{

    x = x;

}


int fputc(int ch, FILE *f)

{

    while((USART1->ISR & 0X40) == 0); //循环发送,直到发送完毕


    USART1->TDR = (u8) ch;

    return ch;

}

#endif


关于这两种方法的一些说明可以查看Mculover666兄的 《重定向printf函数到串口输出的多种方法》这篇文章。这篇文章中不仅包含上面的两种方法,而且也包含着在GCC中使用标准库重定向printf的方法。


自己实现一个打印函数

以上的几种方法基本上是改造C库的printf函数来实现串口打印的功能。其实我们也可以自己实现一个串口打印的功能。


printf本身就是一个变参函数,其原型为:

int printf (const char *__format, ...);


所以,我们要重新封装的一个串口打印函数自然也应该是一个变参函数。具体实现如下:


1、基于STM32的HAL库

左右滑动查看全部代码>>>

#define TX_BUF_LEN  256     /* 发送缓冲区容量,根据需要进行调整 */

uint8_t TxBuf[TX_BUF_LEN];  /* 发送缓冲区                       */

void MyPrintf(const char *__format, ...)

{

  va_list ap;

  va_start(ap, __format);

  

  /* 清空发送缓冲区 */

  memset(TxBuf, 0x0, TX_BUF_LEN);

  

  /* 填充发送缓冲区 */

  vsnprintf((char*)TxBuf, TX_BUF_LEN, (const char *)__format, ap);

  va_end(ap);

  int len = strlen((const char*)TxBuf);

  

  /* 往串口发送数据 */

  HAL_UART_Transmit(&huart1, (uint8_t*)&TxBuf, len, 0xFFFF);

}


因为我们使用printf函数基本不使用其返回值,所以这里直接用void类型了。

自定义变参函数需要用到va_start、va_end等宏,需要包含头文件stdarg.h。关于变参函数的一些学习可以查看网上的一些博文,如:

https://www.cnblogs.com/wulei0630/p/9444062.html

这里我们使用的是STM32的HAL库,其给我们提供HAL_UART_Transmit接口可以直接把整个发送缓冲区的内容给一次性发出去。


2、基于STM32标准库

若是基于STM32的标准库,就需要一字节一字节的循环发送出去,具体代码如:

左右滑动查看全部代码>>>

#define TX_BUF_LEN  256     /* 发送缓冲区容量,根据需要进行调整 */

uint8_t TxBuf[TX_BUF_LEN];  /* 发送缓冲区                       */

void MyPrintf(const char *__format, ...)

{

  va_list ap;

  va_start(ap, __format);

    

  /* 清空发送缓冲区 */

  memset(TxBuf, 0x0, TX_BUF_LEN);

    

  /* 填充发送缓冲区 */

  vsnprintf((char*)TxBuf, TX_BUF_LEN, (const char *)__format, ap);

  va_end(ap);

  int len = strlen((const char*)TxBuf);

  

  /* 往串口发送数据 */

  for (int i = 0; i < len; i++)

  {

 while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);    

 USART_SendData(USART1, TxBuf[i]);

  }

}


测试结果:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

我们也可以使用我们的MyPrintf函数按照上一篇文章: 《C语言、嵌入式中几个非常实用的宏技巧》的方式封装一个宏打印函数:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

以上就是我们自定义方式实现的一种串口打印函数。

但是,我想说:对于串口打印的使用,我们没必要自己创建一个打印函数。

看到这,是不是有人想要打我了。。。。看了半天,你却跟我说没必要用。。。

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

哈哈,别急,我们不应用在串口打印调试方面,那可以用在其它方面呀。

(1)应用一:

比如最近我在实际应用中:我们的MCU跑的是我们老大自己写的一个小的操作系统+我们公司自己开发的上位机

我们MCU端与上位机使用的是串口通讯,MCU往上位机发送的数据有两种类型,一种是HEX格式数据,一种是字符串数据。

但是我们下位机的这两种数据,在通过串口发送之前都得统一把数据封包交给那个系统通信任务,然后再由通信任务发出去。

在这里,就不能用printf了。老大也针对他的这个系统实现了一个deb_printf函数用于打印调试。

但是,那个函数既复杂又很鸡肋,稍微复杂一点的数据就打印不出来了。

因此我利用上面的思路给它新封装了一个打印调试函数,很好用,完美地兼容了老大的那个系统。具体代码就不分享了,大体代码、思路如上。

(2)应用二:

我们在使用串口与ESP8266模块通讯时,可利用类似这样的方式封装一个发送数据的函数,这个函数的使用可以像printf一样简单。

可以以很简单的方式把数据透传至服务端,比如我以前的毕设中就有这么应用:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

关键字:STM32  KEIL  串口打印  printf 引用地址:STM32 KEIL 串口打印printf使用详解

上一篇:STM32中重定义printf函数
下一篇:【STM32】学习笔记RTC日历基础应用问题分析

推荐阅读

以“共创智慧新动能,共享开放新时代”为主题的2018世界机器人大会,于8月15日至8月19日在北京亦创国际会展中心举行。世界机器人大会已经成功举办三届,有机器人界“达沃斯”、“汉诺威展”、“奥运会”的美称,业已发展成为沟通中国与世界、融合科技与产业的一个重要平台。2018世界机器人大会由论坛、博览会、大赛、地面无人系统活动四大板块组成,将继往...
5G只是小儿科,人工智能才是大产业,才是华为发展的战略要地。人类社会未来二三十年最大的推动是人工智能。如果科技是一顶皇冠,那么人工智能将是这顶皇冠上的明珠。 就在外界普遍认同“5G”是华为当之无愧的代名词时,任正非在近日(7月31日)华为“千疮百孔的烂伊尔2飞机”战旗交接仪式上的最新讲话中提出了如上的观点,再次正式的将华为人工智能提到台...
近期,摩托罗拉一款型号为E7 Plus的新机现身GeekBench,预计在近期就将发布。这款手机是摩托罗拉E7的升级版,GeekBench数据显示,该手机的运行内存为4GB,是摩托罗拉E7的两倍。  摩托罗拉E7 Plus在GeekBench跑分单核为1152分,多核达到了4373分,尚不清楚是哪一款处理器。上一代摩托罗拉E7搭载了骁龙632处理器,E7 Plus应该会有所升级,大...
随着“新基建”快进键的按下,得益于网络、和人工智能算法等技术的支持,机器人正迎来一轮新的发展。智能家居机器人、无人配送机器人、智能指引机器人等等,一系列贴近日常生活的机器人成为令人瞩目的新宠。虽然远不及故事中虚构的拟人机器,但真实的机器人已在智能生产的重要环节取代人工,成为有效率的生产方式。 目前投入市场的智能机器人以实现具体...

史海拾趣

小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved