STM32F4之FPU设置

本文介绍如何在STM32F4上高效利用FPU进行浮点运算,包括必要的编译器配置和使用特定函数提高运算速度的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://hi.baidu.com/atf144x

    浮点运算一直是定点CPU的难题,比如一个简单的1.1+1.1,定点CPU必须要按照IEEE-754标准的算法来完成运算,对于8位单片机来说已经完全是噩梦,对32为单片机来说也不会有多大改善。虽然将浮点数进行Q化处理能充分发挥32位单片机的运算性能,但是精度受到限制而不会太高。对于有FPU(浮点运算单元)的单片机或者CPU来说,浮点加法只是几条指令的事情。

        现在又FPU或者硬件浮点运算能力的主要有高端DSP(比如TI F28335/C6000/DM6XX/OMAP等),通用CPU(X87数学协处理器)和高级的ARM+DSP处理器等。

        STM32-F4属于Cortex-M4F构架,这和M0、M3的最大不同就是多了一个F-float,即支持浮点指令集,因此在处理数学运算时能比M0/M3高出数十倍甚至上百倍的性能,但是要充分发挥FPU的数学性能,还需要一些小小的设置:

        1.编译控制选项:虽然STM32F4XX固件库的例程之system_stm32f4XXX.c文件中添加了对应的代码,但给用户评估使用的STM32F4-Discovery例程中却没有,因此MDK4.23编写浮点运算程序时,虽然编译器正确产生了V指令来进行浮点运算,但是因为system_stm32f4XXX.c文件没有启用FPU,因此CPU执行时只认为是遇到非法指令而跳转到HardFault_Handler()中断中原地踏步。因此要保证这个错误不发生,必须要在system_init()函数里面添加如下代码:


  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); 
  #endif

          因为这个选项是有条件编译控制的,因此需要在工程选项(Project->Options for target "XXXX")中的C/C++选项卡的Define中加入如下的语句:__FPU_PRESENT=1,__FPU_USED =1。这样编译时就加入了启动FPU的代码,CPU也就能正确高效的使用FPU进行简单的加减乘除了。

        但这还远远不够。对于复杂运算,比如三角函数,开方等运算,如果编程时还是使用math.h头文件,那是没法提升效率的:因为math.h头文件是针对所有ARM处理器的,其运算函数都是基于定点CPU和标准算法(IEEE-754),并没有预见使用FPU的情况,需要很多指令和复杂的过程才能完成运算,也就增加了运算时间。因此要充分发挥M4F的浮点功能,就需要使用固件库自带的arm_math.h,这个文件根据编译控制项(__FPU_USED == 1)来决定是使用那一种函数方法:如果没有使用FPU,那就调用keil的标准math.h头文件中定义的函数;如果使用了FPU,那就是用固件库自带的优化函数来解决问题。

        在arm_math的开头部分是有这些编译控制信息:

         #ifndef _ARM_MATH_H
         #define _ARM_MATH_H

         #define __CMSIS_GENERIC             

         #if defined (ARM_MATH_CM4)
                    #include "core_cm4.h"
         #elif defined (ARM_MATH_CM3)
                     #include "core_cm3.h"
         #elif defined (ARM_MATH_CM0)
                     #include "core_cm0.h"
         #else
                   #include "ARMCM4.h"
                   #warning "Define either ARM_MATH_CM4 OR ARM_MATH_CM3...By Default building on ARM_MATH_CM4....."
         #endif

         #undef  __CMSIS_GENERIC             
               #include "string.h"
                #include "math.h"

        就是说如果不使用CMSIS的,就会调用keil自带的标准库函数。否则就用CMSIS的定义。这里因为是用的STM32F4,所以应该要ARM_MATH_CM4控制,即加入core_cm4.h,否则就用使用ARMCM4.h——但在编译时keil会提示找不到这文件。因此需要在工程选项之C/C++选项卡的define中继续加入语句ARM_MATH_CM4。

        加入上述编译控制项之后,高级数学函数的使用基本没问题了,比如正余弦三角函数的计算。但需要注意,如果你直接使用sin()、cos()、sqrt()这样的函数,那结果还算调用keil的math.h,你可以在debug时看对应的代码,其汇编指令为BL.W __hardfp_xxx。因此这时要完成三角函数的计算就要使用arm_sin_f32()或者arm_cos_f32(),用法不变,这两个函数的原型分别在arm_sin_f32.c和arm_cos_f32.c中。通过对256点三角函数表的查询和插值算法得到任意角度的精确函数值,这就比“原装”的sin()、cos()快多了。

        当然有些例外的是开发函数sqrt(),在arm_math.h中是这么定义的:

             static __INLINE arm_status  arm_sqrt_f32(float32_t in, float32_t *pOut)
                     {
                        if(in > 0)
                             {
                                     //    #if __FPU_USED
                                    #if (__FPU_USED == 1) && defined ( __CC_ARM   )
                                              *pOut = __sqrtf(in);
                                   #else      
                                              *pOut = sqrtf(in);
                                   #endif
                                 return (ARM_MATH_SUCCESS);
                            }
                        else
                           {
                               *pOut = 0.0f;
                                return (ARM_MATH_ARGUMENT_ERROR);
                          }
               }   

        即开方用的函数是arm_sqrt_f32(),其中首先判断被开发的书是否大于0,只有大于0的才能进行运算,否则输出结果为0并返回“错误”标志。如果大于0,并且实用了FPU和__CC_ARM控制项,那调用__sqrtf()来完成编译,否则调用sqrtf()——这个sqrtf()是能在keil的math.h中找到的,即调用子函数来完成运算,而__sqrtf()呢?新出现的,相信大家都能猜到是什么玩意儿:对,就是VSQRT指令!因此要把这点性能也要发挥出来,就需要工程选项之C/C++选项卡的define中继续加入语句__CC_ARM才行。大家可以比较一下是否加入__CC_ARM编译后会汇编代码的差别巨大差别。

       当然,对于arm_sqrt_f32()函数还是有些麻烦,如果你确认被开方的书是大于等于0的,那就直接使用__sqrtf()函数完成运算,即一条简单的VSQRT指令。

       STM32F4固件库还提供了其他很有用的数学函数,都位于DSP_Lib文件夹,请大家慢慢探索,Discovery!

STM32F4 系列微控制器上,启用和使用 FPU(浮点运算单元)需要进行一系列配置步骤。STM32F4 系列基于 ARM Cortex-M4 内核,该内核支持单精度浮点运算单元(FPU),但默认情况下可能并未启用。 ### 启用 FPU 的步骤 #### 1. 配置 CPACR 寄存器 ARM Cortex-M4 提供了一个名为 `CPACR`(Coprocessor Access Control Register)的寄存器,用于控制协处理器的访问权限。FPU 属于协处理器的一部分(即 CP10 和 CP11),因此需要修改该寄存器以启用对 FPU 的访问。 ```c // 启用 FPU SCB->CPACR |= ((3UL << 10*2) | (3UL << 11*2)); // 启用 CP10 和 CP11 __DSB(); // 数据同步屏障 __ISB(); // 指令同步屏障 ``` 此代码段将 CP10 和 CP11 的访问权限设置为“完全访问”,从而允许使用 FPU。 #### 2. 设置编译器选项 为了确保编译器生成使用 FPU 的指令,必须在编译时启用相应的浮点支持选项。不同的编译器有不同的设置方式: - **GCC**:使用 `-mfpu=fpv4-sp-d16` 和 `-mfloat-abi=hard` 选项。 - **ARMCC**:使用 `--fpu=FPv4-SP` 选项。 - **IAR**:在项目选项中选择适当的 FPU 设置,例如 `Floating point support: VFPv4_SP`. 此外,还需确保链接器脚本和启动文件支持 FPU 初始化。 #### 3. 使用浮点数据类型 一旦 FPU 被正确启用,就可以在代码中直接使用 `float` 类型进行浮点运算。例如: ```c float a = 3.14f; float b = 2.5f; float result = a * b; // 使用 FPU 进行乘法运算 ``` 由于编译器已经知道可以使用硬件 FPU,它会自动生成对应的 VFP 指令来执行这些操作。 #### 4. 性能验证 如引用中所述,在启用 FPU 的情况下,执行一百万次浮点运算仅需约 209 ms;而在未启用 FPU 的情况下,则需要约 555 ms[^1]。这表明 FPU 对浮点运算性能有显著提升作用。可以通过定时器或 SysTick 来测量具体耗时,以验证 FPU 是否正常工作。 #### 5. 注意事项 - 确保堆栈对齐:某些编译器要求堆栈按 8 字节对齐才能高效处理浮点值。 - 浮点库支持:如果使用了标准 C 库中的数学函数(如 `sin`, `cos` 等),需要确认它们是否链接到了利用 FPU 的版本。 - 中断上下文切换:当在中断服务例程中使用浮点寄存器时,应保存和恢复 FPU 上下文,避免破坏主程序的状态。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值