RT-Thread操作系统导致高速数据丢失

首先,RT是个非常优秀的国产操作系统,她没出现前我都是用UCOS的,虽然用的顺手了,但国产操作系统出现后我就毅然决然的选择了操作系统,一方面觉得架构清晰函数简明,另一方面对国产操作系统情节有点重上手之后差点泪流满面,国产开源东西已经做到此等程度确实让人振奋!

书归正传,这么优秀的操作系统用户面就会很广,我因为工作的原因小型嵌入式系统做的比较多,任务繁琐的情况下会有限考虑使用操作系统来简化任务调度。

问题是这样出现的:

耐心看完,用嵌入式操作系统的一定会有帮助的!

在做一个项目,用到STM32F407 的捕获功能,用rt-thread-3.1.3,只用最基础的内核,任何组件都不使用,对一个非周期脉冲进行数据记录,上升沿下降沿都捕获,分别记录脉冲的高低电平宽度,进入片子的脉冲是调制过的,所以波形数据我知道,对记录后的数据也有预期,但发现总是会丢几个脉冲。

然后我用函数发生器产生一个占空比50%的方波束,频率10K数量10000,进行测试,测试结果正常,增加频率,20K正常,50K正常。直接上100k,然后发现丢数了……10000个脉冲只记录到了9k多,查记录到的脉宽数据,本来应该是高低电平各5us,但中间会夹杂着10us的宽度。

捕获计时时钟我用的1us所以误差1~2个us是可以接受的,但大了一倍,而且每次都是一倍就很蹊跷了,继续查数据,发现9k多的脉冲数据丢的那部分脉冲都是被这么吃掉的。

重点来了。我是这样排故的!

首先,怀疑是函数发生器发出的脉冲有问题,用示波器查看波形一切正常。减少脉冲数量,每次只发出100个脉冲,ARM捕获没问题,发出200个脉冲,开始丢数了,一般丢在100多个脉冲的时候,用示波器抓波形,波形好着呢,电压也没问题,不存在触发不了的情况,排除了输入波形的问题。

其次,怀疑捕获中断函数冗长拖沓导致是不是数据处理时间过长导致,脉冲没来得及处理,然后把捕获中断函数里的数据处理全部删掉只简单的写个变量计数,10000个脉冲因为是双沿捕获应该出20000个捕获计数。想着这样应该不会出问题了吧,这样好使了我只需要优化中断代码就行了,不出意外的话就会出意外了,发完10000个脉冲计数只有1万9千多,而且差的还不少,这就奇怪了,怎么还能丢呢,把脉冲数改成1000,在捕获计数的同时记录脉宽数据,发送脉冲完成后DEBUG暂停进去看数据,计数为1千9百多,脉宽数据是中间还是夹杂着10us的宽度,出的几个10us脉宽正好是丢失的那几十个计数。

好了,其实到了这里应该就能确认异常位置了,但还需要进一步确认。心里强烈的感觉是RT的问题,而且经验告诉我八成是RT在任务切换、中断进出时开关硬件中断的问题。

首先,停掉工程里的所有任务线程,关掉出来脉冲捕获之外的所有中断,尽量减少RT的任务切换和调度带来的开关硬件中断操作。重新测试10000个脉冲,结果让人振奋,20000个捕获,好着呢~

然后开一个线程,线程中只有一条延时rt_delay(1)的操作,然后测试脉冲。嗯,不出意外的丢数了,不过丢的很少都是个位数的。

再开一个线程,里面仍然是只有一条延时rt_delay(1)的操作,嗯,又不出意料的丢的更多了。

到此,确认无疑是RT导致的捕获丢数了。

再下一步,把这个确认给她坐实了。坐实前先补充一点知识,RT在关键代码位置都会做保护,把关键代码用rt_hw_interrupt_disable()和rt_hw_interrupt_enable()给阔起来,这样就会避免不可控的意外中断了,RT开关硬件中断是操作PRIMASK寄存器实现的,PRIMASK寄存器不是屏蔽中断,而是中断到来也先被挂起而非立即响应,当RT完成关键代码执行后会使能中断,在关中断期间产生的中断才被执行。

按说如果关中断时间不长,尚不能使同一中断多次到来,还不至于丢数,除非是多次代码保护且间隔很短,致使被挂起的中断尚未开始执行就来了下一次中断,为了证实这个猜想,对RT原代码进行修改,在成对出现的rt_hw_interrupt_disable()和rt_hw_interrupt_enable()函数前后加入引脚的翻转。

这里我用的PE2,设置为输出,100M推挽,虽然设置是100M但执行起来肯定会有延迟,所以先测试连着翻转PE2测下时间,测试代码如下:

PEout(2)=1;

   PEout(2)=0;

   PEout(2)=1;

   PEout(2)=0;

   PEout(2)=1;

   PEout(2)=0;

   PEout(2)=1;

   PEout(2)=0;

   PEout(2)=1;

   PEout(2)=0;

可以看到,引脚翻转还是比较快的,前两次翻转和最后一次翻转用了差不多50ns,中间几次用了25ns,我们后面测试默认引脚翻转时间25ns。

搜索一下代码里那些地方用了关中断。

可以看到一共找到了77处,其中finsh我没有用,所以不用管,一共55处需要添加引脚翻转。 

就像这样,有一点啊,rt_schedule()函数里的关中断别加引脚拉高,因为一旦进了调度,下一步程序跑到哪儿就不好说了。

设置好之后,加载10us周期,50%占空比,个数1000的脉冲,得到双沿捕获数量1988,丢了12个。

捕获数据如下,cntTmp为边沿捕获数量,预期为2000。

可以看见本来一个脉宽5us(第一个是因为我有处理,所以少了2us),第三个就开始丢了一个沿,因为rt_schedule()函数里没加引脚跳转,所以关中断波形里显示的关中断时间比实际要少,找一个最具代表性的波形来看。 

黄色是输入脉冲的波形,绿色高电平表示关了硬件中断,屏幕中间位置就出现一个很明显的频繁关中断且持续时间特别长的、几乎覆盖了一个宽度5us的电平,从捕获数据上看,这个电平捕获确实丢掉了!原因就是频繁关中断导致前一个捕获中断还没读出来计数值,后一个脉冲就来了捕获计数值被覆盖了!

当我把脉冲周期调大,这种密集的关中断操作就不会充斥整个脉宽范围了,比如这样

虽然会有一段密集的关中断,但至少在每个电平里有至少3us以上的时间交给硬件来响应中断,所以边沿全部采到了。 

cntTmp是2000了。

至此,捕获丢数的问题原因找到了,是因为RT在切换任务以及各种保护临界段的时候关中断,导致硬件中断控制器无法及时响应中断造成的,这种影响会在多线程中让人更加崩溃,我尝试在原功能基础上增加几个线程,每个线程只做延时,输入10us周期的脉冲然后丢数更多了。

所以,RT虽然好用,但在高实时性场合、高频率中断的情况下,会造成响应不及时的情况,当然我用的只是开源的简配版RT,RT有个通过军方鉴定的版本不知道会不会好些,我也问过,鉴定版的很贵~

推广一下,之前捕获丢数的时候网上查过相关提问,有人说在RT下多串口同时接收也会出现丢数的情况,一般stm32异步串口最高波特率也就115200,按8位有效数据,1位开始位,1位停止位一个字节传输需要10位宽时间也就是86us,中断接收也就是86us产生一次中断,正常情况下一个串口收数不会出现问题,如果多个串口同时收数据,可能会出现我遇到的情况了!比如两个串口前后收到数据产生中断的时间间隔不超过5us而恰好赶上RT在切换线程,然后就中招了,如果串口多于2个在同时收数,尤其是数据量还比较大的时候就基本可以肯定会出现丢数的情况!

还是那句话,RT很好,但毕竟硬件平台还是有频率限制的。

 

 

 

 

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值