c语言printf转串口,C语言——设计printf调试宏

前言

借调试宏的设计,梳理下C语言宏的用法

重定向printf打印

嵌入式设备基本会配置RS232串口作为调试IO接口,假设底层串口单字节输出函数为SERIAL_PutChar(),利用fputc()和fputs()重定向printf函数

void fputc(int byte, FILE* stream)

{

(void)stream;

SERIAL_PutChar(byte);

}

void fputs(const char *pstr, FILE *stream)

{

(void)stream;

while(*pstr)

{

SERIAL_PutChar(*pstr++);

}

}

这样在代码里面利用printf()函数输出的字符串都老老实实从调试串口出来

调试宏使用场景

某个C驱动模块,希望在调试时打印调试信息,而产品代码中不显示调试信息。

v1--单参数宏

#define DRV_DEBUG 1

#if DRV_DEBUG

#define DRV_PRINT(x) printf(x)

#else

#define DRV_PRINT(x)

#endif

这个版本的DRV_PRINT(x)只能输出单变量——纯字符串

void foo()

{

DRV_PRINT("Driver Initialize Success!");

}

不需要打印调试信息时,更改DRV_DEBUG宏定义

#define DRV_DEBUG 0

当然也可以直接这样定义

#define DRV_PRINT printf

但是如果宏调用了多个参数:

void foo()

{

DRV_PRINT("Driver Initialize Success: ver %d.%d !", 1, 2);

}

产品代码中的#define DRV_PRINT(x)将编译错误!

怎么办?一种处女座肯定接受不了的做法,多加对括号

void foo()

{

DRV_PRINT(("Driver Initialize Success: ver %d.%d !", 1, 2));

}

不管是调试代码还是产品代码,编译都OK

v2--指定参数宏

#define DRV_DEBUG 1

#if DRV_DEBUG

#define DRV_PRINT(fmt, val1, val2) printf(fmt, val1, val2)

#else

#define DRV_PRINT(fmt, val1, val2)

#endif

如果只需要打印一个变量,第2个参数用随意值填位,如

void foo()

{

DRV_PRINT("Driver Initialize Success: ver %d !", val1, 2);

}

类似,如果有4个参数,就:

void foo()

{

DRV_PRINT("Driver Initialize Success: ver %d !", val1, 2, 3, 4);

}

很傻,但是没办法:(,VxWorks 5.5内核代码里就是这样干的!

v3--参数数量可变宏

C90和C++中可将宏声明为接受可变数量的自变量,如ARM编译器是这样的:

#define DRV_DEBUG 1

#if DRV_DEBUG

#define DRV_PRINT(fmt, ...) printf(fmt, __VA_ARGS__)

#else

#define DRV_PRINT(fmt, ...)

#endif

现在DRV_PRINT用法和printf完全一样了,这么爽的功能,C2000编译器却不支持!

题外话,注意这个特性C90支持,而C90是C++的一个子集,但是C99和C++却不兼容了

分层次LOG输出

有时候,某个模块,有输入跟踪信息,输出信息,错误信息等,如果我想单独打开某部分信息,可以这样设计

#define DRV_DEBUG 1

#define DRV_DEBUG_IN 0x0001

#define DRV_DEBUG_OUT 0x0002

#define DRV_DEBUG_ERR 0x0004

#define DRV_DEBUG_ALL 0xFFFF

#if DRV_DEBUG

unsigned int drv_flags = DRV_DEBUG_ERR | DRV_DEBUG_OUT;

#define DRV_PRINT(flag, fmt, ...) \

do{\

if(drv_flags & flag){\

printf(fmt, __VA_ARGS__)}\

}while(0)

#else

#define DRV_PRINT(fmt, ...)

#endif

NOTE: 多行宏,注意换行前加\号

这样,我只打印OUT和ERR:

void drv_write(char* msg_out)

{

DRV_PRINT(DRV_DEBUG_OUT, "Drivr write %s", msg_out); // 输出

DRV_PRINT(DRV_DEBUG_ERR, "Drivr write %s", msg_out); // 输出

DRV_PRINT(DRV_DEBUG_IN, "Drivr write %s", msg_out); // 不输出

}

进一步,可以设计针对整个系统不同模块的LOG输出控制!TCP/IP协议栈实现Lwip就是这么干的!

总结

在嵌入式C语言里面,运用printf调试宏,有助于事后分析,定位BUG,多多益善!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值