历史上的今天
返回首页

历史上的今天

今天是:2025年08月11日(星期一)

正在发生

2021年08月11日 | STM32 | 串口打印知多少?

2021-08-11 来源:eefocus

常规打印方法

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


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

要实现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]);

  }

}


测试结果:

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

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

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

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

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

(1)应用一:

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


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


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


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


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


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


(2)应用二:

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


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

推荐阅读

史海拾趣

Allied Controls Incorporated公司的发展小趣事

Allied Controls Incorporated(ACI)是一家专注于设计和制造温度传感器和控制器的公司,以下是其发展历程的五个相关故事:

  1. 创立和早期发展: Allied Controls Incorporated成立于1983年,总部位于美国新泽西州。公司的创始人是一群对温度控制技术充满热情的工程师和企业家。起初,ACI专注于为工业和商业应用提供高品质的温度传感器和控制器。通过不断提高产品质量和性能,公司逐渐赢得了客户的信任和好评,实现了良好的初期发展。

  2. 技术创新与产品优化: 随着市场需求的变化和技术的发展,ACI不断进行技术创新,并不断优化其产品线。公司投入大量资源用于研发新型温度传感器和控制器,以满足不同行业的需求。ACI的工程团队与客户紧密合作,根据客户的反馈和需求不断改进产品设计和性能,确保产品的质量和可靠性。

  3. 扩大市场份额: 随着业务的稳步增长,ACI逐渐扩大了其市场份额,涵盖了更多的行业和应用领域。除了工业和商业应用外,公司还开始进军医疗、航空航天、汽车和消费电子等领域。通过开发定制化解决方案和与行业领先企业合作,ACI成功地拓展了其市场份额,并在各个行业中树立了良好的声誉。

  4. 国际市场拓展: 除了在美国市场取得成功外,ACI还积极拓展国际市场。公司与全球各地的合作伙伴建立了稳固的合作关系,拓展了产品的销售网络。ACI的产品出口到欧洲、亚洲和其他地区,赢得了国际客户的青睐和认可。通过不断开拓国际市场,ACI进一步巩固了其在全球温度传感器和控制器领域的领先地位。

  5. 持续创新和发展: 作为一家技术驱动型公司,ACI致力于持续创新和发展。公司不断投入研发和技术改进,推出更加先进和高性能的产品。ACI的工程团队不断探索新的技术和解决方案,以满足客户日益增长的需求。通过不断创新和发展,ACI得以在电子行业中保持竞争优势,并为客户提供更加可靠和高效的温度控制解决方案。

德国ACAM公司的发展小趣事

2014年,ACAM公司迎来了一个重要的里程碑事件——被奥地利微电子股份有限公司收购。这次收购极大地增强了ACAM公司在时间数字转换器(TDC)技术领域的实力。奥地利微电子在半导体行业有着深厚的积累和丰富的经验,与ACAM公司的技术优势相结合,进一步提升了ACAM公司在市场上的竞争力。

ASM公司的发展小趣事

进入21世纪,ASM公司开始关注中国市场的潜力。随着中国经济的快速增长和半导体产业的蓬勃发展,ASM公司看到了巨大的商机。公司加大了对中国市场的投入,与中国半导体代工厂建立了紧密的合作关系,为中国市场提供了高质量的产品和服务。这一举措不仅促进了ASM公司在中国市场的快速发展,也为中国半导体产业的进步做出了贡献。

佰宏(BHFUSE)公司的发展小趣事

随着电子产品的不断升级和电路防护需求的提高,佰宏团队不断进行技术研发和产品创新。他们成功开发出了多种严苛环境下的客制化PPTC自恢复保险丝,满足了高精密高标准的电路防护需求。这一技术突破不仅提升了产品的竞争力,也为公司赢得了更多的市场份额。

CYAN公司的发展小趣事

随着公司的发展,CYAN开始寻求与大型企业的合作。2009年,CYAN与沃尔沃汽车达成战略合作,为其提供了先进的网络解决方案,并成功将技术应用于沃尔沃的量产车型中。这次合作不仅提升了CYAN的品牌知名度,也为其带来了更多的商业机会。

Advanced Analog公司的发展小趣事

创新是Advanced Analog公司发展的核心动力。公司始终坚持自主研发和技术创新,不断推出新的产品和技术。这些新产品不仅满足了市场的多样化需求,也进一步巩固了公司在电源管理IC领域的领先地位。同时,Advanced Analog还积极关注行业发展趋势和未来技术走向,为公司的长远发展做好战略布局。

问答坊 | AI 解惑

好用的基准电压源

好用的基准电压源…

查看全部问答>

一个讲电源测试的PPT(very good)

一个讲电源测试的PPT(very good)…

查看全部问答>

DSP系统的测试和调试4

DSP系统的测试和调试4…

查看全部问答>

那位高手用过dp-1581+单片机开发过东西,现在学习中...

那位高手用过dp-1581+单片机开发过东西,现在学习中...能给点代码参考学习一下,那怕最简单的都可以,只是学习用的现在手里有一个仿真板 stc89c52+dp-1581 玩了几天也没玩转,望高手指教一下,学习中...... …

查看全部问答>

内核调试的方法,有点疑惑还是

Platform Settings->Build Options里只选择Enable Eboot Space in Memory 和 Enable Full Kernel Mode。如果要用到 KITL Debug,就需要选择 Enable CE Target Control Support, Enable KITL, Enable Kernel Debugger。(KITL 和 CS8900 driver 不能 ...…

查看全部问答>

请问EVC下开发的应用程序如何下载到目标平台上?

我用PB编译好了winCE内核后,已经把镜像下载到了目标平台上,跑起来了。现在下一步是开发应用程序,就是把evc下的应用程序也下载到目标平台上。我不想使用把应用程序编译到内核然后一起下载到目标板上的的方法,觉得那样不够灵活。请问内核下载到目 ...…

查看全部问答>

可原地旋转的汽车

如果没有过硬的驾驶技巧,是很难让汽车实现360度的原地旋转的。然而有了我们今天为大家介绍的这款概念车,这种“特技”就变成了家常便饭。         这款概念车的四个轮子与车体的连接处都被设计成了弧形的,因此四个轮子 ...…

查看全部问答>

TI阅读:电压基准如何影响 ADC 性能,第 3 部分

本帖最后由 dontium 于 2015-1-23 11:40 编辑 电压基准如何影响 ADC 性能,第 3 部分 …

查看全部问答>

WIFI小车 手机可控

WIFI小车制作全套资料,包括手机端的android程序源码 模块ST-MW-08S模块淘宝有售。  …

查看全部问答>

山东竞赛群196263245

本帖最后由 paulhyde 于 2014-9-15 03:46 编辑 欢迎大家加入,共同讨论,共同提高196263245  …

查看全部问答>