常规打印方法

在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一样简单。

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

最后

以上就是本次的分享。如有错误,欢迎指出!谢谢

本篇笔记会同步至我的个人博客:https://www.lizhengnian.cn/中,欢迎来访。

原创不易,期待您的在看、分享~

点个赞,证明你还爱我

STM32 KEIL 串口打印printf使用详解相关推荐

  1. linux中的shell有printf吗,Linux Shell系列教程之(八)Shell printf命令详解

    在上一篇:Linux Shell系列教程之(七)Shell输出这篇文章中,已经对Shell printf命令有了一个简略的介绍,本篇给大家详细介绍下Shell中的printf命令. 一.Shell p ...

  2. php打印出函数的内容吗,PHP打印函数集合详解以及PHP打印函数对比详解(精)

    1 echo();2 print();3 die();4 printf();5 sprintf();6 print_r();7 var_dump(); 1 echo() 可以同时输出多个字符串,可以多 ...

  3. Linux Shell脚本入门教程系列之(八)Shell printf命令详解

    本文是Linux Shell脚本系列教程的第(八)篇,更多shell教程请看:Linux Shell脚本系列教程 在上一篇:Linux Shell系列教程之(七)Shell输出这篇文章中,已经对She ...

  4. STM32应用IAP进行程序更新详解及实例

      这是以前就想写的一个小专题关于IAP,以及IAP在STM32编程的应用,专题分三小节,主要介绍常见的单片机烧录方式,IAP的实际应用,以及Ymodem协议在IAP编程中应用,在笔记吃灰很久了,终于 ...

  5. php怎么自定义设置打印区域,JavaScript_jQuery实现区域打印功能代码详解,使用CSS控制打印样式,需要设 - phpStudy...

    jQuery实现区域打印功能代码详解 使用CSS控制打印样式,需要设置样式media="print",并且将页面中不需要打印的元素的样式display属性设置为none.如DEMO ...

  6. js打印三角形超详解

    js打印三角形超详解 j控制星星的总行数,i控制每行星星的打印个数 打印图形如下: (1) (2) //str=""用来存储星星// 理解步骤1:在一行输出6个星星如何操作,在循环 ...

  7. FPGA串口接收与发送详解( part 3 )

    之前的part1~2已经详解完了单个数据的串口接收与发送,链接如下: FPGA串口接收与发送 详解 (part 1 )_居安士的博客-CSDN博客 FPGA串口接收与发送详解( part 2 )_居安 ...

  8. Keil串口打印log显示乱码

    Keil串口打印log显示乱码原因分析及解决方案 背景 出错原因及解决方案 1. 波特率不正确 2. 编码格式不正确 3. 外部晶振不一致 小结 背景 使用GD32F450IIH6作为MCU时,外部晶 ...

  9. 迅为linux下串口,迅为iMX6UL开发板多路串口开发平台接口详解

    原标题:迅为iMX6UL开发板多路串口开发平台接口详解 iMX6UL开发板 核心板参数 尺寸:38mm*42mm CPU:iMX6UL 主频528MHz ARM Cortex-A7架构 单核 内存:5 ...

最新文章

  1. .net core 中的[FromBody]
  2. 云服务器如何导入文件,如何将文件导入云服务器中
  3. C# 单元测试简单入门
  4. leetcode 28. Implement strStr() 实现strStr()
  5. k8s之kubebuilder简单理解
  6. 4936: 特殊排序
  7. Omap3530 的GPIO中断设置
  8. oracle data guard方案,Oracle Data Guard 概念篇
  9. ECCV2018 论文简析 Oral_1
  10. x内存满白苹果解决_iphone12pro max无限白苹果重启怎么办
  11. spring3-mvc实例-信息转换
  12. java day57【 Spring 概述 、 IoC 的概念和作用、使用 spring 的 IOC 解决程序耦合 】...
  13. 【leetcode 简单】第五题 最长公共前缀
  14. 51单片机基础知识(重点)
  15. 智能计算机与应用是核心期刊吗,人工智能的核心期刊都有哪些
  16. vb.net 简单取摄像头图片_简单的BP网络识别液晶字符
  17. Java岗大厂面试百日冲刺【Day52】— 数据库8 (日积月累,每日三题)
  18. BadImageFormatException-试图加载格式不正确的程序(0x8007000B)
  19. 2022-8-03 第七小组 黄均睿 学习日记 (day27)线程2
  20. mysql查询第10到第20条记录_“取出数据表中第10条到第20条记录”的sql语句+selecttop用法...

热门文章

  1. Jquery JS 正确比较两个数字大小的方法
  2. android蓝牙打印机
  3. C#托管代码与C++非托管代码互相调用二(C++调用C#代码)
  4. 2011阿里巴巴集团实习生招聘笔试题 CC++
  5. UA OPTI570 量子力学2 物质波与物质粒子
  6. UA MATH567 高维统计IV Lipschitz组合2 Spherical Distribution的Lipschitz函数 Isoperimetric不等式
  7. JSP JSTL标签库基本使用
  8. Windows内核工具Win64AST初步使用
  9. 修改Thickbox,预加载图片和点击图片前后浏览
  10. JavaScript RegExp 对象