问题描述:
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<BlockSize) { 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文件,耗费了十几秒种。