一,问题

在进行嵌入式开发的时候,用一些必要的LED或者printf之类的语句输出调试信息能极大的提升开发效率——“所见即所得”,但是printf语句却也有些很不方便的地方,这需要引起注意 ???

  • 1,大量的打印输出会占用硬件资源(如时钟,GPIO等)
  • 2,增加一定的功耗
  • 3,影响其他输出(如单片机的TX脚(打印脚)还接到了外设的RX脚,导致外设故障等)
  • 4,影响美观(发布版程序应该不包含这些调试代码)

介于这一问题,选择使用宏定义的方式来输出调试语句便是一种很好的解决方法 ???

二,解决

方案1:简洁的“#define ”宏定义 ●●●●●

//完整的调试信息:设备类型,当前源文件名,当前源函数名,当前源代码行号,格式化输出内容,格式化参数
#define DEBUG_L(format,argent...)  printf("Node:S, File: "__FILE__", Func: %s(), Line: %03d: "format"", __func__, __LINE__, ##argent) 

//简洁的调试信息:设备类型,当前源函数名,格式化输出内容,格式化参数
#define DEBUG_M(format,argent...)  printf("Node:S, Func: %s(), "format"", __func__, ##argent)

//最简洁的调试信息:格式化输出内容,格式化参数
#define DEBUG_S(format,argent...)  printf(""format"",##argent)

eg1:
使用:DEBUG_L(“SysTime:d:d:dn”,hour,min,sec);
打印:Node:S,File:main.c,Func:main(),Line:066,SysTime:22:30:11

eg2:
使用:DEBUG_M(“SysTime:d:d:dn”,hour,min,sec);
打印:Node:S,Func:main(),SysTime:22:30:11

eg3:
使用:DEBUG_S(“SysTime:d:d:dn”,hour,min,sec);
打印:SysTime:22:30:11

DEBUG_完全取代了printf,所有的DEBUG_(…)都被完全的替换成了printf(…),当需要关闭调试信息时,只需要在printf前面加上//即可注释掉所有的打印信息。

ANSI C标准中有几个常用的标准预定义宏:

  • __LINE__:在源代码中插入当前源代码行号;
  • __FILE__:在源文件中插入当前源文件名;
  • __DATE__:在源文件中插入当前的编译日期
  • __TIME__:在源文件中插入当前编译时间;
  • __STDC__:当要求程序严格遵循ANSI C标准时该标识被赋值为1;
  • __cplusplus:当编写C++程序时该标识符被定义。
  • __func__:在源代码中插入当前源函数名称;

方案2:专业的“#define,#ifdef,#ifend ”宏定义 ●●●○○

#ifdef __DEBUG

#define DEBUG_L(format,argent...)  printf("Node:S, File: "__FILE__", Func: %s(), Line: %03d: "format"", __func__, __LINE__, ##argent) 
#define DEBUG_M(format,argent...)  printf("Node:S, Func: %s(), "format"", __func__, ##argent)
#define DEBUG_S(format,argent...)  printf(""format"",##argent)

#endif

使用时候,需要先在.c文件或者.h文件的开头加#define __DEBUG,然后就可以直接使用DEBUG_L();DEBUG_M();DEBUG_S();来打印调试信息了,要想关闭,就直接删除#define __DEBUG即可。

当然了,这种方法的灵活性没有第一种好,但关闭时候更简单粗暴 ??

三、开启printf功能

//加入以下代码,支持printf函数,而不需要选择use MicroLIB      
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
    int handle; 
}; 

FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
_sys_exit(int x) 
{ 
    x = x; 
} 

//重定向c库函数printf到USART1
int fputc(int ch, FILE *f)
{
        /* 发送一个字节数据到USART1 */
        USART_SendData(USART1, (uint8_t) ch);
        
        /* 等待发送完毕 */
        while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);        
    
        return (ch);
}
#endif 

对于STM32单片机来说,一般使用USART1的PA9引脚打印调试信息,故将上代码加入到USART1的c文件即可。???


本文标签:笔记总结c语言