单片机
返回首页

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

2017-01-17 来源:eefocus

问题描述:  

  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!\n');

#endif

    }

    

    /* 开始接收数据到缓冲区 */

    while(i

    { 

        status = rSDIDSTA;

        if(status&0x60)                    //检查是否超时和CRC校验是否出错

        {     

            rSDIDSTA=(0x3<<0x5);         //清除超时标志和CRC错误标志

#ifdef __SD_MMC_DEBUG__

        Uart_Printf('there is wrong when reading: %s.\n',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;

}


写函数

/**********************************************************************************

功  能:该函数用于向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!\n');

#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;

}


测试效果

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

  • 单块读

  • 单块写

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

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

  • 先读后写

  • 先写后读

    移植fatfs文件系统测试:

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

 

仍然存在的Bug

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

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

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



进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

最新器件
精选电路图
  • 光控音效发生器电路

  • 非常简单的150W功放电路图

  • 一个简单的警笛电路图

  • 基于IC555的可变PWM振荡器电路

  • 分享一个电网倾角计电路

  • 电谐波图形均衡器示意图

    相关电子头条文章