高速采样中断、处理函数的实时调试神器——SeggerRTT

目录

一、传统的调试   

二、遇到的问题

三、解决的办法


一、传统的调试   

Long time ago,嵌入式开发的调试是一个JLINK配合一个USB转TTL模块,从JLINK V9后,JLINK自带了一个虚拟串口,可以一边进行着SWD调试,一边使用这个USB转TTL接到板子的debug串口打印上看log,省掉了一个USB转TTL模块,桌子瞬间清爽了不少,接线图如下:

JLINK的配置如下:

    先找到Jlink Commander,然后输入f,查看版本,版本号大于V9.0的支持虚拟串口功能。据说JLINK默认虚拟串口功能是关闭的,需要输入VCOM enable命令后开启,如上图,虚拟串口功能就打开了。要了解其他指令功能,可以输入?

    我手头的俩JLINK买回来就已经开启了,查看有没有开启可以直接通过电脑的设备管理器查看串口号,下图是通过设备管理器查看到的虚拟串口COM4,带有JLink CDC UART Port字样。

    说明开启成功,然后PC端可以通过串口调试助手或者Xshell、Mobaxterm等终端查看打印log了。

二、遇到的问题

    目前的项目平台是STM32F103VET6 + RTthread5.0.0,ADC1采样11路外部模拟电压,采用软件触发;ADC2采样频率是2KHz,通过T2周期为500us update event触发ADC2采样IN6,4路注入通道都采样IN6,4次注入通道采集完成后触发一次注入通道中断,中断回调函数如下图:

void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc) // 4 channel inject channel finish at a time, tigged by T2 overflow
{
    if(hadc==(&hadc2))
    {
        for(uint8_t i = 0; i < ADC_INJECTED_RANK_4; i++) // ADC2_SAMP_NUM
        {
            FIFO[0][i] = HAL_ADCEx_InjectedGetValue(&hadc2, ADC_INJECTED_RANK_1 + i);
        }

        asc2_fifo_cnt++;
        if(asc2_fifo_cnt >= 5)
        {
            asc2_fifo_cnt = 0;
        }

        HAL_GPIO_TogglePin(Curr_CHK_GPIO_Port, Curr_CHK_Pin);

//        RT_ASSERT(RT_NULL != rb_adc2);
//        rt_ringbuffer_put_force(rb_adc2, (rt_uint8_t *)&adc2_leak, sizeof(adc2_leak)); // if overflow, covering previous data

        rt_sem_release(adc2_samp_sem);             // send mailbox to lkgecur_det_task

        __HAL_ADC_ENABLE_IT(&hadc2, ADC_IT_JEOC);  // trig cycle ADC2 samp, HAL lib disable interrupt when a sample finish in
    }
}

    中断的上半部就如上面的中断回调一样,发送一个adc2_samp_sem 信号量,中断的下半部是单独一个线程,等待信号量,然后进行缓变剩余电流和突变剩余电流的计算,均方根、滑动窗口,其中均方根的计算有两处浮点运算,肯定很耗时间,主线程如下图:

    while(1) // debug考虑这个任务的优先级,与SPI通信比较
    {
        // wait for a semphore, inject channel of ADC2
        result = rt_sem_take(adc2_samp_sem, 1000);            // wait for ADC2 4 inject channel complete, time should modify
        if (result != RT_EOK)                                 // debug 这个不至于, 错了再重新来一次,没什么大不了
        {
            LOG_E("take adc2_samp_sem failed!");

//            goto _err_exit;
        }

        static uint8_t cnt = 0;
        uint8_t rec_cnt = asc2_fifo_cnt;

        for(rt_uint8_t i = 0; i < rec_cnt; i++)
        {
            for (rt_uint8_t j = 0; j < ADC2_SAMP_NUM; j++)
            {
                lkgecur_handle(FIFO[i][j]);
            }

            asc2_fifo_cnt--;
        }

        if(0 == cnt++%100);
        {
            LOG_D("rec_cnt = %d", rec_cnt);
        }

    考虑到此线程的优先级不是最高的,当高优先级抢占时采样的数据可能处理不过来,因此开始开了一个4个深度的环形缓冲区,通过串口每100次打印LOG_D(....),发现缓冲区整个都满了,idle task的 LED灯都不闪了,说明整个线程占用了CPU资源,还是处理不过来计算漏电流的任务,导致其他低优先级的任务无法执行。通过这里看那完犊子了,那就需要换主频更高的MCU加上带FPU功能的。但是想想一个采样周期2ms,4个深度都满了说明漏电流检测的函数lkgecur_handle执行了8ms还没执行完,虽然开方、平方运算耗时,但是8ms都执行不完有点扯淡,不太可能。

    先在ADC2注入通道中断回调中反转一个IO口,看波形的周期,发现确实是2ms反转一次,说明T2触发采样中断是准确的,没有问题的,那接着找其他问题。

    接着就先优化程序吧,先看看能用的招数,先把程序的优先级调整到第二高,仅次于SPI Slave通信,目前SPI没有接主机,因此此任务不影响,始终在suspend状态。

    那就首先将环形缓冲区换成4个队列,然后看打印信息,依然是接近4个缓冲区都满,看来环形缓冲区耗时不是主要矛盾。继续,直接将lkgecur_handle中的开方、平方运算注释掉,查看打印log,现在好点了,使用的缓冲区在3、4个之间跳,那这有点扯淡了,计算不是最大的耗时矛盾点。

    考虑了一下那就是串口LOG_D( )打印耗费时间了,毕竟他的打印不是中断方式的,而是通过阻塞方式进行的,对这种处理高速采样的线程对时间是很敏感的,这么分析LOG_D( )因该就是耗时的最大矛盾了。

    将LOG_D( )打印注释掉后,观察现象,idle task 的LED可以闪烁了,这说明了系统目前是可调度的,任务安排没问题,采样处理线程也没有一直占用CPU;将注释掉的平方、开方运算还原,观察现象,LED依然能正常闪烁,说明LOG_D( )占用了太多的时间,不适合这种需要高速处理数据线程的调试。

三、解决的办法

    要想高速的调试,看变量的值,只能通过Segger的RTT了,实时打印不影响系统的运行,因为Segger仿真器和MCU之间采用了共享内存的方式,log的打印是1us级别的,所以高速的中断变量的打印调试、高速数据采集线程的处理函数调试都离不开他。

    想想以前用到的调试手段基本一个keil配合串口打印就搞定了,目前使用的是开发环境是RT-Studio,无法像keil那样动态显示变量的值,只能暂停后才能观察变量值,不能动态刷新,这个是RT-Studio的硬伤,比keil LOW了不是一星半点。

    下面进行RTT的RTthread的配置,查找资料发现RTthread已经支持Segger-RTT,只需要简单的初始化和配置就可以代替串口打印,而不需要改变打印的函数,并且支持FinSH

    在RT-Studio里面查找RTT库,输入seg,可以搜索到SEGGER_RTT,点击添加,网上资料显示可以下载V1.1.0,但是我这里操作V1.1.0后在pakage里面无法加载源文件,版本选择了latest后可以了。

    在board.c中添加RTT初始化代码:

    extern int rt_hw_jlink_rtt_init(void);
    rt_hw_jlink_rtt_init();            // support RTT

    代码添加完后如下图:

    注意一个问题,这里RTT占用了一个系统的空闲钩子函数

rt_thread_idle_sethook(segger_rtt_check);

      

  所以如果没有使用系统的空闲钩子函数,需要打开空闲钩子函数,如果已经使用了空闲钩子函数,则钩子函数的数量要增加一个,不然会自己的一个钩子函数会得不到执行的机会,工程中原来我使用了两个空闲钩子函数,没有增加,结果LED运行灯不闪了,找到此问题后,将钩子函数增加到3,LED可以闪烁了,并且RTT打印也可以了,增加钩子函数操作如下图:

  将console的名字改为:jlinkRtt,操作如下:

    上面的操作等效于修改rtconfig.h中的 #define RT_CONSOLE_DEVICE_NAME  "jlinkRtt"

    这样,工程的SWD调试就支持Segger-RTT了,打开J-link RTTViewer 可以跟串口调试那样操作,要想输入FiSH指令,进行一下设置:

    这样在下面的输入框中就可以输入FinSH指令了,到此高速调试环境都搭建完成了,可以看到,输出的缓冲区计数值为rec_cnt = 1,这里可以清晰的看到4个采样周期内线程可以很好的将数据都消费掉,说明目前的MCU可以胜任计算计算漏电流的任务。

    通过这一通折腾,我觉着要是没有keil,还是使用RTT调试比较靠谱些,要是用串口打印的话,这些高速处理的中断和线程中变量没法查看,所以工程搭建的一开始最好把RTT支持调好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值