当前位置: 首页 > article >正文

STM32-- 调试 -日志输出

在调试嵌入式程序时,输出日志是非常重要的环节,可以帮助开发者定位问题、监控程序状态和性能。以下是几种常见的日志输出方式及其适用场景:


1. 使用串口(UART)输出日志

实现方式

通过串口将日志输出到主机的串口工具(如 PuTTYTera Termminicom)中。

优点
  • 简单易用,几乎所有嵌入式设备都支持。
  • 实时性强,适合调试运行时的动态信息。
  • 与 printf 结合使用方便。
示例代码
#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 输出日志

实现方式
  • STM32 和一些 Cortex-M 系列 MCU 提供 SWO(Single Wire Output)ITM(Instrumentation Trace Macrocell) 输出,支持通过 ST-Link 等调试器采集日志。
优点
  • 非阻塞,性能开销小。
  • 无需占用 UART,引脚利用率高。
  • 支持与调试器同步显示日志。
使用工具
  • Keil MDK 自带 SWO Viewer。
  • STM32CubeMonitor。
  • Segger RTT Viewer。
示例代码
#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;
}

总结

  • 实时调试:优先使用串口(UART)、SWO 或网络。
  • 脱机调试:使用 SD 卡或闪存存储日志。
  • 简单场景:用 LED 或 GPIO。
  • 复杂系统:结合 RTOS 提供的队列和任务管理日志输出。

根据项目实际需求选择合适的方法,同时要注意优化性能,避免日志输出对系统实时性造成干扰。


http://www.kler.cn/a/408119.html

相关文章:

  • Android Binder技术概览
  • IIFE - 立即执行函数
  • listening comprehension
  • 跟《经济学人》学英文:2024年11月23日这期 Why British MPs should vote for assisted dying
  • 下载安装Android Studio
  • Oracle - 多区间按权重取值逻辑 ,分时区-多层级-取配置方案(二)
  • vue3的宏到底是什么东西?
  • C++ 中数组作为参数传递时,在函数中使用sizeof 为什么无法得到数组的长度
  • 【MATLAB源码-第222期】基于matlab的改进蚁群算法三维栅格地图路径规划,加入精英蚁群策略。包括起点终点,障碍物,着火点,楼梯。
  • Linux探秘坊-------1.系统核心的低语:基础指令的奥秘解析(1)
  • VSCode 使用技巧
  • Java开发经验——并发工具类库线程安全问题
  • IP转发流程
  • Elasticsearch搜索流程及原理详解
  • Java Web后端项目的特点和组成部分
  • 【element-tiptap】Tiptap编辑器核心概念----内容、扩展与词汇
  • 基于NVIDIA NIM 平台打造智能AI知识问答系统
  • 【JAVA 笔记】12 带有数据库文件的完整的JDBC访问例子,命令行界面
  • C++适配器模式之可插入适配器的实现模式和方法
  • 大模型(LLMs)推理篇
  • 前端开发调试之移动端调试学习笔记
  • 【自动化】如何从列表中找到图片并命名保存下来
  • 2061:【例1.2】梯形面积(http://ybt.ssoier.cn:8088/problem_show.php?pid=2061)
  • 科研实验室的数字化转型:Spring Boot系统
  • C# 命名空间详解(一)
  • 计算机网络socket编程(5)_TCP网络编程实现echo_server