STM32F407由浮点数%f打印乱码牵扯出的栈溢出问题解决

1. 涉及的软硬件工具

(1)MCU为STM32F407,自带FPU浮点数运算

(2)keil工程使用正点原子STM32F407探索者工程,包含动态内存分配mymalloc功能

2. 问题说明

之前的工程中,下面操作是可以直接打印浮点数的:

float num = 11.965;
printf("num = %.3f\r\n", num);
//打印:num = 11.965

后面添加一堆功能之后,发现同样的操作,无法正常打印浮点数,打印效果如下:

num = 11\0965  //原本的小数点'.'变成'\0'
num = 11?965   //原本的小数点'.'变成'?'

3. 问题排查与定位

3.1 排查1

由于最直接的现象是printf()函数打印浮点数是有问题的,所以直接开始了解STM32F407打印浮点数失败的原因(实际上该解决问题的思路有问题,因为原本就可以浮点数打印,不过也当做顺便了解其他知识)。

STM32F407有自带的FPU浮点数运算,但不一定能直接使用,可能需要使能啥的,在CSDN上了解到,具体如下:

(1)是否优化等级开太大了。并没有优化

(2)工程是否选择为Single Precision。已选择

(3)__FPU_PRESENT和__FPU_USED宏是否都为1

__FPU_PRESENT确定为1

__FPU_USED为0,可能有问题,手动改为1,会产生很多报错,应该不能直接改

后来发现原来在启动文件下已经帮我们做好了,直接通过汇编使能了浮点运算,跟上面的__FPU_PRESENT和__FPU_USED宏是否为1应该已经关系不大。

(4)小尝试

由于printf()是标准库函数,是否是由于标准库函数有点问题,需要使用keil自带的小型的标准库。

勾选"Use MicroLIB"选项使能,结果,发现居然能解决问题!神奇。具体原因后续进行说明,这里虽然解决了,但大概率是治标不治本的,先标记一下。

排查到这里,大概知道不是FPU不使能的问题,毕竟之前工程已经能正常打印浮点数,所以肯定是因为自己加了什么代码导致的。

3.2 排查2

目前新的工程添加了挺多局部大数组,如static uint8_t rx_buf[1024]; (留意这里的static),是否因为由于RAM不够导致的一些程序上的异常。好像也不是,编译工程:

Program Size: Code=71162 RO-data=1358 RW-data=9852 ZI-data=88268  

当前的RAM占用如下:

RW Data + ZI Data = 9852 + 88268 = 98120

占用了98kB,而STM32F407的RAM有128kB,因此不是RAM不够导致。

3.3 排查3

由于之前添加的代码有点多,所以通过屏蔽代码的方式,最终定位到如下代码(伪代码):

void test_fun (void)
{
    uint8_t rx_buf[1024] = {0};
    
    //对rx_buf数组进行一些操作...
}

将这个函数屏蔽了,其他的什么都没改动,就能正常的打印浮点数了,跟之前的工程效果一样。

为什么函数内静态的static uint8_t rx_buf[1024]可以,而非静态的uint8_t rx_buf[1024]就不可以?

这里又涉及到不同变量存储位置不同的问题,具体如下:

(1)函数内的局部静态变量static rx_buf[1024]和局部非静态变量uint8_t rx_buf[1024],两者都是占用了RAM,但更进一步的分区不同

(2)一般单片机的RAM,分为栈区、堆区、全局静态区、常量区、代码区,这5个区域的划分(具体可参考其他文章,可能叫法上有小的差异)

局部静态变量static rx_buf[1024],是存储在全局静态区,而局部非静态变量uint8_t rx_buf[1024]是存储在栈区

(3)当前是栈区的变量uint8_t rx_buf[1024]有问题,所以需要看看当前工程定义的栈大小是多少。再次看到启动文件,可以发现当前工程分配的栈大小为0x400,也就是1024字节。所以直接在函数内部定义一个非静态的大数组1024字节,栈区就会直接溢出,导致一些奇奇怪怪的问题出现。如printf()打印浮点数出现问题。

4. 问题解决与总结

最终定位到是由于局部变量大数组导致的栈溢出问题,进而程序异常导致的printf()函数打印浮点数出现乱码的问题。

解决

解决方法也很简单:

(1)将栈空间定义大一点,如10kB

(2)不使用栈空间,仍然定义局部静态变量,加上static即可

(3)使用动态内存分配的方式

总结

(1)回到3.1排查1的(4)小尝试,为什么勾选"Use MicroLIB"后就能正常打印浮点数?猜测是由于使用了比较小型的标准库,在调用printf("%f")打印浮点数时,没有访问到出错的位置,运气不错避开了,所以能够正常打印浮点数。

(2)实际上,假如经常用到大数组,最好还是用动态内存分配,需要用到时就分配,用完就释放,保证RAM空间是足够使用且安全可控的。对于动态内存的分配和释放,会增加代码的复杂度,而且RAM的释放时机,需要比较好的把握。实际使用需要进行权衡。

(3)栈空间定义多大才足够使用,实际上是未知的,不确定未来还会使用多大的空间。(不过栈用起来确实挺方便的,每次都是重新初始化,相当于自动清理之前的数据)

(4)栈溢出的情况,可能会比当前遇到的现象更加严重,目前应该算运气好只是打印浮点数有异常,实际上还会导致程序跑飞。因为栈溢出了,开始出现一些随机性,就看程序访问到什么位置,访问到非法位置,那程序就必然会跑飞。

【个人工作经验总结,有描述不当或不够严谨的地方,请多多指教,相互学习提升!】

  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值