• 关于2440的裸跑程序中SD卡读后不能成功写入问题的讨论


    问题描述:  

      TQ2440的官方裸跑程序中,对SD卡先进行读操作,然后再写,发现不能程序卡死。倘若对SD卡先写后读,程序可以正常运行,奇哉怪哉?

    写数据的关键代码-->

    while(i < BlockSize)
    {         
        //开始传递数据到缓冲区
        status=rSDIFSTA;
        if((status&0x2000)==0x2000)
        { //如果发送FIFO可用,即FIFO未满
            rSDIDAT = *TxBuffer;
            TxBuffer++;
            i++;    
        }
    }

    调试与问题分析:

      调试的时候发现,当不能在写的时候,FIFO available detect for Tx (TFDET)为0,也即是说是fifo满了。此时,程序循环了16次(i=0x10)。循环一次写入4个字节,16次刚好是fifo的最大容量64字节。这证明了,写入fifo中的数据,本应该发送给SD卡,腾空fifo以供用户继续写,却被搁置在fifo中,有进无出。就像下水道中转站被堵,上游的污水就不能继续排放一行的道理。

        先写后读是可以正常工作的,我打印了执行写函数之后部分寄存器的值,如左图所示。可以发现写后的寄存器rSDIDCNT、rSDIDSTA都恢复到了初始值。右图是执行读函数之后寄存器的值,可以发现执行读函数之后,rSDIDCNT、rSDIDSTA都没有回到初始值,都仍然停留在读函数执行的状态中。也就是说,读函数没有执行彻底,SDMMC模块没有进入到空闲状态。在没有准备好的情况下,继续进行写操作,是不可能成功的。

    修复

      修复的方法主要是无论读操作,还是写操作,都确认SDIO总线空闲时,然后再才退出当前的函数。这样可以保证在随后的操作中,SDMMC模块处于准备好的状态,而非遗留状态。

    读函数 

    /**********************************************************************************
    功  能:该函数用于从SD卡中读出指定块起始地址的单个数据块
    参  数:
     U32  Addr  被读块的起始地址
     U8* RxBuffer 用于接收读出数据的缓冲区
    返回值:
     0 读块操作不成功
     1 读块操作成功
    举  例:
     在主调函数中定义一个数组作为接收缓冲区,如U8 Rx_buffer[BlockSize];
     然后开始调用Read_One_Block(addr,Rx_buffer);
    **********************************************************************************/
    U8 Read_One_Block(U32 Addr,U8 * RxBuffer)
    {
        U16 i=0;
        U32 status=0;
        U16 BlockSize;                        //定义块大小
        U16 BlockNumber;
        
        BlockSize=1 << SDCard_BlockSize;     //以byte为单位
        BlockNumber = ((Addr >>  SDCard_BlockSize) + 1) &0x0fff;
        
        rSDIDTIMER=0x7fffff;                // Set timeout count
        rSDIBSIZE=0x200;                    // 512byte(128word)
        rSDIFSTA=rSDIFSTA|(1<<16);            // FIFO reset
        rSDIDCON = (BlockNumber<<0)|(2<<12)|(1<<14)|(1<<16)|(1<<17)|(1<<19)|(0<<22);
        
        while(CMD17(Addr)!=1)                //发送读单个块指令
        {
    #ifdef __SD_MMC_DEBUG__
            Uart_Printf("Send read addr failed!
    ");
    #endif
        }
        
        /* 开始接收数据到缓冲区 */
        while(i<BlockSize)
        { 
            status = rSDIDSTA;
            if(status&0x60)                    //检查是否超时和CRC校验是否出错
            {     
                rSDIDSTA=(0x3<<0x5);         //清除超时标志和CRC错误标志
    #ifdef __SD_MMC_DEBUG__
            Uart_Printf("there is wrong when reading: %s.
    ",status&0x20 ? "time out" :"CRC error");
    #endif
                return 0;
            }        
            status=rSDIFSTA;
            if((status&0x1000)==0x1000)        //如果接收FIFO中有数据
            { 
                *RxBuffer=rSDIDAT;
                RxBuffer++;
                i++;
            }
        status = rSDIDSTA;
        Delay(2);                            //延时2ms
        rSDIDCON=rSDIDCON&~(7<<12);            //结束SDMMC模块的接收
        rSDIDSTA = status;                     //清状态标志位
    
        /* 确认SD卡进入了空闲状态--SDIO总线空闲 */
        rSDIDCON=(0<<18)|(1<<17)|(1<<16)|(1<<14)|(1<<12)|(BlockNumber<<0);
        rSDIDTIMER=0x7fffff;
        status = rSDIDSTA;
        while( !( ((status&0x08)==0x08) | ((status&0x20)==0x20)| ((status&0x800)==0x800) )){
            status=rSDIDSTA;
        }
        
        if( (status&0x20) == 0x20 ){
            rSDIDSTA = status; 
            return 0;
        }    
        rSDIDSTA = status;    
        return 1;
    }
    View Code

    写函数

    /**********************************************************************************
    功  能:该函数用于向SD卡的一个数据块写入数据
    参  数:
     U32  Addr  被写块的起始地址
     U8* TxBuffer 用于发送数据的缓冲区
    返回值:
     0 数据写入操作失败
     1 数据写入操作成功
    举  例:
     在主调函数中定义一个数组作为发送缓冲区,如U8 Tx_buffer[BlockSize];
     然后开始调用Write_One_Block(addr,Tx_buffer);
    **********************************************************************************/
    U8 Write_One_Block(U32 Addr,const U8 * TxBuffer)
    {
        U16 i = 0;
        U32 status = 0;
        U16 BlockSize;                             //定义块大小
        U16 BlockNumber;
        
        BlockSize = 1<< SDCard_BlockSize;         //以byte为单位
        BlockNumber = ((Addr >>  SDCard_BlockSize) +1) &0x0fff;
        
        rSDIDTIMER=0x7fffff;                    // Set timeout count
        rSDIBSIZE=0x200;                        // 512 byte(128 word)
        rSDIFSTA = rSDIFSTA|(1<<16);             // FIFO reset
        rSDIDCON = BlockNumber|(3<<12)|(1<<14)|(1<<16)|(1<<17)|(1<<20)|(0<<22);
    
        while(CMD24(Addr)!=1)                     //发送写单块操作指令
        {
    #ifdef __SD_MMC_DEBUG__
            Uart_Printf("Send write addr failed!
    ");
    #endif
        }
        /* 开始传递数据到缓冲区 */
        while(i < BlockSize)
        {         
            
            status=rSDIFSTA;
            if((status&0x2000)==0x2000)            //如果发送FIFO可用,即FIFO未满
            { 
                rSDIDAT = *TxBuffer;
                TxBuffer++;
                i++;    
            }
        }    
        
        status = rSDIDSTA;
        Delay(5);
        rSDIDCON=rSDIDCON&~(7<<12);                //结束SDMMC模块的发送
        rSDIDSTA = status; 
        
        /* 确认SD卡进入了空闲状态--SDIO总线空闲 */
        rSDIDCON=(0<<18)|(1<<17)|(1<<16)|(1<<14)|(1<<12)|(BlockNumber<<0);
        rSDIDTIMER=0x7fffff;                    // Set timeout count
        status = rSDIDSTA;
        while( !( ((status&0x08)==0x08) | ((status&0x20)==0x20)| ((status&0x800)==0x800) )){
            status=rSDIDSTA;
        }
        
        if( (status&0x20) == 0x20 ){
            rSDIDSTA = status; 
            return 0;
        }    
        rSDIDSTA = status;        
          return 1;
    }
    View Code

    测试效果

      以下操作都得到成功验证:

    • 单块读
    • 单块写
    • 多块读(调用单块读函数实现)
    • 多块写(调用单块写函数实现)
    • 先读后写
    • 先写后读

        移植fatfs文件系统测试:

      大多数操作没有故障出现,偶尔也会出现写函数卡死的情况

    仍然存在的Bug

      1、读函数在确认SDIO总线空闲时候,经常进入超时状态,这导致读函数的速度很慢。

      2、移植fatfs文件系统测试,偶尔也会出现写函数卡死的情况。由于底层驱动运行缓慢,所以文件系统很卡。保存一张

    482KB的bmp文件,耗费了十几秒种。

  • 相关阅读:
    Python类属性的延迟计算
    解析Python编程中的包结构
    解析Python编程中的包结构
    Python查询Mysql时返回字典结构的代码
    VS2010中如何查看DLL的导出接口
    C++ 简单的日志类
    ilmerge工具合并多个DLL或EXE
    基于InstallShield2013LimitedEdition的安装包制作
    c# 操作注册表
    Source Insight 常用设置和快捷键大全
  • 原文地址:https://www.cnblogs.com/amanlikethis/p/3964820.html
Copyright © 2020-2023  润新知