-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
RT-Thread Version
5.1.0
Hardware Type/Architectures
STM32F407VGT6
Develop Toolchain
MDK Keil
Describe the bug
现象复现:
-
在menuconfig界面下使能
BSP_SPIx_TX_USING_DMA
及BSP_SPIx_RX_USING_DMA
; -
使用
rt_malloc()
函数申请两块内存:tx_data(>=10)和rx_data(>=10); -
将目标SPI总线的MISO接口拉高或拉低;
-
调用
rt_spi_transfer
函数向SPI设备发送>=10字节的数据,使用上一步申请的内存tx_data和rx_data分别作为发送缓存和接收缓存; -
打印rx_data中的内容,发现内容不符合预期。期望结果应为rx_data的值全为0xFF(MISO接口拉高)或0x00(MISO接口拉低),而实际则为内存中未更新的随机数据。
问题定位:
问题出在 rt-thread-5.1.0/bsp/stm32/libraries/HAL_Drivers/drivers/drv_spi.c
的spixfer
函数中。
原因分析:
- 348行到374行的分析:

在启用TX_DMA的情况下,为了提高DMA的效率,该函数对send_buf
的值(即发送缓冲区的地址)做了对齐4字节的操作,当send_buf
的值恰巧对齐4字节时,直接将send_buf的值赋给p_txrx_buffer
;当不对齐时,则通过rt_malloc()
函数重新申请一块内存(该函数能够保证生成的内存对齐4字节),并将该地址赋给p_txrx_buffe
,同时也保存一份副本在dma_aligned_buffer
变量里(在此之前该变量的值为RT_NULL
)。
- 383行的分析:

在TX_DMA和RX_DMA皆启用的情况下,数据的传输实际上是调用了HAL_SPI_TransmitReceive_DMA
函数实现的。但是在这个驱动中使用p_txrx_buffe
同时作为HAL_SPI_TransmitReceive_DMA
的TX和RX缓存,也就是说HAL_SPI_TransmitReceive_DMA
在发送p_txrx_buffe
中的数据的同时也在将接收到的数据写入其中,这本身并没有太大问题,只要后面再将接收到的数据复制回recv_buf
中就行。
- 468行到482行的分析:

但是这个函数并不是在任何情况下都会将接收到的数据复制回recv_buf
中,而是只在dma_aligned_buffer != RT_NULL
的情况下才会执行rt_memcpy(recv_buf, p_txrx_buffer, send_length);
语句将数据复制回去。这里的dma_aligned_buffer
就是前面提到的重新申请的内存的副本。只有当send_buf
的值不对齐4字节时,dma_aligned_buffer
才会保存一份重新申请的内存的副本,而如果send_buf
的值恰巧对齐4字节,dma_aligned_buffer
将维持初始值RT_NULL
,此时rt_memcpy(recv_buf, p_txrx_buffer, send_length);
语句也将无法得到执行,于是就造成了recv_buf
不更新的bug。
其他问题:
事实上,这份驱动还有其他问题。例如,如果你只使能了TX_DMA而未使能RX_DMA,则spixfer
则只使用语句HAL_SPI_Transmit_DMA(spi_handle, (uint8_t *)p_txrx_buffer, send_length);
将数据发送出去,而完全抛弃了接收数据。在这种情况下使用rt_spi_transfer
传输数据依然会出现接收数据错误的问题。
Other additional context
No response