一、打印串口信息
代码
#define USART_DEBUG
#ifdef USART_DEBUG
#define user_printf(format, ...) printf(format"\r\n", ##__VA_ARGS__)
#define user_info(format, ...) printf(__FILE__"\t[info]:"format"\r\n", ##__VA_ARGS__)
#define user_debug(format, ...) printf(__FILE__"\t[debug]:"format"\r\n", ##__VA_ARGS__)
#define user_error(format, ...) printf(__FILE__"\t[error]:"format"\r\n", ##__VA_ARGS__)
#else
#define user_printf(format, ...)
#define user_info(format, ...)
#define user_debug(format, ...)
#define user_error(format, ...)
#endif
使用
通过开发板每500ms通过串口打印以下信息
user_printf("user_printf %d", i++);
user_info("user_info %d", i);
user_debug("user_debug %d", i);
user_error("user_error %d", i);
通过打印结果可知与普通的
printf
相比,有以下优点:
1)自动换行
2)显示文件路径
3)通过对齐使打印信息更整洁
4)不需要打印信息时,只需要注释掉#define USART_DEBUG
理解
制表符 \t
的作用
- 排版对齐
\t
通常用于格式化输出,帮助将文本或数据对齐,例如输出表格或列数据时,它可以让数据更加整齐
#include <stdio.h>
int main(void)
{
printf("aaa\tTest\r\n");
printf("aaaaa\tTest\r\n");
return 0;
}
- 跳到下一个制表位置
制表符让你能够快速将光标移动到一个固定的列位置,这样你就不需要手动计算空格数
可变参数宏
在GNU C中,从C99开始,宏可以接受可变数目的参数,就象可变参数函数一样。和函数一样,宏也用三个点…来表示可变参数
__VA_ARGS__
宏
#include <stdio.h>
#define debug(...) printf(__VA_ARGS__)
int main(void)
{
int year = 2024, month= 11, day= 7;
debug("[%d年%d月%d日]\tThis is a debug test\r\n", year, month, day);
return 0;
}
- 可变参数别称
通过一些语法,你可以给可变参数起一个名字,而不是使用__VA_ARGS__
#include <stdio.h>
#define debug(format, args...) printf(format, args)
int main(void)
{
int year = 2024, month= 11, day= 7;
debug("[%d年%d月%d日]\tThis is a debug test\r\n", year, month, day);
return 0;
}
- 无参传入情况
与可变参数函数不同的是,可变参数宏中的可变参数必须至少有一个参数传入,不然会报错,为了解决这个问题,需要一个特殊的“##”操作,如果可变参数被忽略或为空,“##”操作将使预处理器(preprocessor)去除掉它前面的那个逗号
#include <stdio.h>
#define debug(format, args...) printf(format, ##args)
int main(void)
{
debug("This is a debug test\r\n");
return 0;
}
- 宏连接符##
宏定义为:#define XNAME(n) x##n
代码为:XNAME(4)
预编译时,宏发现XNAME(4)
与XNAME(n)
匹配,则令n
为4
,然后将右边的n
的内容也变为4
,然后将整个XNAME(4)
替换为x##n
,亦即x4
,故最终结果为XNAME(4)
变为x4
#include <stdio.h>
#define XNAME(n) x##n
#define PRINT_XN(n) printf("x" #n " = %d\n", x##n);
int main(void)
{
int XNAME(1) = 14; // becomes int x1 = 14;
int XNAME(2) = 20; // becomes int x2 = 20;
PRINT_XN(1); // becomes printf("x1 = %d\n", x1);
PRINT_XN(2); // becomes printf("x2 = %d\n", x2);
return 0;
}
参考连接:
🫡优快云橘长_
二、简化代码(单片机)
代码
通过设置单片机 PB7
引脚的高低电平来控制 LED
的亮灭
#define LED0_GPIO_PORT GPIOB
#define LED0_GPIO_PIN GPIO_PIN_7
#define LED0(x) do{ x ? \
HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_RESET); \
}while(0) /* LED0翻转 */
使用
配置好 LED
控制引脚,并将上述宏定义写在 LED
驱动头文件后,在 main
函数的 while
循环中执行以下代码可实现亮、灭
LED0(1);
delay_ms(500);
LED0(0);
delay_ms(500);
上面的宏有什么作用?
1)前两个宏可以使我们更加方便的更换LED
的控制引脚
2)第3个宏可以使我们用更加简洁、易懂的代码去控制LED
亮灭
理解
- 预处理时,
LED0(1)
会替换成do{...}while
,1 会替换掉 x - 通过三目运算符(表达式1
?
表达式2:
表达式3)来判断执行那个表达式 \
可以给代码换行,更易观察