硬汉嵌入式论坛

 找回密码
 立即注册
查看: 6351|回复: 14
收起左侧

[DMA] SPI使用DMA传输问题

[复制链接]

9

主题

57

回帖

84

积分

初级会员

积分
84
发表于 2021-1-6 18:37:59 | 显示全部楼层 |阅读模式
最近在用STM32H7的SPI驱动W5500网络芯片,如果单独的使用阻塞等待的方式(如HAL_SPI_Transmit)就能够成功,但是使用DMA 的方式(如HAL_SPI_Transmit_DMA)就无法正常读写数据(但函数返回的状态是HAL_OK),我也做了数据一致性处理(SCB_CleanDCache_by_Addr和SCB_InvalidateDCache_by_Addr),研究了很久也没找到问题所在,请问这是什么原因导致的呀?
__attribute__((section (".RAM_D3"))) uint8_t SPI_DMATXBUF[DATA_BUF_SIZE];
void SPI_DMA_WRITE(uint8_t* Addref, uint8_t* pTxBuf, uint16_t tx_len)
{
        uint16_t i;
        osEvent event;
        uint8_t res = HAL_ERROR;
        uint32_t timer = 0;
        while(HAL_SPI_GetState(&hspi2) != HAL_SPI_STATE_READY);
        memset(SPI_DMATXBUF, 0, tx_len + 3);


        SPI_DMATXBUF[0] = Addref[0];
        SPI_DMATXBUF[1] = Addref[1];
        SPI_DMATXBUF[2] = Addref[2];


        for(i=0; i<tx_len; i++)
                SPI_DMATXBUF[3 + i] = pTxBuf;
        WIZCHIP.CS._select();
#if (ENABLE_SPI_DMA_CACHE_MAINTENANCE == 1)   
        uint32_t alignedAddr;
        alignedAddr = (uint32_t)SPI_DMATXBUF &  ~0x1F;
        SCB_CleanDCache_by_Addr((uint32_t*)alignedAddr, tx_len + 3 + ((uint32_t)SPI_DMATXBUF - alignedAddr));
#endif
        res = HAL_SPI_Transmit_DMA(&hspi2,(uint8_t*)SPI_DMATXBUF,tx_len + 3);
        if(res == HAL_OK)
        {
                timer = osKernelSysTick() + SPI_DMA_TIMEOUT;
                while(timer > osKernelSysTick())
                {
                        if(HAL_SPI_GetState(&hspi2) == HAL_SPI_STATE_READY)
                        {
                                event = osMessageGet(SPIQueueID, SPI_DMA_TIMEOUT);
                                if(event.status == osEventMessage && event.value.v == WRITE_CPLT_MSG)
                                {
                                        res = HAL_OK;
                                        break;
                                }
                        }
                }
        }
        WIZCHIP.CS._deselect();
}

void SPI_DMA_READ(uint8_t* Addref, uint8_t* pRxBuf, uint16_t rx_len)
{
        osEvent event;
        uint8_t res = HAL_ERROR;
        uint32_t timer = 0;
        while(HAL_SPI_GetState(&hspi2) != HAL_SPI_STATE_READY);
        memset(SPI_DMATXBUF, 0, rx_len+3);
        memset(SPI_DMARXBUF, 0, rx_len+3);
        SPI_DMATXBUF[0] = Addref[0];
        SPI_DMATXBUF[1] = Addref[1];
        SPI_DMATXBUF[2] = Addref[2];
        WIZCHIP.CS._select();
        SPI_DMA_WRITE(Addref,NULL,0);
        res = HAL_SPI_Receive_DMA(&hspi2,SPI_DMARXBUF,rx_len);
        if(res == HAL_OK)
        {
                timer = osKernelSysTick() + SPI_DMA_TIMEOUT;
                while(timer > osKernelSysTick())
                {
                        if(HAL_SPI_GetState(&hspi2) == HAL_SPI_STATE_READY)
                        {
                                event = osMessageGet(SPIQueueID, SPI_DMA_TIMEOUT);
                                if(event.status == osEventMessage && event.value.v == READ_CPLT_MSG)
                                {
#if (ENABLE_SPI_DMA_CACHE_MAINTENANCE == 1)   
                                        uint32_t alignedAddr;
                                        alignedAddr = (uint32_t)SPI_DMARXBUF & ~0x1F;
                                        SCB_InvalidateDCache_by_Addr((uint32_t*)alignedAddr, rx_len + ((uint32_t)SPI_DMARXBUF - alignedAddr));
#endif
                                        res = HAL_OK;
                                }
                                break;
                        }
                }
        }
        WIZCHIP.CS._deselect();
        memcpy(pRxBuf, SPI_DMARXBUF, rx_len);
}

回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
发表于 2021-1-7 08:18:41 | 显示全部楼层
1、首先把Cache处理函数
     uint32_t alignedAddr;
        alignedAddr = (uint32_t)SPI_DMATXBUF &  ~0x1F;
        SCB_CleanDCache_by_Addr((uint32_t*)alignedAddr, tx_len + 3 + ((uint32_t)SPI_DMATXBUF - alignedAddr));
   改成用
      SCB_CleanInvalidateDCache();

2、然后收发函数都改成用HAL_SPI_TransmitReceive_DMA
回复

使用道具 举报

9

主题

57

回帖

84

积分

初级会员

积分
84
 楼主| 发表于 2021-1-7 08:39:26 | 显示全部楼层
eric2013 发表于 2021-1-7 08:18
1、首先把Cache处理函数
     uint32_t alignedAddr;
        alignedAddr = (uint32_t)SPI_DMATXBUF &   ...

谢谢eric大佬的回复,使用HAL_SPI_TransmitReceive_DMA就成功了,为什么使用单独的发送和接收会失败呢?
回复

使用道具 举报

210

主题

1042

回帖

1682

积分

至尊会员

More we do, more we can do.

积分
1682
发表于 2021-1-7 09:12:17 | 显示全部楼层
楼主可以做读写测试,或者抓波形。
SPI全双工分别收或发函数能不能行,时间太久不记得了,用收发同时的函数肯定没错。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
发表于 2021-1-7 09:34:14 | 显示全部楼层
烟花易冷~ 发表于 2021-1-7 08:39
谢谢eric大佬的回复,使用HAL_SPI_TransmitReceive_DMA就成功了,为什么使用单独的发送和接收会失败呢?

像SPI Flash这种,也是必须要收发全上的。有些可以仅发送,像DAC8563,ST7789
回复

使用道具 举报

9

主题

57

回帖

84

积分

初级会员

积分
84
 楼主| 发表于 2021-1-7 09:43:38 | 显示全部楼层
eric2013 发表于 2021-1-7 09:34
像SPI Flash这种,也是必须要收发全上的。有些可以仅发送,像DAC8563,ST7789

好的,感谢解答疑惑!
回复

使用道具 举报

9

主题

57

回帖

84

积分

初级会员

积分
84
 楼主| 发表于 2021-1-7 09:44:40 | 显示全部楼层
emwin 发表于 2021-1-7 09:12
楼主可以做读写测试,或者抓波形。
SPI全双工分别收或发函数能不能行,时间太久不记得了,用收发同时的函 ...

是的,收发一起用就可以通信成功
回复

使用道具 举报

9

主题

57

回帖

84

积分

初级会员

积分
84
 楼主| 发表于 2021-1-7 13:57:36 | 显示全部楼层
eric2013 发表于 2021-1-7 08:18
1、首先把Cache处理函数
     uint32_t alignedAddr;
        alignedAddr = (uint32_t)SPI_DMATXBUF &   ...

我测试了一下,换成 SCB_CleanInvalidateDCache()后传输速度满了一半多,应该是DCache刷新范围太大了
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
发表于 2021-1-8 08:40:15 | 显示全部楼层
烟花易冷~ 发表于 2021-1-7 13:57
我测试了一下,换成 SCB_CleanInvalidateDCache()后传输速度满了一半多,应该是DCache刷新范围太大了

功能正常了没
回复

使用道具 举报

9

主题

57

回帖

84

积分

初级会员

积分
84
 楼主| 发表于 2021-1-8 09:52:18 | 显示全部楼层

现在功能正常了,W5500配置100MB模式,传输速度受SPI影响,TCP通信能达到2MByte每秒
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
发表于 2021-1-9 08:12:40 | 显示全部楼层
烟花易冷~ 发表于 2021-1-8 09:52
现在功能正常了,W5500配置100MB模式,传输速度受SPI影响,TCP通信能达到2MByte每秒

正常了,说明确实是Cache问题。
回复

使用道具 举报

9

主题

57

回帖

84

积分

初级会员

积分
84
 楼主| 发表于 2021-1-9 08:30:33 | 显示全部楼层
eric2013 发表于 2021-1-9 08:12
正常了,说明确实是Cache问题。

是的,换成SCB_CleanInvalidateDCache()速度只有几百KByte
回复

使用道具 举报

9

主题

28

回帖

55

积分

初级会员

积分
55
发表于 2021-6-21 10:08:30 | 显示全部楼层
楼主的spi dma配置是什么?我配置的spi也是同样问题,不用dma就是正常的,用了dma以后就不能正常发送了
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
发表于 2021-6-21 14:33:50 | 显示全部楼层
chaqs3 发表于 2021-6-21 10:08
楼主的spi dma配置是什么?我配置的spi也是同样问题,不用dma就是正常的,用了dma以后就不能正常发送了

应该是DMA配置还有点问题。
另外注意HAL库使用最新版的HAL
回复

使用道具 举报

58

主题

267

回帖

446

积分

高级会员

积分
446
发表于 2021-9-14 22:11:10 | 显示全部楼层
eric2013 发表于 2021-1-7 08:18
1、首先把Cache处理函数
     uint32_t alignedAddr;
        alignedAddr = (uint32_t)SPI_DMATXBUF &   ...

碰到一个跟楼主类似但又不相同的问题。
用 SPI 轮询模式通信正常,换成 DMA 后,示波器查看发出去的数据,和返回的数据均正确。
在 DMA 函数中跟踪代码,开启 DMA 传输的一瞬间,SPI 的 RXDR 寄存器中的数据也正确,但就是 DMA 接收缓存区的数据变成了 0。传输之前不是 0,开始传输后,寄存器中的数据是正确的,但是缓存区的数据变成了 0。

这是什么情况。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-4-28 12:54 , Processed in 0.198864 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表