目录

RTT和Easylogger结合使用

RTT和Easylogger的关系

结合使用操作步骤:

错误解决:

问题根源分析

FreeRTOS 下的elog“锁”实现:(同/异步都一样)

修改 elog_port.c 文件

FreeRTOS下实现异步输出日志

elog_cfg.h

elog_async.c

elog_port.c

main.c 初始化

异步输出问题:无法一次性连续发送多条(>11条)日志

问题说明

ds 的解决方法:

1. 提高异步输出任务优先级

2. 优化异步任务处理逻辑

3. 优化默认任务的日志输出

4. 添加缓冲区监控功能


RTT和Easylogger结合使用

RTT和Easylogger的关系

        RTT 是 “调试数据传输工具”,EasyLogger 是 “嵌入式日志管理库”,二者可协同工作,而非竞争或从属关系。

维度 RTT(Real Time Transfer) EasyLogger
核心定位 SEGGER 推出的调试数据实时传输技术(依附 J-Link) 国产开源的嵌入式日志管理库(纯软件组件)
核心功能 解决 “目标板→PC” 的高效数据传输:
- 把目标板的日志 / 调试信息,低开销、实时传到 PC;
- 支持双向传输(PC 也能发指令给目标板);
- 无需占用 UART/SPI 等外设资源。
解决 “日志本身的管理”:
- 日志生成(分级:Debug/Info/Warn/Error);
- 日志过滤(按级别 / 模块关闭不需要的日志);
- 日志格式化(添加时间戳、模块名、级别);
- 支持日志输出到不同 “目的地”(RTT、UART、Flash 等)。
依赖条件 必须配合 J-Link 调试器使用(硬件依赖) 无硬件依赖,只需嵌入式系统(RT-Thread/FreeRTOS/ 裸机等)支持,直接移植代码即可。

实际开发中,两者常搭配使用,形成 “日志管理→高效传输” 的完整链路:

  1. 目标板上,通过EasyLogger生成规范化日志(比如带时间戳的 Error 级日志);

  2. 不直接用 UART 传输(速度慢、占外设),而是调用 RTT 的 API,将 EasyLogger 生成的日志数据,通过RTT传输到 PC;

  3. PC 端用 “J-Link RTT Viewer” 工具接收数据,实时查看 EasyLogger 格式化后的日志。

简单总结:

EasyLogger 负责 “把日志做规范、做灵活”RTT 负责 “把规范的日志快速传到 PC”,二者结合能兼顾日志的 “易用性” 和 “传输效率”,是嵌入式开发中很常见的日志方案。

结合使用操作步骤:

首先把RTT和Easylogger的相关文件移植进来,路径记得添加

重写elog_port.c文件,先包含头文件#include "SEGGER_RTT.h",编写初始化、输出、获取时间等函数

输出时间戳:

/**
 * get current time interface
 *
 * @return current time
 */
const char *elog_port_get_time(void) {
    
    /* add your code here */
    static char time_str[20] = { 0 };
    // get time
    snprintf(time_str, sizeof(time_str), "%d", HAL_GetTick());
    return time_str;
}

main函数中包含elog.h头文件,调用elog的初始化函数,调用api就能在rtt的viewner上看见打印信息

错误解决:

问题根源分析

(1) 现象描述

启用这两个宏后日志不显示:说明日志被缓存到内存(异步队列或缓冲区),但未触发实际输出到 RTT。

禁用后日志正常:EasyLogger 退化为同步直接输出模式,每条日志立即调用 elog_port_output 写入 RTT。

(2) 具体原因

  1. 未初始化异步任务:

若启用了 ELOG_ASYNC_OUTPUT_ENABLE,必须调用 elog_async_start() 启动后台任务,否则日志堆积在队列中无法处理。

  1. 未刷新缓冲区:

若启用了 ELOG_BUF_OUTPUT_ENABLE,需在适当位置调用 elog_flush() 强制刷新缓冲区,否则日志可能驻留内存未输出。

  1. RTT 输出函数未适配异步/缓冲模式:

在异步或缓冲模式下,EasyLogger 的输出最终仍需要通过 elog_port_output 写入 RTT。若该函数未正确处理缓冲数据(如未传递正确指针或长度),会导致 RTT 接收错误。

在启用 ELOG_ASYNC_OUTPUT_ENABLE 时,确保在 elog_init() 后启动异步任务:

void main() {
    elog_init();
    elog_async_start(); // 必须调用!启动异步任务处理日志队列
    // ... 其他初始化
}

检查缓冲区刷新

如果启用了 ELOG_BUF_OUTPUT_ENABLE,在需要立即输出日志时手动刷新:

log_i("tag", "message");
elog_flush(); // 强制刷新缓冲区到输出设备

FreeRTOS 下的elog“锁”实现:(同/异步都一样)

修改 elog_port.c 文件

通过使用 FreeRTOS 的互斥锁来保护输出函数:

/*
 * This file is part of the EasyLogger Library.
 *
 * Copyright (c) 2015, Armink, <armink.ztl@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * 'Software'), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Function: Portable interface for each platform.
 * Created on: 2015-04-28
 */
 
#include <elog.h>
#include "stdio.h"
#include "SEGGER_RTT.h"

 /* FreeRTOS add begin */
#include "FreeRTOS.h"
#include "semphr.h"
#include "stm32f4xx_hal.h"

//#define OutputSemaphore       // open lock or not

#ifdef OutputSemaphore
/* FreeRTOS 互斥锁用于保护输出 */
static SemaphoreHandle_t xOutputMutex = NULL;
/* FreeRTOS add end */
#endif

 /**
 * EasyLogger port initialize
 *
 * @return result
 */
ElogErrCode elog_port_init(void) {
    ElogErrCode result = ELOG_NO_ERR;

    /* add your code here */
#ifdef OutputSemaphore
    /* create xOutputMutex to protect output */
    xOutputMutex = xSemaphoreCreateMutex();
    if (xOutputMutex == NULL) {
        /* xOutputMutex create failed */
        result = ELOG_NO_ERR;
    }
#endif
    return result;
}

/**
 * EasyLogger port deinitialize
 *
 */
void elog_port_deinit(void) {

    /* add your code here */
#ifdef OutputSemaphore
    /* delete xOutputMutex */
    if (xOutputMutex != NULL) {
        vSemaphoreDelete(xOutputMutex);
        xOutputMutex = NULL;
    }
#endif
}

/**
 * output log port interface
 *
 * @param log output of log
 * @param size log size
 */
void elog_port_output(const char *log, size_t size) {
    
    /* add your code here */
    // SEGGER_RTT_Write(0,log,size);
    printf("%.*s",size,log);
}

/**
 * output lock
 */
void elog_port_output_lock(void) {
    
    /* add your code here */
#ifdef OutputSemaphore
    if (xOutputMutex != NULL) {
        /* wait xOutputMutex forever */
        xSemaphoreTake(xOutputMutex, portMAX_DELAY);
    }
#endif
}

/**
 * output unlock
 */
void elog_port_output_unlock(void) {
    
    /* add your code here */
#ifdef OutputSemaphore
    if (xOutputMutex != NULL) {
        /* release xOutputMutex */
        xSemaphoreGive(xOutputMutex);
    }
#endif
}

/**
 * get current time interface
 *
 * @return current time
 */
const char *elog_port_get_time(void) {
    
    /* add your code here */
    static char time_str[20] = { 0 };
    // get time
    snprintf(time_str, sizeof(time_str), "%d", HAL_GetTick());
    return time_str;
}

/**
 * get current process name interface
 *
 * @return current process name
 */
const char *elog_port_get_p_info(void) {
    
    /* add your code here */
    return "";
}

/**
 * get current thread name interface
 *
 * @return current thread name
 */
const char *elog_port_get_t_info(void) {
    
    /* add your code here */
    return "";
}

FreeRTOS下实现异步输出日志

下面文件中修改与添加的代码部分都加了注释/* FreeRTOS add */ 或者 /* FreeRTOS modify */

elog_cfg.h

在 elog_cfg.h 中打开异步输出使能,关闭 pthread 支持。

/* enable asynchronous output mode */
#define ELOG_ASYNC_OUTPUT_ENABLE
/* the highest output level for async mode, other level will sync output */
#define ELOG_ASYNC_OUTPUT_LVL                    ELOG_LVL_ASSERT
/* buffer size for asynchronous output mode */
#define ELOG_ASYNC_OUTPUT_BUF_SIZE               (ELOG_LINE_BUF_SIZE * 10)
/* each asynchronous output's log which must end with newline sign */
#define ELOG_ASYNC_LINE_OUTPUT
/* 注释掉或删除 pthread 支持 */
// #define ELOG_ASYNC_OUTPUT_USING_PTHREAD

elog_async.c

/*
 * This file is part of the EasyLogger Library.
 *
 * Copyright (c) 2016-2017, Armink, <armink.ztl@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * 'Software'), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Function: Logs asynchronous output.
 * Created on: 2016-11-06
 */

#include <elog.h>
#include <string.h>
/* FreeRTOS add begin*/
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "queue.h"
/* FreeRTOS add end*/

#ifdef ELOG_ASYNC_OUTPUT_ENABLE

#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREAD
#include <pthread.h>
#include <sched.h>
#include <semaphore.h>
/* thread default stack size */
#ifndef ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE
#if PTHREAD_STACK_MIN > 4*1024
#define ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE     PTHREAD_STACK_MIN
#else
#define ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE     (1*1024)
#endif
/* thread default priority */
#ifndef ELOG_ASYNC_OUTPUT_PTHREAD_PRIORITY
#define ELOG_ASYNC_OUTPUT_PTHREAD_PRIORITY       (sched_get_priority_max(SCHED_RR) - 1)
#endif
/* output thread poll get log buffer size  */
#ifndef ELOG_ASYNC_LINE_OUTPUT
#ifndef ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE
#define ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE         (ELOG_ASYNC_OUTPUT_BUF_SIZE - 4)
#endif
#else
#ifndef ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE
#define ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE         (ELOG_LINE_BUF_SIZE - 4)
#endif
#endif
#endif /* ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE */

/* asynchronous output log notice */
static sem_t output_notice;
/* asynchronous output pthread thread */
static pthread_t async_output_thread;
#endif /* ELOG_ASYNC_OUTPUT_USING_PTHREAD */

/* the highest output level for async mode, other level will sync output */
#ifdef ELOG_ASYNC_OUTPUT_LVL
#define OUTPUT_LVL                               ELOG_ASYNC_OUTPUT_LVL
#else
#define OUTPUT_LVL                               ELOG_LVL_ASSERT
#endif /* ELOG_ASYNC_OUTPUT_LVL */

/* buffer size for asynchronous output mode */
#ifdef ELOG_ASYNC_OUTPUT_BUF_SIZE
#define OUTPUT_BUF_SIZE                          ELOG_ASYNC_OUTPUT_BUF_SIZE
#else
#define OUTPUT_BUF_SIZE                          (ELOG_LINE_BUF_SIZE * 10)
#endif /* ELOG_ASYNC_OUTPUT_BUF_SIZE */

/* Initialize OK flag */
static bool init_ok = false;
#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREAD
/* thread running flag */
static bool thread_running = false;
#endif
/* FreeRTOS add begin */
/* FreeRTOS task running flag */
static bool task_running = false;
/* FreeRTOS add end */
/* asynchronous output mode enabled flag */
static bool is_enabled = false;
/* asynchronous output mode's ring buffer */
static char log_buf[OUTPUT_BUF_SIZE] = { 0 };
/* log ring buffer write index */
static size_t write_index = 0;
/* log ring buffer read index */
static size_t read_index = 0;
/* log ring buffer full flag */
static bool buf_is_full = false;
/* log ring buffer empty flag */
static bool buf_is_empty = true;

/* FreeRTOS add begin*/
/* FreeRTOS task handle and synchronization objects */
static TaskHandle_t async_output_task_handle = NULL;
static SemaphoreHandle_t output_notice_sem = NULL;
static SemaphoreHandle_t async_lock_mutex = NULL;
/* FreeRTOS add end*/

extern void elog_port_output(const char *log, size_t size);
extern void elog_output_lock(void);
extern void elog_output_unlock(void);

/**
 * asynchronous output ring buffer used size
 *
 * @return used size
 */
static size_t elog_async_get_buf_used(void) {
    if (write_index > read_index) {
        return write_index - read_index;
    } else {
        if (!buf_is_full && !buf_is_empty) {
            return OUTPUT_BUF_SIZE - (read_index - write_index);
        } else if (buf_is_full) {
            return OUTPUT_BUF_SIZE;
        } else {
            return 0;
        }
    }
}

/**
 * asynchronous output ring buffer remain space
 *
 * @return remain space
 */
static size_t async_get_buf_space(void) {
    return OUTPUT_BUF_SIZE - elog_async_get_buf_used();
}

/**
 * put log to asynchronous output ring buffer
 *
 * @param log put log buffer
 * @param size log size
 *
 * @return put log size, the log which beyond ring buffer space will be dropped
 */
static size_t async_put_log(const char *log, size_t size) {
    size_t space = 0;

    space = async_get_buf_space();
    /* no space */
    if (!space) {
        size = 0;
        goto __exit;
    }
    /* drop some log */
    if (space <= size) {
        size = space;
        buf_is_full = true;
    }

    if (write_index + size < OUTPUT_BUF_SIZE) {
        memcpy(log_buf + write_index, log, size);
        write_index += size;
    } else {
        memcpy(log_buf + write_index, log, OUTPUT_BUF_SIZE - write_index);
        memcpy(log_buf, log + OUTPUT_BUF_SIZE - write_index,
                size - (OUTPUT_BUF_SIZE - write_index));
        write_index += size - OUTPUT_BUF_SIZE;
    }

    buf_is_empty = false;

__exit:

    return size;
}

#ifdef ELOG_ASYNC_LINE_OUTPUT
/**
 * Get line log from asynchronous output ring buffer.
 * It will copy all log when the newline sign isn't find.
 *
 * @param log get line log buffer
 * @param size line log size
 *
 * @return get line log size, the log size is less than ring buffer used size
 */
size_t elog_async_get_line_log(char *log, size_t size) {
    size_t used = 0, cpy_log_size = 0;
    /* lock output */
    /* FreeRTOS modify */
    // elog_output_lock();
    if (async_lock_mutex != NULL) {
        xSemaphoreTake(async_lock_mutex, portMAX_DELAY);
    }
    /* FreeRTOS modify */
    used = elog_async_get_buf_used();

    /* no log */
    if (!used || !size) {
        goto __exit;
    }
    /* less log */
    if (used <= size) {
        size = used;
    }

    if (read_index + size < OUTPUT_BUF_SIZE) {
        cpy_log_size = elog_cpyln(log, log_buf + read_index, size);
        read_index += cpy_log_size;
    } else {
        cpy_log_size = elog_cpyln(log, log_buf + read_index, OUTPUT_BUF_SIZE - read_index);
        if (cpy_log_size == OUTPUT_BUF_SIZE - read_index) {
            cpy_log_size += elog_cpyln(log + cpy_log_size, log_buf, size - cpy_log_size);
            read_index += cpy_log_size - OUTPUT_BUF_SIZE;
        } else {
            read_index += cpy_log_size;
        }
    }

    if (used == cpy_log_size) {
        buf_is_empty = true;
    }

    if (cpy_log_size) {
        buf_is_full = false;
    }

__exit:
    /* lock output */
    /* FreeRTOS modify */
    // elog_output_unlock();
    if (async_lock_mutex != NULL) {
        xSemaphoreGive(async_lock_mutex);
    }
    /* FreeRTOS modify */
    return cpy_log_size;
}
#else
/**
 * get log from asynchronous output ring buffer
 *
 * @param log get log buffer
 * @param size log size
 *
 * @return get log size, the log size is less than ring buffer used size
 */
size_t elog_async_get_log(char *log, size_t size) {
    size_t used = 0;
    /* lock output */
    /* FreeRTOS modify */
    // elog_output_lock();
    if (async_lock_mutex != NULL) {
        xSemaphoreTake(async_lock_mutex, portMAX_DELAY);
    }
    /* FreeRTOS modify */
    used = elog_async_get_buf_used();
    /* no log */
    if (!used || !size) {
        size = 0;
        goto __exit;
    }
    /* less log */
    if (used <= size) {
        size = used;
        buf_is_empty = true;
    }

    if (read_index + size < OUTPUT_BUF_SIZE) {
        memcpy(log, log_buf + read_index, size);
        read_index += size;
    } else {
        memcpy(log, log_buf + read_index, OUTPUT_BUF_SIZE - read_index);
        memcpy(log + OUTPUT_BUF_SIZE - read_index, log_buf,
                size - (OUTPUT_BUF_SIZE - read_index));
        read_index += size - OUTPUT_BUF_SIZE;
    }

    buf_is_full = false;

__exit:
    /* lock output */
    /* FreeRTOS modify */
    // elog_output_unlock();
    if (async_lock_mutex != NULL) {
        xSemaphoreGive(async_lock_mutex);
    }
    /* FreeRTOS modify */
    return size;
}
#endif /* ELOG_ASYNC_LINE_OUTPUT */

void elog_async_output(uint8_t level, const char *log, size_t size) {
    /* this function must be implement by user when ELOG_ASYNC_OUTPUT_USING_PTHREAD is not defined */
    // extern void elog_async_output_notice(void);
    size_t put_size;

    if (is_enabled) {
        if (level >= OUTPUT_LVL) {
            put_size = async_put_log(log, size);
            /* notify output log thread */
            /* FreeRTOS modify */
            // if (put_size > 0) {
            //     elog_async_output_notice();
            // }
            if (put_size > 0 && output_notice_sem != NULL) {
                xSemaphoreGive(output_notice_sem);
            }
            /* FreeRTOS modify */
        }
        else {
            elog_port_output(log, size);
        }
    } else {
        elog_port_output(log, size);
    }
}

/**
 * FreeRTOS 异步输出任务
 */
static void async_output_task(void* arg) {
    size_t get_log_size = 0;
    static char poll_get_buf[ELOG_LINE_BUF_SIZE];

    while (task_running) {
        /* waiting log */
        if (xSemaphoreTake(output_notice_sem, portMAX_DELAY) == pdTRUE) {
            /* polling gets and outputs the log */
            while (true) {
#ifdef ELOG_ASYNC_LINE_OUTPUT
                get_log_size = elog_async_get_line_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#else
                get_log_size = elog_async_get_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#endif

                if (get_log_size) {
                    elog_port_output(poll_get_buf, get_log_size);
                }
                else {
                    break;
                }
            }
        }
    }

    /* 删除自己 */
    vTaskDelete(NULL);
}

#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREAD
void elog_async_output_notice(void) {
    sem_post(&output_notice);
}

static void *async_output(void *arg) {
    size_t get_log_size = 0;
    static char poll_get_buf[ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE];

    while(thread_running) {
        /* waiting log */
        sem_wait(&output_notice);
        /* polling gets and outputs the log */
        while(true) {

#ifdef ELOG_ASYNC_LINE_OUTPUT
            get_log_size = elog_async_get_line_log(poll_get_buf, ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE);
#else
            get_log_size = elog_async_get_log(poll_get_buf, ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE);
#endif

            if (get_log_size) {
                elog_port_output(poll_get_buf, get_log_size);
            } else {
                break;
            }
        }
    }
    return NULL;
}
#endif

/**
 * enable or disable asynchronous output mode
 * the log will be output directly when mode is disabled
 *
 * @param enabled true: enabled, false: disabled
 */
void elog_async_enabled(bool enabled) {
    is_enabled = enabled;
}

/**
 * asynchronous output mode initialize
 *
 * @return result
 */
ElogErrCode elog_async_init(void) {
    ElogErrCode result = ELOG_NO_ERR;

    if (init_ok) {
        return result;
    }

#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREAD
    pthread_attr_t thread_attr;
    struct sched_param thread_sched_param;

    sem_init(&output_notice, 0, 0);

    thread_running = true;

    pthread_attr_init(&thread_attr);
    //pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
    pthread_attr_setstacksize(&thread_attr, ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE);
    pthread_attr_setschedpolicy(&thread_attr, SCHED_RR);
    thread_sched_param.sched_priority = ELOG_ASYNC_OUTPUT_PTHREAD_PRIORITY;
    pthread_attr_setschedparam(&thread_attr, &thread_sched_param);
    pthread_create(&async_output_thread, &thread_attr, async_output, NULL);
    pthread_attr_destroy(&thread_attr);
#endif

    /* FreeRTOS add begin */
    /* 创建信号量 */
    output_notice_sem = xSemaphoreCreateCounting(10, 0);
    if (output_notice_sem == NULL) {
        return ELOG_NO_ERR;
    }

    /* 创建互斥锁 */
    async_lock_mutex = xSemaphoreCreateMutex();
    if (async_lock_mutex == NULL) {
        vSemaphoreDelete(output_notice_sem);
        return ELOG_NO_ERR;
    }

    task_running = true;

    /* 创建异步输出任务 */
    if (xTaskCreate(async_output_task, "elog_async", configMINIMAL_STACK_SIZE + 512,
        NULL, tskIDLE_PRIORITY + 2, &async_output_task_handle) != pdPASS) {
        vSemaphoreDelete(output_notice_sem);
        vSemaphoreDelete(async_lock_mutex);
        task_running = false;
        return ELOG_NO_ERR;
    }
    /* FreeRTOS add end */

    init_ok = true;

    return result;
}

/**
 * asynchronous output mode deinitialize
 *
 */
void elog_async_deinit(void) {
    if (!init_ok) {
        return ;
    }

#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREAD
    thread_running = false;

    elog_async_output_notice();

    pthread_join(async_output_thread, NULL);
    
    sem_destroy(&output_notice);
#endif

    /* FreeRTOS add begin */
    task_running = false;

    /* 通知任务退出 */
    if (output_notice_sem != NULL) {
        xSemaphoreGive(output_notice_sem);
    }

    /* 等待任务结束 */
    if (async_output_task_handle != NULL) {
        vTaskDelete(async_output_task_handle);
        async_output_task_handle = NULL;
    }

    /* 删除信号量和互斥锁 */
    if (output_notice_sem != NULL) {
        vSemaphoreDelete(output_notice_sem);
        output_notice_sem = NULL;
    }

    if (async_lock_mutex != NULL) {
        vSemaphoreDelete(async_lock_mutex);
        async_lock_mutex = NULL;
    }
    /* FreeRTOS add end */

    init_ok = false;
}

#endif /* ELOG_ASYNC_OUTPUT_ENABLE */

elog_port.c

/*
 * This file is part of the EasyLogger Library.
 *
 * Copyright (c) 2015, Armink, <armink.ztl@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * 'Software'), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Function: Portable interface for each platform.
 * Created on: 2015-04-28
 */
 
#include <elog.h>
#include "stdio.h"
#include "SEGGER_RTT.h"
/* FreeRTOS add begin */
#include "FreeRTOS.h"
#include "semphr.h"
#include "stm32f4xx_hal.h"

//#define OutputSemaphore       // open lock or not

#ifdef OutputSemaphore
/* FreeRTOS 互斥锁用于输出同步 */
static SemaphoreHandle_t output_mutex = NULL;
#endif
/* FreeRTOS add end */

  /**
 * EasyLogger port initialize
 *
 * @return result
 */
ElogErrCode elog_port_init(void) {
    ElogErrCode result = ELOG_NO_ERR;

    /* add your code here */
#ifdef OutputSemaphore
    /* 创建互斥锁用于输出同步 */
    output_mutex = xSemaphoreCreateMutex();
    if (output_mutex == NULL) {
        return ELOG_NO_ERR;
    }
#endif
    return result;
}

/**
 * EasyLogger port deinitialize
 *
 */
void elog_port_deinit(void) {

    /* add your code here */
#ifdef OutputSemaphore
    /* 删除互斥锁 */
    if (output_mutex != NULL) {
        vSemaphoreDelete(output_mutex);
        output_mutex = NULL;
    }
#endif
}

/**
 * output log port interface
 *
 * @param log output of log
 * @param size log size
 */
void elog_port_output(const char *log, size_t size) {
    
    /* add your code here */
    SEGGER_RTT_Write(0,log,size);
    // printf("%.*s",size,log);
}

/**
 * output lock
 */
void elog_port_output_lock(void) {
    
    /* add your code here */
#ifdef OutputSemaphore
    /* 获取互斥锁 */
    if (output_mutex != NULL) {
        xSemaphoreTake(output_mutex, portMAX_DELAY);
    }
#endif
}

/**
 * output unlock
 */
void elog_port_output_unlock(void) {
    
    /* add your code here */
#ifdef OutputSemaphore
    /* 释放互斥锁 */
    if (output_mutex != NULL) {
        xSemaphoreGive(output_mutex);
    }
#endif
}

/**
 * get current time interface
 *
 * @return current time
 */
const char *elog_port_get_time(void) {
    
    /* add your code here */
    static char time_str[20] = { 0 };
    // get time
    snprintf(time_str, sizeof(time_str), "%d", HAL_GetTick());
    return time_str;
}

/**
 * get current process name interface
 *
 * @return current process name
 */
const char *elog_port_get_p_info(void) {
    
    /* add your code here */
    return "";
}

/**
 * get current thread name interface
 *
 * @return current thread name
 */
const char *elog_port_get_t_info(void) {
    
    /* add your code here */
    /* 返回当前任务名称 */
    TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
    if (current_task != NULL) {
        return pcTaskGetName(current_task);
    }
    return "unknown";
}

main.c 初始化

// ... 其他代码 ...

/* USER CODE BEGIN PFP */
// 重定向printf
#ifdef __GNUC__
     #define PUTCHAR_PROTOTYPE int _io_putchar(int ch)
 #else
     #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
 #endif /* __GNUC__*/
 
 /******************************************************************
     *@brief  Retargets the C library printf  function to the USART.
     *@param  None
     *@retval None
 ******************************************************************/
 PUTCHAR_PROTOTYPE
 {
     // HAL_UART_Transmit(&huart1, (uint8_t *)&ch,1,0xFFFF);
         SEGGER_RTT_PutChar(0,ch);
     return ch;
 }

 
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void app_elog_init(void)
{
        elog_init();
        
        elog_set_fmt(ELOG_LVL_ASSERT,ELOG_FMT_TIME | ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_DIR | ELOG_FMT_LINE | ELOG_FMT_FUNC);
        elog_set_fmt(ELOG_LVL_DEBUG,ELOG_FMT_TIME | ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_DIR | ELOG_FMT_LINE | ELOG_FMT_FUNC);
        elog_set_fmt(ELOG_LVL_ERROR,ELOG_FMT_TIME | ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_DIR | ELOG_FMT_LINE | ELOG_FMT_FUNC);
        elog_set_fmt(ELOG_LVL_INFO,ELOG_FMT_TIME | ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_DIR | ELOG_FMT_LINE | ELOG_FMT_FUNC);
        elog_set_fmt(ELOG_LVL_WARN,ELOG_FMT_TIME | ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_DIR | ELOG_FMT_LINE | ELOG_FMT_FUNC);
        
        elog_start();
}

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  // ... 硬件初始化代码 ...
  
  /* 初始化 FreeRTOS */
  // ...
  
  /* 初始化 EasyLogger */
  app_elog_init();
  /* 初始化 RTT */
  SEGGER_RTT_Init();
  
  /* 创建任务线程 */
  // ...
  /* 启动调度器 */
  // ...
  // ... 其他代码 ...
}

异步输出问题:无法一次性连续发送多条(>11条)日志

问题说明

目前这种异步输出实现不能一次性连续发送多条(>11条)日志,如果连续发送多条,就会出现丢日志的情况:

目前可以通过穿插 osDelay(3) 在第十一条和第十二条之间来解决这个问题:

ds 的解决方法:

  1. 任务优先级问题

异步输出任务的优先级可能低于默认任务,导致异步任务无法及时处理日志。

  1. 信号量竞争

在异步模式下,每次写入日志后会发送信号量通知异步任务。如果日志生成速度过快,可能会导致信号量多次发送,但异步任务只能处理一次。

  1. 输出速度瓶颈

如果输出接口(如串口)速度较慢,而日志生成速度快,会导致积压。

1. 提高异步输出任务优先级

elog_async.c 中提高异步任务的优先级:

/* 创建异步输出任务 */
if (xTaskCreate(async_output_task, "elog_async", configMINIMAL_STACK_SIZE + 1024, 
                NULL, tskIDLE_PRIORITY + 4, &async_output_task_handle) != pdPASS) {  // 优先级从+2提高到+4
    vSemaphoreDelete(output_notice_sem);
    vSemaphoreDelete(async_lock_mutex);
    task_running = false;
    return ELOG_NO_MEM;
}
2. 优化异步任务处理逻辑

修改 elog_async.c 中的异步任务处理逻辑,确保它能处理所有积压的日志:

/**
 * FreeRTOS 异步输出任务
 */
static void async_output_task(void *arg) {
    size_t get_log_size = 0;
    static char poll_get_buf[ELOG_LINE_BUF_SIZE];
    TickType_t last_wake_time = xTaskGetTickCount();
    const TickType_t frequency = pdMS_TO_TICKS(10);  // 每10ms检查一次

    while(task_running) {
        /* 定期检查,确保不会错过信号量 */
        if (xSemaphoreTake(output_notice_sem, frequency) == pdTRUE) {
            /* 处理所有积压的日志 */
            do {
#ifdef ELOG_ASYNC_LINE_OUTPUT
                get_log_size = elog_async_get_line_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#else
                get_log_size = elog_async_get_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#endif

                if (get_log_size) {
                    elog_port_output(poll_get_buf, get_log_size);
                }
            } while (get_log_size > 0);
        } else {
            /* 即使没有信号量,也检查一次是否有积压的日志 */
#ifdef ELOG_ASYNC_LINE_OUTPUT
            get_log_size = elog_async_get_line_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#else
            get_log_size = elog_async_get_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#endif
            
            if (get_log_size) {
                elog_port_output(poll_get_buf, get_log_size);
            }
        }
        
        /* 让出CPU给其他任务 */
        vTaskDelayUntil(&last_wake_time, frequency);
    }
    
    /* 删除自己 */
    vTaskDelete(NULL);
}
3. 优化默认任务的日志输出

在默认任务中,减少一次性输出的日志数量,或增加延迟:

void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN 5 */
    osDelay(500);
    log_d("DefaultTask is alive\r\n");

    key_press_status_t key_result = KEY_NOT_PRESSED;
    for (;;)
    {
        log_d("APP task is alive");
        
        // 分批输出日志,避免一次性输出太多
        for(int i = 1; i <= 5; i++) {
            log_a("hello%d", i);
            log_i("hello%d", i+5);
            log_w("hello%d", i+10);
            log_e("hello%d", i+15);
            log_d("hello%d", i+20);
            osDelay(1);  // 每次输出后稍微延迟
        }

        // ... 其他代码 ...
        
        osDelay(100);
    }
  /* USER CODE END 5 */
}
4. 添加缓冲区监控功能

elog_async.c 中添加监控功能,以便调试缓冲区使用情况:

// 添加一个函数来获取缓冲区使用情况
size_t elog_async_get_buf_usage(void) {
    return elog_async_get_buf_used();
}

size_t elog_async_get_buf_capacity(void) {
    return OUTPUT_BUF_SIZE;
}

// 在异步任务中添加调试输出
static void async_output_task(void *arg) {
    size_t get_log_size = 0;
    static char poll_get_buf[ELOG_LINE_BUF_SIZE];
    TickType_t last_wake_time = xTaskGetTickCount();
    const TickType_t frequency = pdMS_TO_TICKS(10);
    uint32_t loop_count = 0;

    while(task_running) {
        /* 定期检查,确保不会错过信号量 */
        if (xSemaphoreTake(output_notice_sem, frequency) == pdTRUE) {
            /* 处理所有积压的日志 */
            do {
#ifdef ELOG_ASYNC_LINE_OUTPUT
                get_log_size = elog_async_get_line_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#else
                get_log_size = elog_async_get_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#endif

                if (get_log_size) {
                    elog_port_output(poll_get_buf, get_log_size);
                }
            } while (get_log_size > 0);
        } else {
            /* 即使没有信号量,也检查一次是否有积压的日志 */
#ifdef ELOG_ASYNC_LINE_OUTPUT
            get_log_size = elog_async_get_line_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#else
            get_log_size = elog_async_get_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#endif
            
            if (get_log_size) {
                elog_port_output(poll_get_buf, get_log_size);
            }
        }
        
        // 每100次循环输出一次缓冲区使用情况
        if (++loop_count % 100 == 0) {
            size_t used = elog_async_get_buf_usage();
            size_t total = elog_async_get_buf_capacity();
            log_d("Async buffer: %d/%d bytes (%.1f%%)", used, total, (float)used/total*100);
        }
        
        /* 让出CPU给其他任务 */
        vTaskDelayUntil(&last_wake_time, frequency);
    }
    
    /* 删除自己 */
    vTaskDelete(NULL);
}
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐