你好
我对 SPI 与 DMA 和 D-Cache 的结合感到困惑。
我正在使用
STM32H743ZI 和 KEIL uVision CMSIS 驱动程序。
在我看来,CMSIS 驱动程序有一个弱点。如果长度是 32 的倍数,它仅通过 HAL_SPI_TransmitReceive_DMA() 发送数据。否则它通过 HAL_SPI_TransmitReceive_IT() 发送数据。都是在D-Cache开启的情况下。
这是来自驱动程序的代码片段。
- static int32_t SPI_Transfer (const void *data_out, void *data_in, uint32_t num, const SPI_RESOURCES *spi)
- {
- ...
- spi->xfer->dma_flag = 0U;
- if (spi->dma_use == SPI_DMA_USE_TX_RX) {
- #if (__DCACHE_PRESENT == 1U)
- // Is DChache enabled
- if ((SCB->CCR & SCB_CCR_DC_Msk) != 0U) {
- if ((((uint32_t)data_out & 0x1FU) == 0) &&
- (((uint32_t)data_in & 0x1FU) == 0) &&
- ((num & 0x1FU) == 0)) {
- // Data is 32Byte aligned and size is n *32 -> DMA can be used
- spi->xfer->dma_flag = 1U;
- SCB_CleanDCache_by_Addr ((uint32_t *)((uint32_t)data_out), (int32_t)(num * spi->xfer->dataSize));
- }
- } else {
- spi->xfer->dma_flag = 1U;
- }
- #else
- spi->xfer->dma_flag = 1U;
- #endif
- }
- #ifdef __SPI_DMA
- if (spi->xfer->dma_flag != 0U) {
- // DMA mode
- stat = HAL_SPI_TransmitReceive_DMA (spi->h, (uint8_t *)(uint32_t)data_out, (uint8_t *)(uint32_t)data_in, (uint16_t)num);
- } else
- #endif
- {
- // Interrupt mode
- stat = HAL_SPI_TransmitReceive_IT (spi->h, (uint8_t *)(uint32_t)data_out, (uint8_t *)(uint32_t)data_in, (uint16_t)num);
- }
- }
这就是为什么我也联系了 Keil 的支持。在我看来,通过 SPI 只能发送 32 的倍数的数据量是没有意义的。
但是KEIL反复告诉我不能用DMA和D-Cache通过SPI发送长度不等于32的倍数的数据!
在我看来,我可以通过以下方式做到这一点:
定义 TX 和 RX 缓冲区:
- 尺寸(size)是32的倍数。
- 分配给 32 字节地址。
- 链接到相应的 DMA/RAM 区域。
将 TX 数据复制到 TX 缓冲区。字节数 (
num ) 必须小于或等于缓冲区大小 (
size )。
SCB_CleanDCache_by_Addr ((uint32_t *)((uint32_t)txBuffer), (int32_t)(
大小));
HAL_SPI_TransmitReceive_DMA( ...txBuffer, rxBuffer,
num );
我也测试过,它似乎有效。
我错过了什么吗?