STM32– 调试 -日志输出
在调试嵌入式程序时,输出日志是非常重要的环节,可以帮助开发者定位问题、监控程序状态和性能。以下是几种常见的日志输出方式及其适用场景:
1. 使用串口(UART)输出日志
实现方式:
通过串口将日志输出到主机的串口工具(如 PuTTY
、Tera Term
、minicom
)中。
优点:
示例代码:
#include <stdio.h>
// 配置 fputc 实现 printf 重定向到 UART
int fputc(int ch, FILE *f) {
UART_SendByte((uint8_t)ch); // 自定义的 UART 发送函数
while (!UART_IsTxEmpty()); // 等待发送完成
return ch;
}
void debug_log(const char *format, ...) {
va_list args;
va_start(args, format);
vprintf(format, args); // 使用重定向后的 printf
va_end(args);
}
int main(void) {
UART_Init(); // 初始化 UART
debug_log("System initialized.\n");
while (1) {
debug_log("Looping...\n");
HAL_Delay(1000);
}
return 0;
}
2. 使用 SWO 或 ITM 输出日志
实现方式:
优点:
使用工具:
示例代码:
#include "stm32f1xx.h"
void ITM_SendChar(uint8_t ch) {
while (ITM->PORT[0].u32 == 0); // 等待 ITM 可用
ITM->PORT[0].u8 = ch;
}
void debug_log(const char *format, ...) {
char buffer[128];
va_list args;
va_start(args, format);
vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
for (char *p = buffer; *p; ++p) {
ITM_SendChar(*p);
}
}
int main(void) {
debug_log("Hello SWO logging!\n");
while (1);
}
3. 使用 LED 或 GPIO 信号
实现方式:
通过控制 LED 闪烁频率或 GPIO 电平变化,输出简单的调试信息。
优点:
示例代码:
void debug_signal(uint8_t code) {
for (uint8_t i = 0; i < code; i++) {
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 假设 LED 在 PC13
HAL_Delay(200);
}
HAL_Delay(1000); // 间隔一段时间
}
int main(void) {
debug_signal(3); // 表示某种错误代码
while (1);
}
4. 使用 SD 卡或文件系统存储日志
实现方式:
将日志存储到 SD 卡或闪存,供后续分析。
优点:
示例代码:
#include "fatfs.h"
void debug_log(const char *format, ...) {
char buffer[128];
va_list args;
va_start(args, format);
vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
FIL file;
if (f_open(&file, "log.txt", FA_WRITE | FA_OPEN_APPEND) == FR_OK) {
f_write(&file, buffer, strlen(buffer), NULL);
f_close(&file);
}
}
5. 使用网络(如 Ethernet、Wi-Fi)输出日志
实现方式:
通过 TCP/UDP 协议将日志输出到远程服务器或 PC 工具。
优点:
示例代码:
void debug_log(const char *format, ...) {
char buffer[256];
va_list args;
va_start(args, format);
vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
// 假设使用 lwIP 的 UDP 发送
udp_send_to(buffer, strlen(buffer), "192.168.1.100", 5000);
}
6. 使用实时操作系统(RTOS)日志管理
实现方式:
借助 RTOS 的任务与队列,集中管理日志的输出。
优点:
示例代码:
QueueHandle_t logQueue;
void logger_task(void *param) {
char buffer[128];
while (1) {
if (xQueueReceive(logQueue, buffer, portMAX_DELAY)) {
printf("%s", buffer); // 可根据需求输出到 UART 或文件
}
}
}
void debug_log(const char *format, ...) {
char buffer[128];
va_list args;
va_start(args, format);
vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
xQueueSend(logQueue, buffer, 0);
}
int main(void) {
logQueue = xQueueCreate(10, 128);
xTaskCreate(logger_task, "Logger", 512, NULL, 1, NULL);
debug_log("RTOS logging started.\n");
vTaskStartScheduler();
return 0;
}
总结
根据项目实际需求选择合适的方法,同时要注意优化性能,避免日志输出对系统实时性造成干扰。
作者:code_snow