STM32单片机极简方法 使用宏定义 代替复杂的重定向printf()函数,实现串口打印。(HAL库例程)
对于 printf() 函数我们并不陌生,初学C语言使用的第一个函数,其作用是在终端打印显示格式化字符串。
但是如果我们使用的是单片机运行C语言代码,如果不经任何修改直接使用 printf() 函数,结果是什么现象都没有。要想使用这个函数,常规方法是重定向 printf() 函数,结合串口来打印到串口助手上位机。这里的重定向就非常复杂了,你需要懂 printf() 函数内部实现机制。。。。。(参考正点原子的例程,本人表示看不懂太多太复杂了)
接下来我介绍一种特别简单的方法,让你不需要重定向 printf() 函数也可以实现相似的功能,就是那种%d,%f,%c。。。各种格式控制符的功能,我们想用这个函数大半原因就是为了这些功能。只要在合适的地方添加以下语句即可:
#define _DEBUG_ 1 //串口打印宏函数开关,1是开,0是关,调试的时候开,调式完毕就可以一键关闭
uint8_t USART_TX_BUF[200]; //发送缓冲,最大200字节,不能太小,如果你的内容太长会访问非法内存
#if _DEBUG_
#define ps(...) HAL_UART_Transmit(&huart1,USART_TX_BUF,sprintf((char *)USART_TX_BUF,__VA_ARGS__),1000)//可修改到其他串口
#else
#define ps(...)
#endif
以上代码表示用 ps() 这个宏函数代替HAL库的串口发送函数,并且可以使用格式控制符%c,%d,%f,r,n等可变参数控制符。
ps()宏函数使用方法如下:
while (1)
{
num++;
ps("串口打印宏函数 rn");//无参数打印,rn表示换行
ps("num = %d rn",num);//含参数%d
HAL_Delay(500);
}
串口助手打印情况:
*************************************************************************************************
如果上面看不懂我再解释一下原理:
核心内容:变参宏 “__VA_ARGS__” 与 “...”
1.首先三个点 "..." 在C语言中代表“参数个数可变的参数”,我们可以看一下printf()函数的原型:
int printf(const char* format,...);//printf()函数声明原型
//使用printf函数的方法
int num1;
printf("num1 = %d rn",num1);//一个参数时,参数是整型
float num2;
printf("num2 = %f rn",num2);//参数是浮点型
printf("num1 = %d,num2 = %f rn",num1,num2);//两个不同类型的参数时
printf("Hello World rn");//无参数时
由上述代码可以知道,"fomat"表示只读类型的字符串,而三个点 “...” 表示个数未确定的参数,可以没有参数,也可以有多个。
2.__VA_ARGS__是三个点"..."的宏定义形式。也就是说宏定义中的__VA_ARGS__会被替换成"..."
#define ps(format,...) printf(format,__VA_ARGS__)//第1种方法,有些C标准不支持要加##
#define ps(format,...) printf(format,##__VA_ARGS__)//第2种方法,##可以防止无参数时编译出错
#define ps(...) printf(format,__VA_ARGS__)//第3种方法
#define ps(...) printf(format,##__VA_ARGS__)//第4种方法
#define ps(...) printf(__VA_ARGS__)//第5种方法
#define ps(...) printf(##__VA_ARGS__)//第6种方法
以上六种都可以用 ps() 代替 printf() 函数,功能以及用法一模一样。C标准不一样的时候可能会有差别,总之编译出错时在以上6种之中更换即可。我只用第1、2、5这三种方法。
3.其实在单片机之中如果不重定向 printf() 函数。我们只要使用 sprintf() 函数即可:
int printf(const char* format,...);//printf()函数声明原型
int sprintf(char *buffer,const char* format,...);//sprintf()函数声明原型
sprintf() 只是比 printf() 多一个参数,即第一个字符数组,他们两功能也相似,只是有以下区别:
sprintf()----------》把内容转成字符串,并输出到一个字符数组中,返回字符串的字符个数;
printf()------------》把内容转成字符串,并输出到显示终端,返回字符串的字符个数;
我们可以利用sprintf()的特点把要显示的内容转换成字符串,存到一个预先定义好的字符数组中,然后再用HAL库串口发送函数,把该字符数组内的信息发出去,发送的个数就是sprintf()的返回值:
uint8_t USART_TX_BUF[200]; //发送缓冲数组,最大200字节
#define ps(...) HAL_UART_Transmit(&huart1,USART_TX_BUF,sprintf((char *)USART_TX_BUF,__VA_ARGS__),1000)//可修改到其他串口
- 史上最清晰的红黑树讲解(下)
- 网络操作系统VyOS安装与初步使用
- 为何webpack风靡全球?三大主流模块打包工具对比
- 今天研究了一下手机通信录管理系统(C语言)
- Android软件测试Monkey测试工具
- 程序员面试50题(4)—把字符串转换成整数[算法]
- 程序员面试50题(3)—翻转句子中单词的顺序[算法]
- H5动画开发快车道
- Open vSwith模拟网关实现不同子网的互通
- 总结了一些指针易出错的常见问题(二)
- 史上最清晰的红黑树讲解(上)
- C++之new/delete/malloc/free详解
- 验证 结构体指针与自增运算符
- Spring Cloud中Hystrix 线程隔离导致ThreadLocal数据丢失下篇
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- VBA编写Ribbon Custom UI编辑器05——转换结构体XML
- MySQL 8.0.19 Linux平台安装 Part 1
- MySQL 8.0.19 Linux平台安装 Part 2
- 使用XtraBackup备份MySQL 8.0 Part 1 xtrabackup 8.0 安装
- 10个解放双手的 IDEA 插件,少些冤枉代码!
- 二叉树的 4 种遍历方式,你会多少?
- 【C++简明教程】Python和C++指定元素排序比较
- PG原生解码工具pg_recvlogical的使用-在脑裂时帮我们找回丢失的数据
- 使用XtraBackup备份MySQL 8.0 Part 4 对数据库进行全备
- 介绍一下java的基本类型; 每种数据类型的位数(答错了,太菜了)
- 可读代码编写炸鸡十一 - 小黄鸭从你的心里游到脑子里
- 大数据技术之Hadoop(HDFS)第1章 HDFS概述
- Mongodb多键索引之数组
- 使用XtraBackup备份MySQL 8.0 Part 5 对全备份进行恢复
- LeetCode题目32:最长的有效括号