掌上单片机实验室 – RTT 运行性能评估(21)

一、背景

        已经基于 RT-Thread 实现了小车的基本控制,由于小车的编码器较为简陋,为实现PID调速,算法上做了不少处理,故增加了不少运算量。虽说从现象上看,程序运行正常,预期的功能均已实现。

        但如何要想得到一个可靠的程序,必须知道程序运行时的几个关键指标:堆栈使用情况、MCU的占用率、中断响应时间。这些对实时操作系统而言是至关重要的,合理的堆栈剩余空间、MCU占用率及中断响应时间是嵌入式设备连续运行状态下可靠性的保障。

        为此,打算基于前面所完成的程序,测试一下上述指标。

二、需求

        在最接近真实运行状态下,检测:

  1. 具备堆栈使用情况监测手段
  2. CPU(MCU)占用率
  3. 外部中断响应时间

        所谓“真实”,是指:所添加的测试代码尽量不影响原来程序的运行,同时又能反映程序在正常运行状态下,各种情况所对应的上述参数。

        至于为何选择这三个指标,网上应该有很多相关讨论,在此就不详述了。

三、测试方式及结果

3.1 堆栈使用情况监测

        一般多任务操作系统都需要监测堆栈使用情况,因为栈溢出将会导致程序跑飞,故一般OS会提供一个函数获取栈的最大使用情况。在FreeRTOS中,我就是用此方式获取的。

        但在RT-Thread中未找到类似函数,但它提供了两个更好的方式:

  1. 支持监测栈溢出:就是在程序出现栈溢出时,会通过FinSH 从串口输出信息,这个对使用者还是十分友好的,在使用FreeRTOS时,我是发现程序异常再打开栈监测(因为栈监测输出有点耗资源,故只在需要时打开),此时如果程序已经不能运行基本功能,则需要通过“试”来确定是不是栈溢出所致?是那个任务的栈不够?有点麻烦。在RT-Thread中直接就可以通过输出信息知道那个线程栈不够,很方便。
  2. 可以通过FinSH交互命令,通过串口终端命令,看到所有线程的栈使用情况;这个和之前我在 Arduino+FreeRTOS 下实现的功能类似,但无需自己编写代码,也是一个对用户十分友好的手段。

        有了栈溢出监测,为何还需要知道每个线程栈的使用情况?这是因为必须保证一定的余量,否则程序长期运行时就存在隐患。

        这是用 FinSH 得到从目前程序栈使用情况:

3.2  CPU(MCU)占用率

        这个也是从程序连续运行的可靠性及实时性角度考虑的,如果CPU的占用率过高,在特殊情况下,就存在实时响应跟不上的可能,也许会造成程序崩溃。

        但此指标 RT-Thread 似乎没有直接提供手段获取,FreeRTOS有。

        在网上找了参考资料,看分析多有局限性,于是想自己结合对 RT-Thread 的理解,以及目前程序的架构,尝试写一段代码实现 CPU 占用率的监测。

        查阅 RT-Thread的API手册,拟利用系统提供的进入空闲状态的钩子函数:

        rt_err_t  rt_thread_idle_sethook (void(*)(void) hook)

        和系统进入中断钩子函数:

        void  rt_interrupt_enter_sethook (void(*)(void) hook)

        结合程序中启用的硬件计时器,实现对MCU占用率的监测,具体构思如下:

  1. 定义:进入空闲状态时时间为 T1及上次进入空闲时间 T1last,进入中断时间为 T2;
  2. 在进入中断钩子函数时,记录T2。
  3. 在进入空闲钩子函数时,读取硬件计时值,得到T1,如此时 T2有效,则计算:

        运行时间:Trun =  T1 –  T2  + 累积Trun

        空闲时间:Tidle =  T2 – T1last  + 累积 Tidle

        刷新 T1last = T1

        总累积时间 Ttotal  = 累积Trun +累积 Tidle

        当总累积时间大于等于设定的计算周期时,计算CPU占用率:

        累积Trun / Ttotal

        并将两个累积值清零。

        上述构思是以此为前提:系统进入中断钩子函数涵盖了所有中断(Tick中断、用户定义的中断),这样不论是任务调度还是外部中断,都会终止空闲计时,开始工作计时。

        实际实施中发现不行,先是以为上述钩子函数都是在中断中调用的,读取硬件定时器函数是否会因在中断中读取冲突而失败?系统提供的读取函数还是挺复杂的。

        因为是在中断中处理,没有加原子操作(而且我也不倾向于对这类不是十分要紧的处理加原子操作,担心会因为频繁关中断而降低实时性),对读取硬件计时值有点没底。

        调试发现:确实出现了读取定时值不对的情况,在调试环境中跟踪到系统函数时,才发现我设置的硬件计时器溢出值太大,导致硬件定时器启动函数:

        rt_device_write(sHwTimer, 0, &sHwTimeoutVal, sizeof(sHwTimeoutVal))

        中调用的参数计算函数(在 hwtimer.c 中 ):

        timeout_calc()

        计算溢出了。本来我是想用定时器周期方式,但不期望频繁溢出,所以设置了86400秒的溢出周期,正好是一天。但此时上述函数计算结果异常,导致定时器不能正常工作。

        我是在单独编写一个硬件定时器测试程序时才发现,随意写了一个 50000秒的溢出周期,结果没有问题,跟踪后发现参数计算正常。改为86400后,那个周期浮点值就出错了;改为60000,也是正常的,就没有再去深究,本次的目的不在于此,先记录下来,有空再琢磨。有朋友知道问题所在的,还望不吝赐教。

        但消除了定时器问题,上述采集CPU占用率的方法还是不行,感觉那个进入中断的钩子函数没有吃透,算了,改弦易辙。

        从系统钩子函数中发现,还有一个调度器钩子函数,每次线程转换会调用,并且给出从哪个线程转到哪个线程。我想基于此钩子函数实现:

  1. 在进入空闲线程(to 为空闲线程)时记录起始时间
  2. 在离开空闲线程(from 为空闲线程)时记录结束时间。
  3. 在得到结束时间时,计算本次空闲线程的运行时间(相减),并累加;
  4. 同时在首次离开空闲线程时,记录此时时间作为计算周期起点,之后每次离开空闲线程均计算一下,是否大于等于预设的计算周期,如是,则计算CPU占用率:

        (实际计算时间 – 累计的空闲线程时间)/实际的计算时间

        上述算法的想法很简单,就是根据单位时间空闲线程的运行时间,得到CPU占有率。

        不知道此构思是否有漏洞,欢迎有兴趣的朋友一起探讨、指正。

        按此方式基本实现了对 CPU 占用率的采集。只是在运行中,偶尔会出现采集的两种异常:

  1. 离开时间小于进入时间,程序加了标志锁定,似乎无效,总是相似的差值:约60ms。
  2. 累计的空闲线程运行时间大于计算周期,似乎也是大相似的值:约60ms。

        不清楚是何原因所致。

        初步将计算周期定为 100ms,不清楚定为多少合适?

        测试结果为电机不转时,大约3% ~ 20%;电机调速运行时,最大可达到70%。这个数据感觉接近实际情况。

        因为CPU 占用率是动态的,为了能比较直观的观察结果,借用原来程序实现的速度、PWM采集方式,通过PC机谱图呈现。

        在单片机程序中增加CPU占有率的记录功能,设置了16个单元环形存放。再增加一个读CPU占有率记录的通讯命令。

        在PC端程序上增加读CPU占用率操作按钮,触发定时读取,一秒读一次,按100ms计算周期,可以保证读到连续的数据。

        借用原来的 PWM 波形显示处理。修改后的的PC界面:

 

 运行后采集的CPU占用率波形图(双电机定速、定距离运行):

        通过这种方式应该可以比较全面的反映单片机的运行情况。

3.3 外部中断响应时间

        小车目前所用到的外部中断为编码器脉冲采集,这也是现实应用中最常见的应用场景:对脉冲计数及相关测量(周期、脉冲宽度等)。

        中断响应速度决定了可以可靠采集脉冲的频率。

        测试方式如下:

        用一个富裕的 IO 作为测试信号输出,在脉冲中断进入时输出高电平,脉冲中断处理结束后输出低电平,这样就可以得到一个对应每个脉冲输入的程序响应信号:

 

        示波器的一个通道接脉冲信号,另一个接上述测试信号输出;用脉冲信号作为触发信号,从波形上即可得到程序脉冲响应时间。因为运行中响应时间有变化,故录制视频观察,从视频中截取的最大延时如下:

        从图中看,多数脉冲响应时间在15us左右,最长的约45~50us。

        对应目前的小车应用而言,最快脉冲速度只有 200Hz 左右,脉冲周期5ms,上述响应时间没有问题。

        但按此参数,从可靠角度考虑,如果用软件在中断中处理脉冲信号,在目前的MCU上能采集的最快脉冲频率约10KHz,即脉冲周期 100us,此时MCU已经很勉强了,如果中断中处理的时间长于50us,则将出现脉冲失步。(目前程序的中断处理时间约10us)

四、总结

        通过对上述三个指标的检测,可以说 RT-Thread 完全可以胜任大部分应用场景;而可靠性用我这样的方式是无法检测的,我所做的只能算是一般性评估,对初期选择有一定参考价值。

        后续还想尝试一下 RISC-V 内核的单片机,看看不同架构的单片机运行会有什么区别。

————————————

文中程序下载:

1、单片机程序:

链接:https://pan.baidu.com/s/1VRia-Tfg20cfNkxhKuiPLA

提取码:dxpo

2、PC端程序:

链接:https://pan.baidu.com/s/11efHwAgGBobHa9ZL6CZkpw

提取码:siqn

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入之梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值