Log_Debug

调试打印文件名等信息                                                            

#define _DEBUG_TRACE_CMH_ 2  
#if 0 != _DEBUG_TRACE_CMH_  
    #include <cstdio>  
#endif  
  
#if 1==_DEBUG_TRACE_CMH_    //普通打印  
    #define TRACE_CMH printf  
#elif 2==_DEBUG_TRACE_CMH_  //打印文件名、行号  
    #define TRACE_CMH(fmt,...) \  
        printf("%s(%d): "##fmt, __FILE__, __LINE__, ##__VA_ARGS__)  
#elif 3==_DEBUG_TRACE_CMH_  //打印文件名、行号、函数名  
    #define TRACE_CMH(fmt,...) \  
        printf("%s(%d)-<%s>: "##fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)  
#else  
    #define TRACE_CMH  
#endif //_TRACE_CMH_DEBUG_  

这段代码中用到了这几个宏:
  1) __VA_ARGS__   是一个可变参数的宏,这个可宏是新的C99规范中新增的,目前似乎gcc和VC6.0之后的都支持(VC6.0的编译器不支持)。宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用。
  2) __FILE__    宏在预编译时会替换成当前的源文件名
  3) __LINE__   宏在预编译时会替换成当前的行号
  4) __FUNCTION__   宏在预编译时会替换成当前的函数名称

函数Trace:

#define FUNC_ENTRY    \
	{\
	printf("FUNC_ENTRY:   %s L#%d \n", __func__, __LINE__);  \
	}
#define FUNC_EXIT    \
	{\
	printf("FUNC_EXIT:   %s L#%d \n", __func__, __LINE__);  \
	}
#define FUNC_EXIT_RC(x)    \
	{\
	printf("FUNC_EXIT:   %s L#%d Return Code : %d \n", __func__, __LINE__, x);  \
	return x; \
	}

只显示文件名,不显示路径                                                      

#include <string.h> //strrchr()函数所需头文件
windows:
#define filename(x) strrchr(x,'\\')?strrchr(x,'\\')+1:x
linux:
#define filename(x) strrchr(x,'/')?strrchr(x,'/')+1:x


Marvell SDK                                                                               

#ifdef LOG_DEBUG

#define filename(x) strrchr(x,'/')?strrchr(x,'/')+1:x
#define my_d(_fmt_, ...)  \
		wmprintf(_fmt_ "\r\n",  ##__VA_ARGS__)
#define my_entry_d(_fmt_, ...)				\
		wmprintf("[%s(%d) - %s] " _fmt_ "\r\n", filename(__FILE__), __LINE__, __func__, ##__VA_ARGS__)
#else
#define my_d(...)
#define my_entry_d(...)
#endif 



我习惯的写法                                                                                 

#ifdef CONFIG_MY_DEBUG
#define LOG(LogTypeString,format,...)  printf("%s[" __FUNCTION__ "] "format, LogTypeString, __VA_ARGS__)
#define CONERR(format,...) LOG("Error:",format,__VA_ARGS__)
#define CONMSG(format,...) LOG("",format,__VA_ARGS__)
#define CONWRN(format,...) LOG("Warn:",format,__VA_ARGS__)
#define CONDBG(format,...) LOG("",format,__VA_ARGS__)
#else
#define LOG(LogTypeString,format,...)
#define CONERR(format,...)
#define CONMSG(format,...)
#define CONWRN(format,...)
#define CONDBG(format,...)
#endif 

配置echo格式                                                                             

echo显示带颜色,需要使用参数-e
格式如下:
echo -e "\033[字背景颜色;文字颜色m字符串\033[0m"
例如: 
echo -e "\033[41;37m TonyZhang \033[0m"
其中41的位置代表底色, 37的位置是代表字的颜色
 注:
1、字背景颜色和文字颜色之间是英文的“""”
2、文字颜色后面有个m
3、字符串前后可以没有空格,如果有的话,输出也是同样有空格
下面看几个例子:

echo -e "\033[30m 黑色字 \033[0m"
echo -e "\033[31m 红色字 \033[0m"
echo -e "\033[32m 绿色字 \033[0m"
echo -e "\033[33m 黄色字 \033[0m"
echo -e "\033[34m 蓝色字 \033[0m"
echo -e "\033[35m 紫色字 \033[0m"
echo -e "\033[36m 天蓝字 \033[0m"
echo -e "\033[37m 白色字 \033[0m"
 
echo -e "\033[40;37m 黑底白字 \033[0m"
echo -e "\033[41;37m 红底白字 \033[0m"
echo -e "\033[42;37m 绿底白字 \033[0m"
echo -e "\033[43;37m 黄底白字 \033[0m"
echo -e "\033[44;37m 蓝底白字 \033[0m"
echo -e "\033[45;37m 紫底白字 \033[0m"
echo -e "\033[46;37m 天蓝底白字 \033[0m"
echo -e "\033[47;30m 白底黑字 \033[0m"
控制选项说明 :
\33[0m 关闭所有属性 
\33[1m 设置高亮度 
\33[4m 下划线 
\33[5m 闪烁 
\33[7m 反显 
\33[8m 消隐 
\33[30m -- \33[37m 设置前景色 
\33[40m -- \33[47m 设置背景色 
\33[nA 光标上移n行 
\33[nB 光标下移n行 
\33[nC 光标右移n行 
\33[nD 光标左移n行 
\33[y;xH设置光标位置 
\33[2J 清屏 
\33[K 清除从光标到行尾的内容 
\33[s 保存光标位置 
\33[u 恢复光标位置 
\33[?25l 隐藏光标 
\33[?25h 显示光标 


字符串转换操作                                                                                               

/*将字符串s转换成相应的十进制整数*/  例如 "10" -> 10
int atoi(char s[])  
{  
    int i;  
    int n = 0;  
    for (i = 0; s[i] >= '0' && s[i] <= '9'; ++i)  {  
        n = 10 * n + (s[i] - '0');  
    }  
    return n;  
}  

/*将大写字母转换成小写字母*/  
int tolower(int c)  
{  
    if (c >= 'A' && c <= 'Z')  {  
        return c + 'a' - 'A';  
    }  
    else{  
        return c;  
    }  
}  

//将十六进制的字符串转换成整数  
int htoi(char s[])  
{  
    int i;  
    int n = 0;  
    if (s[0] == '0' && (s[1]=='x' || s[1]=='X'))  {  
        i = 2;  
    }  
    else  {  
        i = 0;  
    }  
    for (; (s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'z') || (s[i] >='A' && s[i] <= 'Z');++i)  {  
        if (tolower(s[i]) > '9')  {  
            n = 16 * n + (10 + tolower(s[i]) - 'a');  
        }  
        else  {  
            n = 16 * n + (tolower(s[i]) - '0');  
        }  
    }  
    return n;  
}  

// 十六进制数组 -> 字符串
int hex2str(uint8_t *src, char *dst, int len)
{
	int i;
	uint8_t hb;
	uint8_t lb;
	for(i = 0 ; i < len ; i++){
		hb = (src[i] & 0xf0) >> 4;
		if((hb >= 0)&&(hb <= 9))
			hb += 0x30;
		else if((hb >= 10)&&(hb <= 15))
			hb = hb - 10 + 'A';
		else
			return 1;

		lb = src[i] & 0x0f;
		if((lb >= 0)&&(lb <= 9))
			lb += 0x30;
		else if((lb >= 10)&&(lb <= 15))
			lb = lb - 10 + 'A';
		else
			return 1;

		dst[i*2] = hb;
		dst[i*2+1] = lb;
	}
	return 0;
}


// 字符串 -> 16进制
void strToHex(char *pbDest, char *pbSrc, int nLen)
{    
	char h1,h2;    
	char s1,s2;    
	int i;    

	for (i=0; i<nLen; i++){        
		h1 = pbSrc[2*i];        
		h2 = pbSrc[2*i+1];        
		s1 = toupper(h1) - 0x30;        

		if (s1 > 9)        
			s1 -= 7;        
		s2 = toupper(h2) - 0x30;        
		if (s2 > 9)        
			s2 -= 7;        
		pbDest[i] = s1*16 + s2;    
	}
}


输出限制范围内的随机数                                                                                  

static int get_rand(int limit)
{
	int val;
	while (1) {
		val = rand() % limit;
		if (val)
			break;
	}
	return val;
}


另外,关于time_t和tm讲解,有一篇不错的参考文章:

http://blog.youkuaiyun.com/luoweifu/article/details/20288549


GDB                                                                                                                                    

参考文章:

GDB十分钟教程:    http://blog.youkuaiyun.com/liigo/article/details/582231/

命令 解释 示例
file <文件名>加载被调试的可执行程序文件。
因为一般都在被调试程序所在目录下执行GDB,因而文本名不需要带路径。
(gdb) file gdb-sample
rRun的简写,运行被调试的程序。
如果此前没有下过断点,则执行完整个程序;如果有断点,则程序暂停在第一个可用断点处。
(gdb) r
cContinue的简写,继续执行被调试程序,直至下一个断点或程序结束。(gdb) c
b <行号>
b <函数名称>
b *<函数名称>
b *<代码地址>

d [编号]

b: Breakpoint的简写,设置断点。两可以使用“行号”“函数名称”“执行地址”等方式指定断点位置。
其中在函数名称前面加“*”符号表示将断点设置在“由编译器生成的prolog代码处”。如果不了解汇编,可以不予理会此用法。

d: Delete breakpoint的简写,删除指定编号的某个断点,或删除所有断点。断点编号从1开始递增。

(gdb) b 8
(gdb) b main
(gdb) b *main
(gdb) b *0x804835c

(gdb) d

s, ns: 执行一行源程序代码,如果此行代码中有函数调用,则进入该函数;
n: 执行一行源程序代码,此行代码中的函数调用也一并执行。

s 相当于其它调试器中的“Step Into (单步跟踪进入)”;
n 相当于其它调试器中的“Step Over (单步跟踪)”。

这两个命令必须在有源代码调试信息的情况下才可以使用(GCC编译时使用“-g”参数)。

(gdb) s
(gdb) n
si, nisi命令类似于s命令,ni命令类似于n命令。所不同的是,这两个命令(si/ni)所针对的是汇编指令,而s/n针对的是源代码。(gdb) si
(gdb) ni
p <变量名称>Print的简写,显示指定变量(临时变量或全局变量)的值。(gdb) p i
(gdb) p nGlobalVar
display ...

undisplay <编号>

display,设置程序中断后欲显示的数据及其格式。
例如,如果希望每次程序中断后可以看到即将被执行的下一条汇编指令,可以使用命令
“display /i $pc”
其中 $pc 代表当前汇编指令,/i 表示以十六进行显示。当需要关心汇编代码时,此命令相当有用。

undispaly,取消先前的display设置,编号从1开始递增。

(gdb) display /i $pc

(gdb) undisplay 1

iInfo的简写,用于显示各类信息,详情请查阅“help i”。(gdb) i r
qQuit的简写,退出GDB调试环境。(gdb) q
help [命令名称]GDB帮助命令,提供对GDB名种命令的解释说明。
如果指定了“命令名称”参数,则显示该命令的详细说明;如果没有指定参数,则分类显示所有GDB命令,供用户进一步浏览和查询。
(gdb) help display

设置某个文件某一行断点:

(gdb) break [<file-name>:]<func-name>
(gdb) break [<file-name>:]<line-num>

<think>好的,我需要帮助用户理解如何在USART中使用log_debug函数实现日志打印。首先,用户提到了之前的引用内容,特别是引用[3]中的示例,其中涉及日志级别和log_debug的使用。用户可能已经设置好了USART的基本功能,现在想进一步集成带调试级别的日志功能。首先,我需要回顾之前的回答,确保这次的内容一致。之前的回答包括了USART初始化、发送函数、格式化输出和日志分级控制。用户现在特别关注log_debug函数,所以需要详细说明如何结合日志级别和USART传输来实现。接下来,引用[3]中的代码显示了一个模块级别的日志控制,使用静态变量来设置日志级别。这可能意味着用户希望每个模块可以独立控制日志输出级别,这样可以在不同情况下灵活调整。需要将这种机制与USART的发送函数结合起来,比如在log_debug函数中检查当前日志级别是否允许输出调试信息,如果允许,则通过USART发送日志消息。然后,引用[2]中的宏定义使用了DMA传输,并等待传输完成标志。这可能涉及到如何确保数据完整发送,避免数据丢失。用户可能需要在log_debug函数中处理发送方式,比如使用阻塞发送或者DMA,但需要确保线程安全,尤其是在RTOS环境中。 用户的问题中还提到USART相关的配置,比如波特率和硬件连接,这些在之前的回答中已经覆盖,但可能需要在这里简要提及,确保log_debug函数正确使用已初始化的USART实例。需要分步骤解释如何实现log_debug函数,包括定义日志级别、实现日志函数、集成USART发送,并提供示例代码。同时,注意引用之前的引用内容,例如使用DMA发送(引用[2])和日志级别控制(引用[3])。另外,用户可能遇到的问题包括日志级别不生效、USART发送不成功或数据丢失,以及多线程环境下的安全性。在回答中需要提及这些注意事项,帮助用户避免常见错误。 最后,生成的相关问题应围绕日志实现的其他方面,如不同日志级别的控制、性能优化、多模块日志管理等,确保覆盖用户可能感兴趣的后续问题。</think>### 如何在USART中使用log_debug函数实现日志打印 #### 一、核心实现原理 通过结合日志分级控制机制与USART传输功能,实现带调试级别的日志输出。当调用`log_debug`时,系统会根据当前设置的日志级别判断是否需要通过USART发送调试信息[^3]。 #### 二、实现步骤 1. **定义日志级别枚举** ```c typedef enum { LOG_LEVEL_NONE = 0, LOG_LEVEL_ERROR, LOG_LEVEL_WARN, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_ALL } LOG_LEVEL; ``` 2. **实现日志函数** ```c // 全局日志级别控制变量 static LOG_LEVEL current_log_level = LOG_LEVEL_INFO; void log_debug(LOG_LEVEL level, const char *fmt, ...) { if (level > current_log_level) return; // 级别过滤 char buffer[128]; va_list args; va_start(args, fmt); vsnprintf(buffer, sizeof(buffer), fmt, args); va_end(args); // USART发送(引用[2]实现方式) HAL_UART_Transmit(&huart2, (uint8_t*)buffer, strlen(buffer), 100); while(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TC) == RESET); // 等待发送完成 } ``` 3. **USART初始化(关键参数)** ```c // 确保USART时钟和GPIO已正确配置 void MX_USART2_UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 115200; // 推荐波特率[^1] huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; HAL_UART_Init(&huart2); } ``` #### 三、使用示例 ```c // 设置全局日志级别 current_log_level = LOG_LEVEL_DEBUG; // 输出调试信息 log_debug(LOG_LEVEL_DEBUG, "Sensor value: %d, Status: %s\r\n", 245, "OK"); // 输出效果: // Sensor value: 245, Status: OK ``` #### 四、高级功能实现 1. **带格式的调试输出** ```c #define log_debug(level, ...) \ do { \ if (level <= current_log_level) { \ char _buf[128]; \ snprintf(_buf, sizeof(_buf), "[DEBUG] " __VA_ARGS__); \ HAL_UART_Transmit_DMA(&huart2, (uint8_t*)_buf, strlen(_buf)); \ } \ } while(0) ``` 2. **模块化日志控制(引用[3]方法)** ```c // 每个模块定义自己的日志级别 static LOG_LEVEL module_log_level = LOG_LEVEL_WARN; void module_task(void) { log_debug(module_log_level, "Initializing peripheral..."); // 模块内部操作 } ``` #### 五、关键注意事项 1. **缓冲区管理** - 建议使用循环缓冲区避免数据丢失 - 使用互斥锁保护共享资源(RTOS环境下)[^2] 2. **性能优化** - DMA传输可提升效率(参考引用[2]实现) - 设置合理日志级别减少无用输出 3. **输出格式规范** - 建议添加时间戳:`[T+00001234]` - 包含模块标识:`[MODULE_A]` $$ \text{日志有效性判断条件} = \begin{cases} \text{true} & \text{当 } L_{\text{current}} \geq L_{\text{msg}} \\ \text{false} & \text{其他情况} \end{cases} $$
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值