OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 20618|回复: 34

SPI+DMA方式驱动SD卡

  [复制链接]

15

主题

28

帖子

1

精华

中级会员

Rank: 3Rank: 3

积分
277
金钱
277
注册时间
2014-12-12
在线时间
30 小时
发表于 2016-4-9 10:05:57 | 显示全部楼层 |阅读模式
本帖最后由 JM哆啦a梦 于 2016-4-9 10:09 编辑

使用DMA方式传送数据可以提高传送速度,提高CPU的使用效率。程序的核心部分就是将之前需要CPU搬运大量数据的地方换成DMA的方式实现。战舰开发板上配套的程序修上改修如下:
1.void SD_SPI_Init(void);函数 DMAinit.png

2.u8 SD_RecvData(u8*buf,u16 len);////从sd卡读取一个数据包的内容。
read.png
3.u8 SD_SendBlock(u8*buf,u8 cmd);//向sd卡写入一个数据包的内容 512字节。
write.png

4.上面涉及到的3个与DMA有关的函数都放在了dma.c文件中,直接添加到工程中即可。

换成DMA方式后究竟速度能提高多少?复制同一个文件:
无DMA模式:

无DMA方式

无DMA方式

DMA模式:

DMA方式

DMA方式

复制不同的文件速度会有不同,反正就是使用DMA方式速度能够提高好多好多。大家可以尝试显示一张JPG、BMP图片,效果会很明显(本人已验证过)。附上代码:





实验50 USB读卡器实验SPI DMA驱动SD(支持4G以上的SD卡).zip

1013.86 KB, 下载次数: 6098

代码

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165371
金钱
165371
注册时间
2010-12-1
在线时间
2110 小时
发表于 2016-4-9 20:58:44 | 显示全部楼层
回复 支持 1 反对 0

使用道具 举报

15

主题

786

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3223
金钱
3223
注册时间
2015-7-26
在线时间
811 小时
发表于 2016-4-9 10:48:14 | 显示全部楼层
谢谢分享!
回复 支持 反对

使用道具 举报

2

主题

13

帖子

0

精华

初级会员

Rank: 2

积分
55
金钱
55
注册时间
2016-3-22
在线时间
8 小时
发表于 2016-4-9 17:10:35 | 显示全部楼层
mark,回去用用
回复 支持 反对

使用道具 举报

5

主题

74

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
329
金钱
329
注册时间
2015-9-11
在线时间
46 小时
发表于 2016-4-9 19:27:29 | 显示全部楼层
取走了!
回复 支持 反对

使用道具 举报

fantasea 该用户已被删除
发表于 2016-4-15 21:36:41 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

0

主题

13

帖子

0

精华

初级会员

Rank: 2

积分
71
金钱
71
注册时间
2015-5-9
在线时间
11 小时
发表于 2016-8-1 15:42:27 | 显示全部楼层
多谢楼主分享
回复 支持 反对

使用道具 举报

0

主题

3

帖子

0

精华

初级会员

Rank: 2

积分
87
金钱
87
注册时间
2017-12-31
在线时间
27 小时
发表于 2016-8-1 18:20:17 | 显示全部楼层
make,down下来研究
回复 支持 反对

使用道具 举报

0

主题

12

帖子

0

精华

新手上路

积分
31
金钱
31
注册时间
2016-8-4
在线时间
6 小时
发表于 2016-8-6 07:14:25 | 显示全部楼层
这个好,不知道换一个高速卡速度还能不能更高一些.
回复 支持 反对

使用道具 举报

2

主题

12

帖子

0

精华

新手上路

积分
36
金钱
36
注册时间
2016-7-28
在线时间
7 小时
发表于 2016-8-6 17:23:01 | 显示全部楼层
在09年就有前辈讨论spi+dma接收失败的问题  
当时的解决方案也是用双dma通道同时启动,发送通道提供时钟信号
回复 支持 反对

使用道具 举报

0

主题

10

帖子

0

精华

初级会员

Rank: 2

积分
99
金钱
99
注册时间
2014-8-21
在线时间
15 小时
发表于 2016-8-6 17:43:08 | 显示全部楼层
DMA 确实可以减轻cpu
回复 支持 反对

使用道具 举报

1

主题

35

帖子

0

精华

初级会员

Rank: 2

积分
101
金钱
101
注册时间
2013-10-18
在线时间
20 小时
发表于 2016-8-7 22:33:23 | 显示全部楼层
谢谢分享
回复 支持 反对

使用道具 举报

2

主题

13

帖子

0

精华

初级会员

Rank: 2

积分
76
金钱
76
注册时间
2016-8-3
在线时间
11 小时
发表于 2016-8-8 15:47:16 | 显示全部楼层
学习学习
回复 支持 反对

使用道具 举报

1

主题

43

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
232
金钱
232
注册时间
2016-8-9
在线时间
29 小时
发表于 2016-8-21 20:35:41 | 显示全部楼层
看看,参考下。谢谢楼主
回复 支持 反对

使用道具 举报

4

主题

211

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2600
金钱
2600
注册时间
2016-7-6
在线时间
546 小时
发表于 2016-8-21 23:43:06 | 显示全部楼层
谢谢分享!
回复 支持 反对

使用道具 举报

18

主题

103

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
350
金钱
350
注册时间
2016-8-2
在线时间
78 小时
发表于 2016-8-31 10:46:07 | 显示全部楼层
参照楼主的代码写SPI FLASH,用DMA方式,在读数据时候始终无法读取数据,发送数据部分正常,纠结ing,望楼主指点迷津,我用的是STM32F103
回复 支持 反对

使用道具 举报

1

主题

7

帖子

0

精华

初级会员

Rank: 2

积分
66
金钱
66
注册时间
2015-12-1
在线时间
6 小时
发表于 2016-9-2 21:09:35 | 显示全部楼层
good ,thank you for your shared
回复 支持 反对

使用道具 举报

39

主题

131

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1439
金钱
1439
注册时间
2015-12-26
在线时间
208 小时
发表于 2016-10-30 01:17:45 | 显示全部楼层
不错,谢谢楼主的分享!
回复 支持 反对

使用道具 举报

15

主题

70

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
307
金钱
307
注册时间
2013-11-4
在线时间
112 小时
发表于 2016-11-9 10:04:50 | 显示全部楼层
多谢楼主,取走,参考。
回复 支持 反对

使用道具 举报

1

主题

13

帖子

0

精华

初级会员

Rank: 2

积分
81
金钱
81
注册时间
2016-2-13
在线时间
16 小时
发表于 2017-5-4 16:08:41 | 显示全部楼层
MARK,用DMA方式确实快很多,SPI方式,CLASS 10 的SD卡不用DMA写入800K需要2秒多,用DMA不到1秒~
回复 支持 反对

使用道具 举报

4

主题

59

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
360
金钱
360
注册时间
2017-3-9
在线时间
63 小时
发表于 2017-5-4 17:17:00 | 显示全部楼层
mark            
回复 支持 反对

使用道具 举报

3

主题

36

帖子

0

精华

初级会员

Rank: 2

积分
68
金钱
68
注册时间
2017-3-28
在线时间
29 小时
发表于 2017-8-24 14:25:32 | 显示全部楼层
支持,下载您的资料了,谢谢!
回复 支持 反对

使用道具 举报

51

主题

2166

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
10653
金钱
10653
注册时间
2017-4-14
在线时间
2780 小时
发表于 2018-4-27 15:29:23 | 显示全部楼层
//向sd卡写入一个数据包的内容 512字节 启动DMA传输
//buf:数据缓存区
u8 DMA1_Star_SPI_TX(u8 *buffer)
{
                DMA1_Channel5->CNDTR=512; //设置要传输的数据长度
                DMA1_Channel5->CMAR=(uint32_t)buffer; //设置RAM缓冲区地址
               
                DMA_Cmd(DMA1_Channel5,ENABLE); //启动DMA传输 RAM->SPI
                while(!DMA_GetFlagStatus(DMA1_FLAG_TC5)); //等待DMA通道5传输完成
                DMA_ClearFlag(DMA1_FLAG_TC5); //清除通道5传输完成状态标记
                DMA_Cmd(DMA1_Channel5,DISABLE); //使DMA通道5停止工作
          return 0;
}

看你的tx代码,while(!DMA_GetFlagStatus(DMA1_FLAG_TC5));  cpu一直在这里等待,用dma还有啥意义呢?

回复 支持 反对

使用道具 举报

49

主题

341

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
5259
金钱
5259
注册时间
2012-8-25
在线时间
1025 小时
发表于 2018-4-27 15:50:22 | 显示全部楼层
275891381 发表于 2018-4-27 15:29
//向sd卡写入一个数据包的内容 512字节 启动DMA传输
//buf:数据缓存区
u8 DMA1_Star_SPI_TX(u8 *buffer)
...

dma方式不用消耗循环时间,循环量很大时很可观。
dma方式中断不停,如果要大量中断,循环方式中断的时候停止
有系统的时候,系统切换到别的任务,dma不会停,而循环方式停止
回复 支持 反对

使用道具 举报

51

主题

2166

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
10653
金钱
10653
注册时间
2017-4-14
在线时间
2780 小时
发表于 2018-4-27 16:32:58 | 显示全部楼层
gotofly21 发表于 2018-4-27 15:50
dma方式不用消耗循环时间,循环量很大时很可观。
dma方式中断不停,如果要大量中断,循环方式中断的时候 ...

明白了,上系统就有效果了,谢谢
回复 支持 反对

使用道具 举报

3

主题

28

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
229
金钱
229
注册时间
2016-1-4
在线时间
17 小时
发表于 2018-7-27 20:04:24 | 显示全部楼层
gotofly21 发表于 2018-4-27 15:50
dma方式不用消耗循环时间,循环量很大时很可观。
dma方式中断不停,如果要大量中断,循环方式中断的时候 ...

你好,你讲的这个不太明白。
while(!DMA_GetFlagStatus(DMA1_FLAG_TC5));  cpu一直在这里等待。
我没用上操作系统,我想用中断的方式判断接受数据完成,然后去取数据慢慢处理。请问是用SPI1的中断服务函数?还是用DMA1_CH2的中断服务函数呢?谢谢指教
回复 支持 反对

使用道具 举报

4

主题

10

帖子

0

精华

初级会员

Rank: 2

积分
51
金钱
51
注册时间
2013-3-19
在线时间
3 小时
发表于 2019-4-23 16:55:13 | 显示全部楼层
等待DMA传输完成时CPU并没有被解放出来,很好奇这么使用DMA的意义何在
回复 支持 反对

使用道具 举报

0

主题

5

帖子

0

精华

初级会员

Rank: 2

积分
179
金钱
179
注册时间
2019-3-30
在线时间
20 小时
发表于 2019-5-11 09:31:09 | 显示全部楼层
试试看,
回复 支持 反对

使用道具 举报

3

主题

32

帖子

0

精华

初级会员

Rank: 2

积分
126
金钱
126
注册时间
2018-8-11
在线时间
28 小时
发表于 2019-9-18 14:47:52 | 显示全部楼层
您好 您上面说这个是战舰的案例,刷到战舰板上,显示读取SD error。什么情况???
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手入门

积分
12
金钱
12
注册时间
2021-2-6
在线时间
5 小时
发表于 2021-2-12 09:43:02 | 显示全部楼层
你好  自己掿建板的板子  自己写的FAT32  
DMA 方式能读  但不能写入   SPI 方式正常   不知什么原因
回复 支持 反对

使用道具 举报

15

主题

118

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3059
金钱
3059
注册时间
2015-12-20
在线时间
299 小时
发表于 2021-3-26 09:26:14 | 显示全部楼层
学习了。
回复 支持 反对

使用道具 举报

0

主题

3

帖子

0

精华

新手上路

积分
41
金钱
41
注册时间
2018-4-14
在线时间
10 小时
发表于 2021-4-21 16:45:13 | 显示全部楼层
lihail1
回复 支持 反对

使用道具 举报

0

主题

8

帖子

0

精华

初级会员

Rank: 2

积分
55
金钱
55
注册时间
2021-3-5
在线时间
14 小时
发表于 2021-8-31 20:55:26 | 显示全部楼层
感谢分享
回复 支持 反对

使用道具 举报

1

主题

7

帖子

0

精华

新手上路

积分
30
金钱
30
注册时间
2020-12-3
在线时间
6 小时
发表于 2022-8-23 22:18:00 | 显示全部楼层
HAL咋实现?
void SPI1_DMARX_BUF(u8 *buffer,u16 len)
{
        u8 temp=0xff;
        //使能spi的dma发送接收请求
        SPI1_Handler.Instance->CR2 |= SPI_CR2_TXDMAEN;
        SPI1_Handler.Instance->CR2 |= SPI_CR2_RXDMAEN;
       
        SPI1_RXDMA_Handler.Instance->CNDTR=len; //设置传输的数据长度
        SPI1_RXDMA_Handler.Instance->CMAR=(uint32_t)buffer; //设置内存缓冲区地址
        SPI1_RXDMA_Handler.Instance->CPAR = (uint32_t)(&SPI1_Handler.Instance->DR);  
       
       
        /*SPI作为主机进行数据接收时必须要主动产生时钟,因此此处必须有DMA通道3的配合*/
        SPI1_TXDMA_Handler.Instance->CNDTR=len;
        SPI1_TXDMA_Handler.Instance->CMAR=(uint32_t)(&temp); //temp=0xff
        SPI1_TXDMA_Handler.Instance->CCR&=~DMA_MINC_ENABLE; //内存地址非自增
        SPI1_TXDMA_Handler.Instance->CPAR = (uint32_t)(&SPI1_Handler.Instance->DR);
       
        //首先启动DMA通道3
        //__HAL_DMA_ENABLE(&SPI1_RXDMA_Handler);
        SPI1_TXDMA_Handler.Instance->CCR |= DMA_CCR_EN;

        //再启动DMA通道2
        //__HAL_DMA_ENABLE(&SPI1_TXDMA_Handler);
        SPI1_RXDMA_Handler.Instance->CCR |= DMA_CCR_EN;

        //等待DMA通道2接收数据完成
        while(!__HAL_DMA_GET_FLAG(&SPI1_RXDMA_Handler,DMA_FLAG_TC2));

        //清除DMA通道2与3的传输完成标志
        __HAL_DMA_CLEAR_FLAG(&SPI1_TXDMA_Handler,DMA_FLAG_TC2);
        __HAL_DMA_CLEAR_FLAG(&SPI1_RXDMA_Handler,DMA_FLAG_TC3);

        //使DMA通道2与3停止工作
        //__HAL_DMA_DISABLE(&SPI1_TXDMA_Handler);
        //__HAL_DMA_DISABLE(&SPI1_RXDMA_Handler);
        SPI1_TXDMA_Handler.Instance->CCR &= ~DMA_CCR_EN;
        SPI1_RXDMA_Handler.Instance->CCR &= ~DMA_CCR_EN;

        SPI1_TXDMA_Handler.Instance->CCR|=DMA_MINC_ENABLE; //将DMA通道3恢复为内存地址自增方式
}
读取函数改成这个样子不知道那里有问题,试了不行
回复 支持 反对

使用道具 举报

1

主题

7

帖子

0

精华

新手上路

积分
30
金钱
30
注册时间
2020-12-3
在线时间
6 小时
发表于 2022-8-24 09:18:52 | 显示全部楼层
使用hal库的时候重写读写函数
void SPI1_DMARX_BUF(u8 *buffer,u16 len)
{
        u8 temp=0xff;
    //使能spi的dma发送接收请求
    SPI1_Handler->Instance->CR2 |= SPI_CR2_TXDMAEN;
        SPI1_Handler->Instance->CR2 |= SPI_CR2_RXDMAEN;       

        DMA1_Channel2->CNDTR=len; //设置传输的数据长度
        DMA1_Channel2->CMAR=(uint32_t)buffer; //设置内存缓冲区地址
        DMA1_Channel2->CPAR = (uint32_t)&SPI1_Handler.Instance->DR;
       
       
        /*SPI作为主机进行数据接收时必须要主动产生时钟,因此此处必须有DMA通道3的配合*/
        DMA1_Channel3->CNDTR=len;
        DMA1_Channel3->CMAR=(uint32_t)&temp; //temp=0xff
        DMA1_Channel3->CCR&=~DMA_MINC_ENABLE; //内存地址非自增
        DMA1_Channel3->CPAR = (uint32_t)&SPI1_Handler.Instance->DR;
       
    //首先启动DMA通道3
        //__HAL_DMA_ENABLE(&SPI1_RXDMA_Handler);
        DMA1_Channel3->CCR |= DMA_CCR_EN;

    //再启动DMA通道2
        //__HAL_DMA_ENABLE(&SPI1_TXDMA_Handler);
        DMA1_Channel2->CCR |= DMA_CCR_EN;
       
        //SPI1_Handler.Instance->CR1 |= SPI_CR1_SPE;
       
    //等待DMA通道2接收数据完成
        while(!__HAL_DMA_GET_FLAG(&SPI1_RXDMA_Handler,DMA_FLAG_TC2));

         //清除DMA通道4与5的传输完成标志
        __HAL_DMA_CLEAR_FLAG(&SPI1_TXDMA_Handler,DMA_FLAG_TC2);
        __HAL_DMA_CLEAR_FLAG(&SPI1_RXDMA_Handler,DMA_FLAG_TC3);
       
         //使DMA通道2与3停止工作
        //__HAL_DMA_DISABLE(&SPI1_TXDMA_Handler);
        //__HAL_DMA_DISABLE(&SPI1_RXDMA_Handler);
    DMA1_Channel3->CCR &= ~DMA_CCR_EN;
    DMA1_Channel2->CCR &= ~DMA_CCR_EN;
       
        DMA1_Channel3->CCR|=DMA_MINC_ENABLE; //将DMA通道3恢复为内存地址自增方式

}
这样写不知道哪里有问题,读不到sd卡数据
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-2-26 06:32

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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