最近把项目构建工具链由STM32CubeIDE​换成了CMake​,在测试过程中发现原先的LCD代码无法使用,经过排查后发现是SPI3​的DMA​传输出了问题,下面是问题的解决过程。

我将CubeMX​生成的两种工具链的代码进行了比对,发现CubeMX​和CMake​项目下的ld链接文件有所不同

CubeIDE​项目下,STM32H723​的内存分配如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* Highest address of the user mode stack */
_estack = ORIGIN(RAM_D1) + LENGTH(RAM_D1); /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Specify the memory areas */
MEMORY
{
ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K
DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 320K
RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 32K
RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 16K
}

CMake​项目的ld中这一部分的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* Highest address of the user mode stack */
_estack = ORIGIN(DTCMRAM) + LENGTH(DTCMRAM); /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Specify the memory areas */
MEMORY
{
DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
RAM (xrw) : ORIGIN = 0x24000000, LENGTH = 128K
RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 32K
RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 16K
ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K
}

两者对比发现在_estack​栈顶的地址上有区别,CubeIDE​将栈区定义在了RAM_D1​上(对应CMake​项目文件的RAM​块),而CMake​项目中把栈区定义在了DTCMRAM​这块内存上。

还有一个区别就是CMake​项目的RAM​内存块比CubeIDE​项目中的RAM_D1​块少了192Kb,少的这部分RAM在数据手册中定义为RAM shared between ITCM and AXI​意思是这块内存是RAM_D1​和ITCMRAM​共享的,全部分配给RAM_D1​或不分配在项目较小时没啥影响。

比对ld文件的其他部分,两个项目的区别还有.data​、.bss​和._user_heap_stack​段在CMake​项目中存储在了DTCMRAM​块,在CubeIDE​项目中存储在了RAM_D1​块,关于存储在不同内存块中有没有其他功能受影响还有待考证

翻阅数据手册的总线矩阵发现DTCMRAM​不能与DMA​通信,如图所示:

image

使用DMA的SPI通信说需要的函数为HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, const uint8_t *pData, uint16_t Size)​,需要传入pData​指针,这个指针指向了需要传输数据的内存,然而经常我们需要传输的数据是局部变量,局部变量存储在栈区,栈顶的地址在之前ld文件里_estack​定义了,这就造成了DMA有可能访问不到我们需要的地址。

综上,由于DMA访问不到DTCMRAM​这块内存,而恰巧CubeMX生成的CMake项目把栈区定义在了这里,造成了DMA的传输失败

我认为这是ST官方的模板没有统一内存的分配导致的,我使用的CubeMX版本是6.12.0​,在最新的6.12.1​版本中Cmake项目的ld文件把上述的差异部分留空让自己填写( 这个已确认是bug ),后续这个ld文件可能还会继续更新。