BES 平台app_trace在线调试的使用

wuchangjian2021-11-04 16:46:51编程学习

简介

在项目的前期开发中,最耗时间的当属调试工作,然而大多平台的调试都采用最传统的方法:查看日志-修改代码-编译-烧录-查看日志。但是这个过程是非常耗时,因为可能你只需要改一个值就需要完整的编译整个工程,当然小改动后编译时间也会对应缩短,但考虑还需要烧录等操作,时间的成本还是很高的。

对于这个问题bes平台开发了app_trace在线调试功能,开发者可以通过串口输入指定的命令来完成开发者想执行的功能,这一定程度上减少了调试的时间成本,提高了效率。但本文的目的不在于app_trace的常规使用方法,而是本人根据使用app_trace调试遇到的问题来对app_trace功能的优化。

1. 如何使用app_trace

  1. 编译时添加宏APP_TRACE_RX_ENABLE=1使能app_trace的功能
  2. 注册测试命令的回调函数,调用app_trace_rx_register("xxx_test", xxx_test_callback)接口注册测试用的指令xxx_test,以及响应指令的回调函数xxx_test_callback,该API在路径apps\common\app_trace_rx.c中,当然你可以在这个源文件里调用这个接口来注册你测试用的回调函数,回调函数也需要写在这个源文件中。但对于我们的模块开发调试这是极其不方便的,建议在自己的模块中调用这个API来注册测试命令的回调函数,只需要添加对应的头文件即可,同时因为app_trace是在app_init里最早初始化的,所以只要保证你的模块在其后初始化即可。注册测试命令的回调函数示例如下,可根据测试指令所传的参数在回调函数中做相应的处理,测试指令的格式为[xxx_test,parameters1,parameters2,...],如果你不需要传参的话可直接发送[xxx_test,]
    void xxx_module_open(void)
    {
        ...
        app_trace_rx_register("xxx_test", xxx_test_callback);
    }

    static unsigned int xxx_test_callback(unsigned char *buf, uint32_t len)
    {

        TRACE(2,"[%s] len = %d", __func__, len);
        TRACE(2,"[%s] %s", __func__, buf);

        // Do something based on the parameters passed in

        return 0;
    }

2. 使用app_trace遇到的问题

问题背景:我在调试硬件i2c的驱动时,注册了trace的回调函数来读写寄存器的值,在开机初始化的过程中初始化i2c的从器件,这时i2c读写均没有问题,但是机器跑起来后在串口输入测试命令来读写寄存器总是失败,提示超时!!!

查找app_trace的实现原理发现,测试指令的回调函数是在串口中断的回调函数里调用的,所以上述我注册的回调函数实际上是中断触发的,那么大家就应该知道了在中断里做长时处理的后果了(i2c和spi都属于长时操作)

    static void hal_trace_rx_irq_handler(uint32_t xfer_size, int dma_error, union HAL_UART_IRQ_T status)
    {
        int res;

        if (trace_rx_cb == NULL) {
            return;
        }

        if (xfer_size) {
            res = trace_rx_cb(trace_rx_buf, xfer_size);
            TRACE(0, "%s: trace_rx_cb (%p) prase data error: %d", __func__, trace_rx_cb, res);
        }
        ...
    }

    int hal_trace_rx_open(unsigned char *buf, unsigned int len, HAL_TRACE_RX_CALLBACK_T rx_callback)
    {
        if (buf == NULL || len == 0 || rx_callback == NULL) {
            return 1;
        }

        if (!hal_trace_is_uart_transport(trace_transport)) {
            return 2;
        }

        ...

        trace_rx_buf = buf;
        trace_rx_len = len;
        
        trace_rx_cb = rx_callback;

        hal_uart_irq_set_dma_handler(trace_uart, hal_trace_rx_irq_handler, NULL);

        if (trace_rx_state != HAL_TRACE_RX_STATE_OPENED) {
            trace_rx_state = HAL_TRACE_RX_STATE_OPENED;
            hal_trace_rx_start();
        }

        return 0;
    }

3. 解决方案

为了避免中断对长时操作的影响,我对app_trace进行了深一层的延展,实现逻辑如下图所示。


根据指令回调函数中收到的串口参数填充结构体APP_MESSAGE_BLOCKmod_idmsg_body,调用app_mailbox_put接口将此结构体发送给app mailbox,app mailbox会根据调用接口app_set_threadhandle(mod_id, msg_process_callback)注册模块消息处理函数所传入mod_id来匹配app_mailbox_put所发送的mod_id,如果一致的话就将消息转发给注册的消息处理函数。因此在我们模块的初始化过程中,应调用接口app_set_threadhandle(APP_MODUAL_XXX, app_trace_cmd_process)来注册接收APP_MODUAL_XXX模块消息的处理函数app_trace_cmd_process,这样在调用app_mailbox_put发送APP_MODUAL_XXX模块的消息就可以在app_trace_cmd_process函数中解析参数,并作相应的处理了

    typedef struct {
        uint32_t message_id;
        uint32_t message_ptr;
        uint32_t message_Param0;
        uint32_t message_Param1;
        uint32_t message_Param2;
    } APP_MESSAGE_BODY;

    typedef struct {
        uint32_t src_thread;
        uint32_t dest_thread;
        uint32_t system_time;
        uint32_t mod_id;
        APP_MESSAGE_BODY msg_body;
    } APP_MESSAGE_BLOCK;

4. 总结

将测试指令回调函数中接收到的串口命令转发到mailbox,转发完参数之后回调函数立即返回,消息流入mailbox并转发到调用接口app_set_threadhandle注册的回调函数中来处理,这样就可以将实时信息转变成异步处理了,规避了在中断回调函数中处理事物所带来的问题。

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。