1. 问题现象
最近做项目时用到国产的一款芯片——国民技术的N32G435,采用32位ARM Cortex™-M4F内核,集成浮点运算单元(FPU)。在用这款芯片做浮点数计算时,却经常出现一些匪夷所思的现象,例如非负的两个浮点数相除得到了负数,两个非负浮点数相减得到一个更大的非负数。
而这款MCU是支持浮点数运算的,软件中也正常开启了浮点数运算功能(FPU)。如下图所示:
这种偶发问题导致了浮点数的计算结果不可靠,引发了严重的后果。
2. 分析思路
2.1 首先排查软件实现上的错误
反复排查软件实现上无误。
2.2 确认MCU是支持浮点数运算并正确配置了FPU模块
大部分情况浮点数计算结果是正确的,出错是少数情况。
2.3 排查浮点数计算出错的根本原因
MCU运行了uCOS操作系统,开了定时器中断和外部中断,在偶然浏览到这篇博客stm32F4单片机计算浮点数的时候偶发性出错的解决方法,与其中遇到的问题类似,并且都是M4的芯片,发现计算浮点数过程中, 若触发中断, 浮点数如果未能正常入栈, 导致中断结束后从栈中取出来的浮点数出现乱码, 从而导致偶发性计算错误。
3. 解决方案
使用keil独有的操作总中断函数
#define taskENTER_CRITICAL() __disable_irq()
#define taskEXIT_CRITICAL() __enable_irq()
在进行浮点数运算前,关总中断,计算结束后再开总中断。
taskENTER_CRITICAL();
//进行浮点数运算
//....
taskEXIT_CRITICAL();
在程序中,复制一份相同的浮点数变量,进行同样的浮点数运算,但是复制的那个变量每次进行浮点数运算时进行关中断保护。接下来就可以跑程序,等待现象复现。验证效果如下:
当原始的float变量浮点数运算出现错误值时,关中断保护的float变量仍然能正确运算。
4. 回顾思考
通过这次问题解决的过程,总结了几点教训:
- 尽量少用、不用浮点数,现在MCU虽然大部分都支持浮点数运算,但是不排除这些MCU也是存在一些bug的,涉及数学运算时能用int解决尽量别用浮点数,稳定性可靠性不及int;
- 要关注MCU负载率,在该项目中,使用了大量软件延时,导致MCU负载率很高,也增加了浮点数运算出错的可能性;
- 使用浮点数运算做好关中断保护,一定要使用浮点数时,需谨慎,做好出入栈保护,避免非应用层的运算出错。