历史上的今天
返回首页

历史上的今天

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

正在发生

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

2021-08-16 来源:eefocus

常规打印方法

在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=

推荐阅读

史海拾趣

粤翔(FlyWin)公司的发展小趣事
明确需要保护的设备和数据的重要性,以及系统对掉电保护的具体要求。
DEWALT公司的发展小趣事

在第二次世界大战期间,DEWALT公司面临了巨大的挑战。由于战争的影响,许多原材料和零部件供应中断,生产受到严重影响。然而,DEWALT公司凭借自身的技术实力和创新能力,成功克服了这些困难。公司还积极为军队提供所需的电动工具产品,为战争胜利做出了贡献。这些经历使DEWALT公司更加坚定了技术创新和品质控制的信念。

Brite-Led Optoelectronics Inc公司的发展小趣事

随着市场竞争的加剧,Brite-Led意识到单凭自身力量难以持续保持领先。于是,公司积极寻求与其他企业和研究机构的合作研发机会。通过与一家知名高校的合作,Brite-Led成功开发出一种新型的LED封装技术,大幅提高了产品的可靠性和耐用性。这一合作不仅加强了Brite-Led的技术实力,也为其带来了更多的商业机会和合作伙伴。

Emerson公司的发展小趣事

1890年,Emerson在美国密苏里州圣路易斯市成立,最初是一家电机和风扇制造商。1892年,新公司靠交流电机起家,并生产出了第一批销往北美的电风扇,净销售额接近6万美元。随着技术的不断革新,Emerson于1897年开始生产吊扇,这一创新使得高层建筑更加宜居,吊扇业务迅速成为公司的重要收入来源。

Control Sciences Inc公司的发展小趣事

Control Sciences Inc公司在电子行业的初期,就以其技术创新而闻名。公司团队不断研发新的控制技术,成功打破了当时行业的局限。他们推出的首款智能控制系统,不仅提高了生产效率,还大大降低了能源消耗,为电子行业带来了巨大的经济效益。这一创新成果使得Control Sciences Inc在业界崭露头角,赢得了众多客户的青睐。

Exar [Exar Corporation]公司的发展小趣事

Exar公司自1971年在加利福尼亚州成立以来,最初以生产高性能、高可靠性的线性集成电路而著名。然而,随着技术的进步和市场的变化,Exar逐渐意识到通信领域的巨大潜力。因此,在1980年代初,公司开始专注于串行通信和数据存储解决方案,包括UARTs(通用异步收发器)和存储器ICs。这一转变不仅扩大了Exar的市场覆盖范围,也为其后续的发展奠定了坚实的基础。

问答坊 | AI 解惑

如何联系管理员

如何联系管理员 联系管理员有两种方式: 1)通过论坛版面右下角的“联系我们”的链接,直接点击快速发邮件与我们联系,如图所示 2)通过管理员团队(目前三个管理员soso、小志、小娜)发短消息与我们联系,如图所示 …

查看全部问答>

你想过自己做工程么?到底有多难?

最近几天也在跟朋友聊,关于创业的事,创业意味着自己有了奔向自由资金的潜力,因而,不管目前状况多窘迫,只要坚持下来,总有收获的。 那么如果想组建个自己的弱电公司,你觉得需要具备哪些条件呢? 也有朋友说,如果不是从产品入手而是从工程入 ...…

查看全部问答>

压马路也能发电?!

相信这对咱们的能源使用有些启发: 吃过晚饭,和心爱的人一起出去压马路散散步会是不错的选择。但你有没有想到其实这压马路也是能发电的呢~设计师Stephen Chan Wing Tak就准备将走路时脚对地面做功产生的热量利用起来,他带来的这款发电地板概念 ...…

查看全部问答>

[招聘]西安一知名公司招聘fpga设计/验证工程师、嵌入式软件工程师等

西安一个业界知名公司的研发中心正在招聘,主要面向fpga设计/验证工程师、嵌入式软件工程师。待遇绝对有竞争力,如有意向速发邮件至:nwumengfei@163.com…

查看全部问答>

怎么阅读芯片资料

刚开始看芯片资料,问几个简单的问题: 如三星S3C2440为例: 问题一:     这里的Address 0x56000050 。。。是虚拟地址还是物理地址啊? 问题二:     为什么GPFCON与GPFDAT的Address相差4?而在GPFCON中的GPF0—GPF7 ...…

查看全部问答>

Wince 使用Directshow

环境:EVC4+SP4 PB5.0 问题:使用Directshow,包含头文件 ,     这些头文件使用到了库文件strmiids.lib、dmoguids.lib,提示错误是canno open file srmiids.lib、dmoguids.lib,     哪位大哥能帮忙传一份DShow可用 ...…

查看全部问答>

求比较好的wince串口调试助手

以前用sunrain_hjb 前辈的wince串口调试助手,但是最近发现这个程序一旦打开就会造成系统运行很慢,刚开始我以为是我的串口驱动问题,但是我找一个比较简单的来用,发现我那个基本不会导致系统慢,并且我放到开发板也是一样的效果。 现在请大伙提 ...…

查看全部问答>

GPRS FLYFOTM260控制流程

我是个新手,刚刚应用FLYFOTM260 GPRS模块,发一点用到的程序,不知对大家有没有用…

查看全部问答>

招聘:德资企业招聘模拟电子工程师

亲爱的盆友们,        一家德资的知名电子公司招聘模拟电子工程师,公司中国区总部在深圳,中山、广州等地均有工厂。招聘量较大,2年至8、9、10年经验都ok的~   JD如下,请将简历发至wmm0415@126.com 或者 ...…

查看全部问答>