17003|23

81

帖子

0

TA的资源

一粒金砂(初级)

楼主
 

spi在dma模式下读写sd卡 [复制链接]

请教大家,
 /****************************************************************************
Desripton   :read block in dma mode
Fuctionname :ReadBlockInDMA()
Input        :None

Return      :None
*****************************************************************************/
u8 ReadBlockInDMA(u8 *pBuffer, u32 ReadAddr, u16 NumByteToRead)
{
    //u32 i = 0;
    DMA_InitTypeDef  DMA_InitStructure;
    u8 rvalue = MSD_RESPONSE_FAILURE;
  
    /* MSD chip select low */
    MSD_CS_LOW();
    

    /*initial dma channel 2*/
    DMA_DeInit(DMA_Channel2);

    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)SPI1_DR_Address;
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)pBuffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = NumByteToRead;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA_Channel2, &DMA_InitStructure);
    
    
    SPI_DMACmd(SPI1, SPI_DMAReq_Rx, ENABLE);
   
    /* Send CMD17 (MSD_READ_SINGLE_BLOCK) to read one block */
    
    

    MSD_SendCmd(MSD_READ_SINGLE_BLOCK, ReadAddr, 0xFF);
    if (!MSD_GetResponse(MSD_RESPONSE_NO_ERROR))
    {
    /* Now look for the data token to signify the start of the data */
        
        if (!MSD_GetResponse(MSD_START_DATA_SINGLE_BLOCK_READ))
        {
                

          
            DMA_Cmd(DMA_Channel2,ENABLE);
            
            while(!DMA_GetFlagStatus(DMA_FLAG_TC2))
            {
                
            }

        }    
    }
    /* Get CRC bytes (not really needed by us, but required by MSD) */
  
    MSD_ReadByte();
    MSD_ReadByte();

    /* Set response value to success */
    rvalue = MSD_RESPONSE_NO_ERROR;
    DMA_Cmd(DMA_Channel2, DISABLE);
    /* MSD chip select high */
    MSD_CS_HIGH();

    /* Send dummy byte: 8 Clock pulses of delay */
    MSD_WriteByte(DUMMY);


    /* Returns the reponse */
    return rvalue;
}

/******************************************************************************
Description  : Write byte in dma mode
Fuctionname  : WriteBlockInDMA(u8 *pBuffer,u32 WriteAddr, u16 NumByteToWrite)
Input         : pBuffer -- data buffer for send
               WriteAddr -- address to write
               NumByteToWrite -- data length of the data to write
Output       : None

*********************************************************************************/
u8 WriteBlockInDMA(u8 *pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
    //u32 i = 0;
    DMA_InitTypeDef  DMA_InitStructure;
    u8 rvalue = MSD_RESPONSE_FAILURE;

    /* MSD chip select low */
    MSD_CS_LOW();

    /*initial the spi1 in write mode*/
    /*initial dma channel 3*/
    DMA_DeInit(DMA_Channel3);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)SPI1_DR_Address;
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)pBuffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = NumByteToWrite;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA_Channel3, &DMA_InitStructure);
    SPI_DMACmd(SPI1, SPI_DMAReq_Tx, ENABLE);
   // SPI_Cmd(SPI1, ENABLE);
    MSD_SendCmd(MSD_SET_BLOCKLEN,512,0x00); 
    MSD_ReadByte();
    MSD_ReadByte();
    /* Send CMD24 (MSD_WRITE_BLOCK) to write multiple block */
    MSD_SendCmd(MSD_WRITE_BLOCK, WriteAddr, 0xFF);

    /* Check if the MSD acknowledged the write block command: R1 response (0x00: no errors) */
    if (!MSD_GetResponse(MSD_RESPONSE_NO_ERROR))
    {
         /* Send a dummy byte */
        MSD_WriteByte(DUMMY);
        /* Send the data token to signify the start of the data */
        MSD_WriteByte(0xFE);
        /* Write the block data to MSD : write count data by block */
        DMA_Cmd(DMA_Channel3, ENABLE);
        while(!DMA_GetFlagStatus(DMA_FLAG_TC3));
        /* Put CRC bytes (not really needed by us, but required by MSD) */
        MSD_ReadByte();
        MSD_ReadByte();
        /* Read data response */
        if (MSD_GetDataResponse() == MSD_DATA_OK)
        {
            rvalue = MSD_RESPONSE_NO_ERROR;
        }
  }
    
    /* MSD chip select high */
    MSD_CS_HIGH();

    /* Send dummy byte: 8 Clock pulses of delay */
    MSD_WriteByte(DUMMY);

    DMA_Cmd(DMA_Channel3, DISABLE);
    /* Returns the reponse */
    return rvalue;
}
  这是分别用dma读写sd卡的读和写一个block的程序,在程序中调用到这俩个程序时,dma读子程序在while(!DMA_GetFlagStatus(DMA_FLAG_TC2)){}这一直等待,也就是说dma传输没有完成。而调用写子程序是完全没问题的,也就是dma时钟等问题以不是问题,不知道还有什么没考虑到的?????
 郁闷!!!!!
       
此帖出自stm32/stm8论坛

最新回复

MarK  详情 回复 发表于 2017-8-19 09:10
点赞 关注(2)
 

回复
举报

18

帖子

0

TA的资源

一粒金砂(初级)

沙发
 

资料很有用!

                                 珍藏
此帖出自stm32/stm8论坛
 
 

回复

68

帖子

0

TA的资源

一粒金砂(初级)

板凳
 

版主帮忙啊。。。

                                  帮主帮忙分析一下,是不是卡的响应时钟与DMA时钟匹配有问题????
此帖出自stm32/stm8论坛
 
 

回复

75

帖子

0

TA的资源

一粒金砂(初级)

4
 

我先自己分析一下

                                   难道是时钟同步问题?因为我们知道读sd卡时每从SPI总线上读一个字节的数据都要先进行时钟同步,这点从MSD_ReadByte()函数可以看出。也就是读一个字节数据前得向spi总线发数据0xff,这是必须的,如果省掉这一步,读数据是不能成功的。问题是不是出在这,也就是说当我们用dma来读SPI->DR中的数据时,这是dma硬件并没有向spi总线发送同步信号,而在SPI->DR中得不到正确的读数而导致dma读取不成功??
此帖出自stm32/stm8论坛
 
 
 

回复

71

帖子

0

TA的资源

一粒金砂(初级)

5
 

补充

                                  而向sd卡写数据时并不需要额外的时钟同步,所以dma写能正确进行。
此帖出自stm32/stm8论坛
 
 
 

回复

78

帖子

0

TA的资源

一粒金砂(中级)

6
 

更正

                                  看来我的理解还是有误,发0xff只是为了启动时钟clock,而实质上dma也是可以启动时钟的。。。。。。问题还是找不到啊。。。。
此帖出自stm32/stm8论坛
 
 
 

回复

75

帖子

0

TA的资源

一粒金砂(初级)

7
 

???

                                 ???????
此帖出自stm32/stm8论坛
 
 
 

回复

66

帖子

0

TA的资源

一粒金砂(初级)

8
 

读时要用两个DMA通道,一个发,一个收。

                                 不发的话无时钟信号,怎么能收到?
此帖出自stm32/stm8论坛

点评

感觉说的有道理。。。  详情 回复 发表于 2012-11-2 12:23
 
 
 

回复

67

帖子

0

TA的资源

一粒金砂(初级)

9
 

不发的话无时钟信号,怎么能收到?

                                    这个spi的dma应该硬件能开启时钟,也就是说DMA_Cmd(DMA_Channel2,ENABLE);后spi就会有时钟信号,但在我这事实却不是这样,开启dma通道2后spi1并没有时钟信号。????
此帖出自stm32/stm8论坛
 
 
 

回复

83

帖子

0

TA的资源

一粒金砂(初级)

10
 

spi时钟

                                 SPI收发是同时进行的,共用一个时钟,主模式发时会开启时钟,收时不会,与DMA无关。DMA只是在接收缓冲区非空或发送缓冲区空时传数据。主模式SPI数据发送过程从“当一字节写进发送缓冲器时,发送过程开始”,即要启动主模式SPI传输,应有一字节写进发送缓冲器,对于发送的DMA操作,当设置好DMA后,因SPI的txe为1,故会触发DMA传一个字节到DR,此操作会开启主模式SPI的发送及接收过程,故“写子程序是完全没问题的”,但对于读操作,如果只设置读的DMA,因触发读DMA的条件是rxne为1,即必须接收缓冲区非空,此条件在只设置读DMA的情况下无法达到,因没有一个字节写进发送缓冲器,也就没有数据收到了,故出现“dma读子程序在while(!DMA_GetFlagStatus(DMA_FLAG_TC2)){}这一直等待”的情况,可见,要完成读DMA操作,也应先写数据到DR中,可通过同时开启另一个DMA通道用于SPI的发送来完成。
此帖出自stm32/stm8论坛
 
 
 

回复

76

帖子

0

TA的资源

一粒金砂(初级)

11
 

我也想到了

    但这样的话也就是在DMA读时,如果我要连续读512字节,也得发512次0xff作为时钟信号。这样用dma还会有速度优势吗?
  
此帖出自stm32/stm8论坛
 
 
 

回复

67

帖子

0

TA的资源

一粒金砂(初级)

12
 

看来以前是对stm32spi的dma时钟理解有误

  感谢10楼,程序修改如下,接受成功,但在11楼提到的问题还是可以继续讨论一下。
/****************************************************************************
Desripton   :read block in dma mode
Fuctionname :ReadBlockInDMA()
Input        :None

Return      :None
*****************************************************************************/
u8 ReadBlockInDMA(u8 *pBuffer, u32 ReadAddr, u16 NumByteToRead)
{
    u32 i = 0;
    DMA_InitTypeDef  DMA_InitStructure;
    u8 rvalue = MSD_RESPONSE_FAILURE;
  
    /* MSD chip select low */
    MSD_CS_LOW();
    

    /*initial dma channel 2*/
    DMA_DeInit(DMA_Channel2);

    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)SPI1_DR_Address;
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)pBuffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = NumByteToRead;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA_Channel2, &DMA_InitStructure);
    
    
    SPI_DMACmd(SPI1, SPI_DMAReq_Rx, ENABLE);
   
    /* Send CMD17 (MSD_READ_SINGLE_BLOCK) to read one block */
    
    

    MSD_SendCmd(MSD_READ_SINGLE_BLOCK, ReadAddr, 0xFF);
    if (!MSD_GetResponse(MSD_RESPONSE_NO_ERROR))
    {
    /* Now look for the data token to signify the start of the data */
        
        if (!MSD_GetResponse(MSD_START_DATA_SINGLE_BLOCK_READ))
        {
                

          
            DMA_Cmd(DMA_Channel2,ENABLE);
            for(i=0 ;i<NumByteToRead; i++)     //发时钟信号,接收同时进  
                                               //行
            {
                SPI1->DR = 0xff;
                while(_SPI_GetFlagStatus(SPI1,SPI_FLAG_TXE) == RESET);
             }
            while(!DMA_GetFlagStatus(DMA_FLAG_TC2))
            {
                
            }

        }    
    }
    /* Get CRC bytes (not really needed by us, but required by MSD) */
  
    MSD_ReadByte();
    MSD_ReadByte();

    /* Set response value to success */
    rvalue = MSD_RESPONSE_NO_ERROR;
    DMA_Cmd(DMA_Channel2, DISABLE);
    /* MSD chip select high */
    MSD_CS_HIGH();

    /* Send dummy byte: 8 Clock pulses of delay */
    MSD_WriteByte(DUMMY);


    /* Returns the reponse */
    return rvalue;
}
此帖出自stm32/stm8论坛
 
 
 

回复

67

帖子

0

TA的资源

一粒金砂(初级)

13
 

速度优势问题

使用DMA并不是为了获得速度,而是充分利用等待的空闲时间,所以我前面说再开启另一个DMA通道用于SPI的发送,发送的同时接收的DMA通道也在工作,完成数据的接收,不需CPU干预,CPU可完成其他任务。要说速度,我就算不用DMA,也照样可在约18026个CPU时钟周期内将512byte数据传到SD卡,或在17234(最快曾达到16401)个CPU时钟周期内将512byte数据从SD卡读到内存,此时速度约为512/(18026/72)=2.049(m/s)、512/(17234/72)=2.13(m/s),已非常接近2.25m/s的理论值。顺便说一下,SD卡的读写速度,除了与此处CPU与SD卡交换数据的速度有关外,还与以下2个操作密切相关:
读时:
    /* Now look for the data token to signify the start of the data */
   if (!MSD_GetResponse(MSD_START_DATA_SINGLE_BLOCK_READ))
写时:
    /* Read data response */
    if (MSD_GetDataResponse() == MSD_DATA_OK)
此2操作花费的时间是惊人的,读时约需等10xxxCPU时钟,接近传512byte数据时间的2/3,写时约需等1xxxxx周期,是传512byte数据时间的10倍以上!仔细思考一下也是合理的,因写时会执行擦除操作,闪存的擦除时间是以ms计的。
此帖出自stm32/stm8论坛
 
 
 

回复

73

帖子

0

TA的资源

一粒金砂(初级)

14
 

节约cpu

  楼上的说得很有道理,通过逻辑分析议抓数据后可以看到 
 /* Now look for the data token to signify the start of the data */
   if (!MSD_GetResponse(MSD_START_DATA_SINGLE_BLOCK_READ))
写时:
    /* Read data response */
    if (MSD_GetDataResponse() == MSD_DATA_OK)确实占用较长的时间,不过接近读512字节的2/3时间还得看不同的卡,但是如果在dma开启后有dma控制器产生时钟信号好像很合理吧,因为放开速度不说,这样在dma开启后还得靠cpu发512字节的时钟信号,这样的操作好像并没有节省多少cpu时间,只有在开启dma后由dma控制器主动发时钟信号,这样在读512字节的数据时完全可以不用cpu来干预,因而才能真正地达到用cpu来处理其它任务的目的,不知道芯片设计的时候是怎么考虑的,当然如果存在其它编程算法而不是像12楼上那种方法来实现那到是有可能的?
此帖出自stm32/stm8论坛
 
 
 

回复

84

帖子

0

TA的资源

一粒金砂(初级)

15
 

开两个DMA通道的目的就是为了让CPU放开手脚干其他事而不用

SPI发送时,开DMA通道2用于将数据从内存搬到DR,
SPI接收时,开DMA通道3用于将数据从DR搬到内存,再开DMA通道2用于将DUMMY搬到DR,此2个DMA一开,数据便源源不断从SPI读入内存,不用CPU干预,更不用
            for(i=0 ;i<NumByteToRead; i++)     //发时钟信号,接收同时进  
                                               //行
            {
                SPI1->DR = 0xff;
                while(_SPI_GetFlagStatus(SPI1,SPI_FLAG_TXE) == RESET);
             }
此帖出自stm32/stm8论坛
 
 
 

回复

76

帖子

0

TA的资源

一粒金砂(初级)

16
 

明白了

                                    就是收的时候并不是只开一个通道2,而是通道2和通道3一起开,发送则只开一个通道即可。
此帖出自stm32/stm8论坛
 
 
 

回复

20

帖子

0

TA的资源

一粒金砂(初级)

17
 

谢谢,程序如下

u8 ReadBlockInDMA(u8 *pBuffer, u32 ReadAddr, u16 NumByteToRead)
{
    //u32 i = 0;
    //u16 j = 0; 
    DMA_InitTypeDef  DMA_InitStructure;
    u8 rvalue = MSD_RESPONSE_FAILURE;
   // u16 *Enp1Addr = NULL;   /*断点1地址*/
    /* MSD chip select low */
    MSD_CS_LOW();
    

    /*initial dma channel 2*/
    DMA_DeInit(DMA_Channel2);
    DMA_DeInit(DMA_Channel3);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)SPI1_DR_Address;
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)pBuffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = NumByteToRead;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA_Channel2, &DMA_InitStructure);
    
    
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)DuumyClock;  //512字节的dummy
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
    DMA_Init(DMA_Channel3, &DMA_InitStructure);
    SPI_DMACmd(SPI1, SPI_DMAReq_Tx, ENABLE);
   
    SPI_DMACmd(SPI1, SPI_DMAReq_Rx, ENABLE);
//    DMA_ClearFlag(DMA_FLAG_TC2); 
    /* Send CMD17 (MSD_READ_SINGLE_BLOCK) to read one block */
    
    

    MSD_SendCmd(MSD_READ_SINGLE_BLOCK, ReadAddr, 0xFF);
    if (!MSD_GetResponse(MSD_RESPONSE_NO_ERROR))
    {
    /* Now look for the data token to signify the start of the data */
        
        if (!MSD_GetResponse(MSD_START_DATA_SINGLE_BLOCK_READ))
        {    
                       
            DMA_Cmd(DMA_Channel3,ENABLE);   
            DMA_Cmd(DMA_Channel2,ENABLE);
//             for(i=0; i<NumByteToRead; i++)
//             {  
//                
//                SPI1->DR = DUMMY;
//                while((SPI1->SR&SPI_FLAG_TXE) == (u16)RESET);
               // while(SPI_GetFlagStatus(SPI1,SPI_FLAG_TXE) == RESET);
//                while(SPI_GetFlagStatus(SPI1,SPI_FLAG_RXNE) == RESET)
//                {
//
//                }
//                *pBuffer = SPI1->DR;
//                 pBuffer++;
//             }
//          

            
            while(!DMA_GetFlagStatus(DMA_FLAG_TC3));
            
            while(!DMA_GetFlagStatus(DMA_FLAG_TC2));
            
            DMA_ClearFlag(DMA_FLAG_TC2); 

        }    
    }
    /* Get CRC bytes (not really needed by us, but required by MSD) */
  
    MSD_ReadByte();
    MSD_ReadByte();

    /* Set response value to success */
    rvalue = MSD_RESPONSE_NO_ERROR;
    DMA_Cmd(DMA_Channel2, DISABLE);
    DMA_Cmd(DMA_Channel3, DISABLE);
    /* MSD chip select high */
    MSD_CS_HIGH();

    /* Send dummy byte: 8 Clock pulses of delay */
    MSD_WriteByte(DUMMY);


    /* Returns the reponse */
    return rvalue;
}
 优先级很重要,收应高于发。
此帖出自stm32/stm8论坛
 
 
 

回复

69

帖子

0

TA的资源

一粒金砂(初级)

18
 

那个DUMMY一个就够了

                                 无需512字节,设置为地址不增加
此帖出自stm32/stm8论坛
 
 
 

回复

77

帖子

0

TA的资源

一粒金砂(初级)

19
 

实质效果一样

                                    对,这样简单一点,发送512次就行了,谢谢提醒!
此帖出自stm32/stm8论坛
 
 
 

回复

89

帖子

0

TA的资源

一粒金砂(初级)

20
 

好资料,收藏了。

有一个问题请教:

在代码中有
while(!DMA_GetFlagStatus(DMA_FLAG_TC3));
while(!DMA_GetFlagStatus(DMA_FLAG_TC2));
两句,意思是DMA传输如果没有完成就继续等待。那么这样DMA的优势是否就没有体现出来呢?

DMA的本意应该是把CPU从数据传输的忙等待中解放出来,可以腾出时间来做其他事情,但是如果向上面那样使用不是好像就和直接CPU忙等待读取一样了吗?因为while循环使得CPU还是在等着DMA传输完毕。

不知道我说得对不对。是不是DMA要配合操作系统的任务管理加上阻塞才有它该有的性能优势呢?谢谢!
此帖出自stm32/stm8论坛
 
 
 

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

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表