嵌入式软件优化记录


嵌入式软件优化

1、ARMCC编译器定义函数在指定flash块

  • 指定函数放置在指定Flash地址,对于具有不同访问速度Falsh的MCU,可以提高核心代码的运行速度,对于内存资源较丰富的MCU,可以将需要快速执行的代码,拷贝到RAM中运行,降低取指令带来的效率影响。
    image

2、在main函数设置中断向量指针

  • ARM核心单片机一般是在cmsis库里定义中断向量指针,如果程序不在默认起始地址,就需要修改MCU中断向量地址变量,这里可以在main()的前端修改,避免在原库代码中修改,影响移植性,然后保证在修改前不会发生中断即可。
    image

3、keil链接脚本设置 AT32F403A

  • AT32F403ACGT7 MCU的.sct文件示例,该MCU存在一块128KB的全速Flash,可以让内核零等待访问取指令,这里在sct文件中将这块内存定义为快速区域,将重要数据和代码放到快速区,提高效率。
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x08001000 0x0004F000  {    ; load region size_region
  
  ;快速代码区域
  ER_IROM1 0x08001000 0x0001F000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   startup_at32f403acgt7.o
   memseta.o
   arm_copy_q7.o
   arm_fill_q7.o
   main.o
   AlgInfraredVector.o
   AlgIRRays.o
   AlgApiMath.o
   AlgKalmanFilter.o
   AlgKMeans.o
   AlgMatchPoints.o
   AlgMatrixCal.o
   AlgSmoothness.o
   AlgTouchPoints.o
   *(.code.fast)    ; #pragma arm section code =".code.fast"
   *(.code.zwflash, +Last) 
  }
 
  ;慢速代码区域 强制pad到128K以后
  ER_IROM2 0x08020000 FIXED  {  ; load address = execution address
   * (.code.slow)   ; #pragma arm section code =".code.slow"
   * (.constdata)
   * (.conststring)
   .ANY (+RO)
  }
  
  RW_IRAM1 0x20000000 0x00034000  {  ; RW data 最大208k, 预留16K给stack
   .ANY (+RW +ZI)
   * (.shared, +Last)
  }
  
  ;强制设置STACK栈顶到SRAM最大 避免浪费SRAM
  RW_STACK 0x20034000 UNINIT 0x00004000  {  ; STACK 16k
   startup_at32f403acgt7.o (STACK)
  }
  
;  RW_HEAP 0x20034000 UNINIT 0x00000000  {  ; HEAP 0k
;   startup_at32f403acgt7.o (HEAP)
;  }
  ER_IROM3 0x08040000 FIXED  {  ; load address = execution address
    * (.code.ext)
    * (.rodata.ext)
  }
}

4.指定函数在快速响应flash区域,并以O1编译

  • 部分代码对时序要求很高,几十纳秒级别,不同的优化等级会影响指令的先后运行,为了避免优化带来时序调试和运行的影响,这里将相关代码指定编译等级编译。
    image

5.带有Cache的MCU开发注意

带有Cache的MCU,及带有高速缓存,往往是因为内核主频过高,而内存和Flash速度较低,CPU内核在这种情况下运行会受到瓶颈,降低效率,所以厂商在内核附近部署了两块较小尺容量的全速内存,即缓存,在运行时Cache会对即将访问的地址进行预存取,然后内核访问相关地址时,如果已经被缓存,即命中,则会从缓存中存取数据,达到全速访问的效果,这种预存取的效率很高,基本可以全命中。

在M7内核或相似较高频率内核的MCU开发上,Cache会带来很多开发上看似玄学的问题:数据不一致,写数据与读数据不一致,外设运行动作异常,DMA出飞机等等,核心原因是,CPU从Cache缓存读写的数据,与实际地址上的数据因为各种未同步原因,出现不一致问题,在开启Cache的情况下,对一块地址的访问,可能会有CPU的直接访问,Cache的预存取访问,DMA的访问,如果代码上没有做好同步与配置,会导致出各种玄学飞机,所以,有Cache的MCU,出玄学问题时,一定要关掉Cache调试!

Cache的配置,往往MCU厂商已经做好了相关内容,可以直接结合需求,配置配置就可以,一定要对内存做好规划,哪些用,哪些给DMA,哪些给外设(USB、以太网FIFO)。

STM32H7示例

Cortex-M7 MPU与Cache配置

image

硬汉哥的一图横扫ICache和DCache

image

6.DWT内核计数器 计算时间

初始化DWT时钟

// 初始化DWT计数器
void DWT_Init(void)
{
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;  // 使能DWT外设
    DWT->CYCCNT = 0;                                // 清零周期计数器
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;            // 使能CYCCNT寄存器
}

使用

Time_use=DWT->CYCCNT;
/***Your Code***/
Time_use=(DWT->CYCCNT-Time_use)/SystemCoreClock*1000000 ;

7.STM32使用 Cordic 计算浮点三角函数

介绍

  • CORDIC的英文全称为Coordinate Rotation Digital Computer,即坐标转换数字计算机,通过不断旋转坐标最终接近达到计算结果
  • 这是“移位相加”的操作去计算各种函数的经典计算应用
  • 特点是开销小,计算快,广泛应用于各种计算领域
  • 能够提高实时计算三角函数、开平方等计算的速度
  • 和查表法对比,还是查表法快

使用

/* Define PI */
#define PI 3.1415926536f
/* Define Q31 */
#define Q31 0x80000000
/* Define Q31 to float unit in RADIAN = Q31/PI */
#define RADIAN_Q31_f 683565275.6f

/**
 * @brief Calculate sin/cos value in float unit
 * @param angle in (-π,π), sin/cos value out
 * @retval None
 */
void Calculate_Float_Sin_Cos(float angle,float *sin, float *cos)
{
    RCC->AHB1ENR |= RCC_AHB1ENR_CORDICEN;  // Enable CORDIC Clock
	/* Q31,two write, two read, sine calculate, 6 precision */
	CORDIC->CSR = 0x00180061;
	/* Write data into WDATA */
	CORDIC->WDATA = (int32_t )(angle * RADIAN_Q31_f);
	/* Modulus is m=1 */
	CORDIC->WDATA = 0x7FFFFFFF;
	/* Get sin value in float */
	*sin = ((int32_t)CORDIC->RDATA)*1.0f/Q31;
	/* Get cos value in float */
	*cos = ((int32_t)CORDIC->RDATA)*1.0f/Q31;
}

大佬测试的计算速度,测试平台应该是STM32G431

image

image


文章作者: biubiu选手
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 biubiu选手 !
评论
  目录