轮腿平衡步兵总结 - 调试工具篇


轮腿平衡步兵总结 - 调试工具篇

若需要明白如何部署 J-link RTT Viewer 或者如何使用,请查阅该文章STM32 实现 SEGGER RTT 打印

在平衡步兵代码中,可以通过定义宏定义 DEBUG_MODE 来使能 RTT Viewer 功能。

通过宏定义的封装,可以实现日志形式的输出。

// RTT
#define PRINTF(str, ...)       \
    SEGGER_RTT_SetTerminal(0); \
    SEGGER_RTT_printf(0, str, ##__VA_ARGS__) // 字符串发送
#define PRINTF_F(str_temp, str, format) \
    sprintf(str_temp, str, format);     \
    SEGGER_RTT_SetTerminal(0);          \
    SEGGER_RTT_printf(0, str_temp); // 带浮点数的发送

#define LOG_PROTO(window, type, color, format, ...)     \
    SEGGER_RTT_printf(window, "  %s%s" format "\r\n%s", \
                      color,                            \
                      type,                             \
                      ##__VA_ARGS__,                    \
                      RTT_CTRL_RESET)

/* 清屏*/
#define LOG_CLEAR() SEGGER_RTT_WriteString(0, "  " RTT_CTRL_CLEAR)

/* 无颜色日志输出 */
#define LOG(format, ...) LOG_PROTO(0, "", "", format, ##__VA_ARGS__)

/* 有颜色格式日志输出 */
#define LOG_INFO(format, ...) LOG_PROTO(0, "[INFO]: ", RTT_CTRL_TEXT_BRIGHT_GREEN, format, ##__VA_ARGS__)
#define LOG_WARNING(format, ...) LOG_PROTO(0, "[WARNING]: ", RTT_CTRL_TEXT_BRIGHT_YELLOW, format, ##__VA_ARGS__)
#define LOG_ERROR(format, ...) LOG_PROTO(0, "[ERROR]: ", RTT_CTRL_TEXT_BRIGHT_RED, format, ##__VA_ARGS__)
#define LOG_FATAL(format, ...) LOG_PROTO(0, "[FATAL]: ", RTT_CTRL_TEXT_BRIGHT_BLUE, format, ##__VA_ARGS__)

Jscope RTT Mode

该模式主要实现高速曲线绘制,可以参考以下博客:利用 J-Scope RTT 模式实现高速(20K)曲线打印

在平衡步兵代码中,可以通过定义宏定义 JSCOPE_RTT_MODE 来使能 Jscope RTT Mode 功能。

FreeRTOS 性能测试模块

该模块可以统计每个任务占用的 CPU 占有率,以及每个任务的堆栈使用情况,可以通过该模块判断哪个任务占用 CPU 时间长从而合理优化其运行时间,通过判断任务堆栈使用剩余量来增加或减少给予该任务的堆栈大小。

参考该博客FreeRTOS 学习笔记(11)——CPU 使用率统计

在平衡步兵代码中,可以通过定义宏定义 DEBUG_MODE_FREERTOS 来使能 FreeRTOS 性能测试模块。

计时器模块

在平衡步兵代码中,采用了 TIM2 作为一个单独的计时器,该计时器主要用来实现精准延时,辅助 debug 使用。

丢帧估计

/**
 * @brief 计算两次调用之间的时间差
 * @param[in] cnt_last 上一次计数值
 */
float GetDeltaT(uint32_t *cnt_last)
{
    volatile uint32_t cnt_now = COUNTER_TIMx->CNT;
    float dt = ((uint32_t)(cnt_now - *cnt_last)) / ((float)(COUNTER_SAMPLING));
    *cnt_last = cnt_now;

    return dt;
}

以上函数能算出两次调用之间的耗时,通过该耗时能够判断帧率是否符合要求以及丢帧帧数判断。例如:

void LossUpdate(Loss_Debugger *loss_debugger, float thresh_t)
{
    loss_debugger->recv_msgs_num++;
    loss_debugger->can_dt = GetDeltaT(&loss_debugger->last_can_cnt);
    if (loss_debugger->can_dt > thresh_t)
    {
        loss_debugger->loss_num++;
    }
}

程序耗时估计

    GetDeltaT(&global_debugger.robot_debugger.last_cnt);
    KF_Wheel_Accel_Update(speed,
                              balance_infantry.INS->MotionAccel_n[1], 0.001);
    global_debugger.robot_debugger.dt = GetDeltaT(&global_debugger.robot_debugger.last_cnt);

精准延时

/**
 * @brief 延时 /us
 * @param[in] void
 */
void Delay(uint32_t Delay)
{
    volatile uint32_t tickstart = COUNTER_TIMx->CNT;
    uint32_t wait = Delay;

    while ((COUNTER_TIMx->CNT - tickstart) < wait)
    {
    }
}

void DelayTo(volatile uint32_t *tickstart, uint32_t Delay)
{
    uint32_t wait = Delay;

    while ((COUNTER_TIMx->CNT - *tickstart) < wait)
    {
    }
}

LED

调试时,可通过 LED 的亮灯情况判断当前机器人的状态。通过以下宏定义封装:

#define LED_R_RCC_AHBxPeriphClockCmd RCC_AHB1PeriphClockCmd
#define LED_R_RCC_AHBxPeriph_GPIOx RCC_AHB1Periph_GPIOC
#define LED_R_GPIOx GPIOC
#define LED_R_GPIO_Pin_x GPIO_Pin_9

#define LED_B_RCC_AHBxPeriphClockCmd RCC_AHB1PeriphClockCmd
#define LED_B_RCC_AHBxPeriph_GPIOx RCC_AHB1Periph_GPIOC
#define LED_B_GPIOx GPIOC
#define LED_B_GPIO_Pin_x GPIO_Pin_8

#define LED_R_ON GPIO_ResetBits(LED_R_GPIOx, LED_R_GPIO_Pin_x)
#define LED_R_OFF GPIO_SetBits(LED_R_GPIOx, LED_R_GPIO_Pin_x)
#define LED_B_ON GPIO_ResetBits(LED_B_GPIOx, LED_B_GPIO_Pin_x)
#define LED_B_OFF GPIO_SetBits(LED_B_GPIOx, LED_B_GPIO_Pin_x)

#define ONLY_LED_B_ON \
    LED_B_ON;         \
    LED_R_OFF

#define ONLY_LED_R_ON \
    LED_R_ON;         \
    LED_B_OFF

#define CLOLOR_LED_ON \
    LED_R_ON;         \
    LED_B_ON

#define ALL_LED_OFF \
    LED_R_OFF;      \
    LED_B_OFF

无线调试器连 SWD 接口

接收端 接收端

只需要电脑端 USB 连接发送端,接收端连接板子 SWD 接口(注意线序),即可正常使用 Keil 下载代码,进入 Debug 模式等等。

蓝牙/WIFI/无线调试器 + VOFA+

参考以下博客Vofa+使用笔记(1):用 Vofa+通过串口连接显示波形

在平衡步兵代码中的 BlueToothTask 中,则是通过以下代码发送需要显示在 VOFA+中的数据。

        // 电容功能测试
        data.fdata[0] = INA260_1.Power / 1000.0f;
        data.fdata[1] = referee_data.Power_Heat_Data.chassis_power;
        data.fdata[2] = buffer_energy.buffering_energy;
        data.fdata[3] = super_power.actual_vol;
        data.fdata[4] = (INA260_1.Power - INA260_2.Power) / 1000.0f;
        data.fdata[5] = balance_infantry.state_vector[STATE_X_V];
        BLUE_TOOTHSendData(&data);

掉线检测任务(Offline_task)

该任务逻辑比较简单,即通过接受传感器帧数来判断是否有传感器掉线,同时该任务也会对电机保护情况进行观测,若有错误则立即反馈异常,交给其它任务处理该异常。

电路测试任务(Test_task)

给电路测试用的任务,可以测试底盘板 CAN1,CAN2,两个 RS485 接口是否有信号。在平衡步兵代码中,可以通过使 TEST_TASK_ON 为 1 使能测试任务。

轮毂电机系统辨识任务(MF9025_IdentifyTask)

辨识轮毂电机转动惯量的任务,可以通过使 MF9025_IDENTIFY_ON 宏定义为 1 使能该任务,具体可以辨识思路可以参考 《轮腿平衡步兵总结》 - 《功能研发》 - 《轮毂电机参数辨识》

看门狗设计

该看门狗只有在所有关键任务都喂狗之后,才算整个系统运行正常而不重启,该目标的实现是通过任务组来实现,即

        xCreatedEventGroup = xEventGroupCreate(); // 创建任务组

        /* 等待所有任务发来事件标志 */
        uxBits = xEventGroupWaitBits(xCreatedEventGroup,           /* 事件标志组句柄 */
                                     TASK_BIT_ALL,                 /* 等待TASK_BIT_ALL被设置 */
                                     pdTRUE,                       /* 退出前TASK_BIT_ALL被清除,这里是TASK_BIT_ALL都被设置才表示“退出”*/
                                     pdTRUE,                       /* 设置为pdTRUE表示等待TASK_BIT_ALL都被设置*/
                                     pdMS_TO_TICKS(xTicksToWait)); /* 等待延迟时间 */
        if ((uxBits & TASK_BIT_ALL) == TASK_BIT_ALL)               // 判断各个任务是否执行
        {
            IWDG_Feed();
        }

喂狗则是通过

 /*  喂狗 */
xEventGroupSetBits(xCreatedEventGroup, CHASIS_CONTROL_BIT); // 标志位置一

这样的话,只要有一个关键任务没有完成喂狗操作,则整个系统都会重启,从而保障了整个系统的稳定性。

Jscope

参考该博客单片机调试工具 J-Scopex 使用说明

Vscode + Ozone 调试

使用 Vscode 代替 Keil 进行代码编辑有以下几个好处(包括但不限于)

  1. Vscode 界面美观

  2. 代码提示,代码可自动缩进对齐

  3. 可通过 Vscode 插件配合提高写代码效率。

详细使用方法可以参考 《轮腿平衡步兵总结》 - 《功能研发》 - 《Ozone+Vscode 嵌入式开发》


文章作者: 闲梦溪
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 闲梦溪 !