搜索
bottom↓
回复: 7

网友遇到过写TF卡轮询SD_WaitReady时间太长的情况吗?

[复制链接]

出0入0汤圆

发表于 2014-12-29 12:36:52 | 显示全部楼层 |阅读模式
做数据采集,原来规划每20mS保存一扇区的数据。

测试中发现大部分TF卡会出现丢写数据的问题,仅有两张卡没有问题。

用逻辑分析仪抓5秒的波形,分析波形,
正常的每个扇区写入时间为两三个毫秒(因为怀疑通信速率是否太高,已经将SPI时钟降到1MHz)。
出现异常的地方扇区写入的时间特别长,二三十毫秒,甚至八九十毫秒。
致使后面采集的数据来不及写入,而丢失。

从波形看,出现问题没有什么特别规律,

同样出问题的卡,这次5秒也许没有出现异常波形,后面一次5秒采集就出现5、6个异常。
不过,会出问题的卡,每5秒的采集大部分都是会出现异常,5秒内不出现异常的相对少。

没有两张问题的卡,绝大部分情况下都不会出现异常,但偶尔也会看到异常。


仿真分析依次从SD.C里下面函数深入: SD_WriteDisk、SD_SendCmd、SD_Select、SD_WaitReady

最后确定在SD_WaitReady函数下这个while循环语句占用了很长时间。
从抓到的SPI波形看,SD_SPI_ReadWriteByte(0XFF)返回的都是0x00。
这个出现异常的轮询时间不固定。
unsigned char SD_WaitReady(void)
{
        unsigned int t=0;
       
        do
        {
                if(SD_SPI_ReadWriteByte(0XFF)==0XFF)
                {
                         return 0;
                }
                       
                t++;                 
               
        }while(t<0XFFFFFF);
       
        return 1;
}

出0入0汤圆

 楼主| 发表于 2014-12-29 12:51:03 | 显示全部楼层
下面是SD.C部分代码

#include "M051Series.h"
#include "SPIC.h"
#include "SDC.h"
#include "ffconf.h"
#include "ff.h"
#include "diskio.h"

                                          
unsigned char  SD_Type=0;//SD卡的类型

/****************************************
*函数名称:SD_SPI_ReadWriteByte
*输    入:d 要写入的数据
*输    出:读到的数据
*功    能:SD卡SPI总线读写数据操作
******************************************/
unsigned char SD_SPI_ReadWriteByte(unsigned char d)
{
        return Spi0WriteRead(d);
}          
/****************************************
*函数名称:SD_SPI_ReadWriteByte
*输    入:无
*输    出:无
*功    能:SD卡初始化的时候,进入低速模式
******************************************/
void SD_SPI_SpeedLow(void)
{
        Spi0SetSpeedLow();
}
/****************************************
*函数名称:SD_SPI_SpeedHigh
*输    入:无
*输    出:无
*功    能:SD卡正常工作的时候,进入高速模式
******************************************/
void SD_SPI_SpeedHigh(void)
{
        Spi0SetSpeedHigh();
}
/****************************************
*函数名称:SD_SPI_Init
*输    入:无
*输    出:无
*功    能:SD卡SPI总线初始化
******************************************/
void SD_SPI_Init(void)
{

        Spi0MasterInit();
       
//        P1_PMD &=~(3UL<<8);
//        P1_PMD |=  1UL<<8 ;
       
        SD_CS(1);
}
/****************************************
*函数名称:SD_DisSelect
*输    入:无
*输    出:无
*功    能:释放SPI总线,即取消选择
******************************************/
void SD_DisSelect(void)
{
        SD_CS(1);
        SD_SPI_ReadWriteByte(0xFF);//提供额外的8个时钟
}
/****************************************
*函数名称:SD_Select
*输    入:无
*输    出:0,成功;
          1,失败;
*功    能:选择SD卡,并且等待卡准备OK
******************************************/
unsigned char SD_Select(void)
{
        SD_CS(0);
       
        if(SD_WaitReady()==0)
        {
                 /* 等待成功 */
                 return 0;
        }
               
        SD_DisSelect();
       
        /* 等待失败 */
        return 1;
}
/****************************************
*函数名称:SD_WaitReady
*输    入:无
*输    出:0,准备好了;
          其他,错误代码
*功    能:等待卡准备好
******************************************/
unsigned char SD_WaitReady(void)
{
        unsigned int t=0;
       
        do
        {
                if(SD_SPI_ReadWriteByte(0XFF)==0XFF)
                {
                         return 0;
                }
                       
                t++;                 
               
        }while(t<0XFFFFFF);
       
        return 1;
}
/****************************************
*函数名称:SD_GetResponse
*输    入:ucResponse 要得到的返回值
*输    出:0,成功得到了该回应值
          其他,得到回应值失败
*功    能:等待SD卡回应
******************************************/
unsigned char SD_GetResponse(unsigned char ucResponse)
{
        unsigned short int Count=0xFFFF;//等待次数       
       
        while ((SD_SPI_ReadWriteByte(0XFF)!=ucResponse)&&Count)
        {  
                  /* 等待得到准确的回应 */
                  Count--;           
        }
               
        if (Count==0)
        {   
                  /* 得到回应失败 */
                  return MSD_RESPONSE_FAILURE;
        }          
        else
        {   /* 正确回应 */
            return MSD_RESPONSE_NO_ERROR;
        }
}
/****************************************
*函数名称:SD_RecvData
*输    入:buf 数据缓存区
          len 要读取的数据长度
*输    出:0,成功;
          其他,失败;       
*功    能:从sd卡读取一个数据包的内容
******************************************/
unsigned char SD_RecvData(unsigned char*buf,unsigned short int len)
{                                    
          if(SD_GetResponse(0xFE))
                {
                         /* 等待SD卡发回数据起始令牌0xFE */
                         return 1;
                }
                /* 开始接收数据 */
    while(len--)
    {
       *buf=Spi0WriteRead(0xFF);
        buf++;
    }
               
    /* 下面是2个伪CRC(dummy CRC)*/
    SD_SPI_ReadWriteByte(0xFF);
    SD_SPI_ReadWriteByte(0xFF);               
                /* 读取成功 */
    return 0;
}
/****************************************
*函数名称:SD_SendBlock
*输    入:buf 数据缓存区
          cmd 指令
*输    出:0,成功;
          其他,失败;       
*功    能:向sd卡写入一个数据包的内容 512字节
******************************************/
unsigned char SD_SendBlock(unsigned char*buf,unsigned char cmd)
{       
                unsigned short int t;                            
               
                if(SD_WaitReady())
                {  
                         /* 等待准备失效 */
                         return 1;
                }
                       
                SD_SPI_ReadWriteByte(cmd);
               
                /* 不是结束指令 */
                if(cmd!=0XFD)
                {
                        /* 提高速度,减少函数传参时间 */
                        for(t=0;t<512;t++)
                        {
                                Spi0WriteRead(buf[t]);
                        }
                          /* 忽略crc */
                                SD_SPI_ReadWriteByte(0xFF);
                                SD_SPI_ReadWriteByte(0xFF);
                          /* 接收响应 */
                        t=SD_SPI_ReadWriteByte(0xFF);
                       
                         if((t&0x1F)!=0x05)
                         {
                                  /* 响应错误 */
                                        return 2;                                                       
                         }
                                                                                                               
                }       
                /* 写入成功 */
    return 0;
}
/****************************************
*函数名称:SD_SendCmd
*输    入:cmd   命令
          arg   命令参数
          crc   crc校验值          
*输    出:SD卡返回的响应               
*功    能:向SD卡发送一个命令
******************************************/
unsigned char SD_SendCmd(unsigned char cmd, unsigned int arg, unsigned char crc)
{
  unsigned char r1;       
        unsigned char Retry=0;
       
        /* 取消上次片选 */
        SD_DisSelect();
       
        if(SD_Select())
        {
                 /* 片选失效 */
                 return 0XFF;
        }
        /* 分别写入命令 */
        SD_SPI_ReadWriteByte(cmd | 0x40);
        SD_SPI_ReadWriteByte(arg >> 24);
        SD_SPI_ReadWriteByte(arg >> 16);
        SD_SPI_ReadWriteByte(arg >> 8);
        SD_SPI_ReadWriteByte(arg);          
        SD_SPI_ReadWriteByte(crc);
       
        /* 当停止读时跳过一个字节 */
        if(cmd==CMD12)
        {
                 SD_SPI_ReadWriteByte(0xFF);
        }
               
  /* 等待响应,或超时退出 */
        Retry=0X1F;
       
        do
        {
                r1=SD_SPI_ReadWriteByte(0xFF);
               
        }while((r1&0X80) && Retry--);         
       
        /* 返回状态值 */
  return r1;
}                                                                                                                                                                              
/****************************************
*函数名称:SD_GetCID
*输    入:cid_data
          arg   命令参数
          crc   crc校验值          
*输    出:SD卡返回的响应               
*功    能:获取SD卡的CID信息,包括制造商信息
******************************************/
unsigned char SD_GetCID(unsigned char *cid_data)
{
    unsigned char r1;          
    /* 发CMD10命令,读CID */
    r1=SD_SendCmd(CMD10,0,0x01);
       
    if(r1==0x00)
          {
                         /* 接收16个字节的数据 */       
                   r1=SD_RecvData(cid_data,16);
    }
               
                /* 取消片选 */
          SD_DisSelect();
               
          if(r1)
                {
                         return 1;
                }
                       
          return 0;
}                                                                                                                                                                  
/****************************************
*函数名称:SD_GetCSD
*输    入:csd_data 存放CID的内存,至少16Byte   
*输    出:0:NO_ERR
                      1:错误               
*功    能:获取SD卡的CSD信息,包括容量和速度信息
******************************************/
unsigned char SD_GetCSD(unsigned char *csd_data)
{
    unsigned char r1;
       
          /* 发CMD9命令,读CSD */
    r1=SD_SendCmd(CMD9,0,0x01);
       
    if(r1==0)
          {
                         /* 接收16个字节的数据 */
             r1=SD_RecvData(csd_data, 16);
    }
               
                /* 取消片选 */
          SD_DisSelect();
               
          if(r1)
                {
                         return 1;
                }
               
          return 0;
}  
/****************************************
*函数名称:SD_GetSectorCount
*输    入:无
*输    出:0   取容量出错
          其他 SD卡的容量(扇区数/512字节)
*功    能:获取SD卡的总扇区数(扇区数)   
          每扇区的字节数必为512,因为如果不是512,则初始化不能通过.               
******************************************/
unsigned int SD_GetSectorCount(void)
{
    unsigned char  csd[16];
    unsigned char  n;
          unsigned short int csize;        
    unsigned int Capacity;  

       
          /* 取CSD信息,如果期间出错,返回0 */
    if(SD_GetCSD(csd)!=0)
                {
                         return 0;       
                }
                            
    /* 如果为SDHC卡,按照下面方式计算 */
    if((csd[0]&0xC0)==0x40)        // V2.00的卡
    {       
                          /* 得到扇区数        */        
                                csize = csd[9] + ((unsigned short int)csd[8] << 8) + 1;
                                Capacity = (unsigned int)csize << 10;          
    }
                else//V1.XX的卡
    {          
                          /* 得到扇区数 */
                                n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
                                csize = (csd[8] >> 6) + ((unsigned short int)csd[7] << 2) + ((unsigned short int)(csd[6] & 3) << 10) + 1;
                                Capacity= (unsigned int)csize << (n - 9);
    }
               
    return Capacity;
}
/****************************************
*函数名称:SD_Initialize
*输    入:无
*输    出:0    -成功
          其他 -失败
*功    能:SD卡初始化
******************************************/
unsigned char SD_Initialize(void)
{
    unsigned char  r1;     // 存放SD卡的返回值
    unsigned short int retry;  // 用来进行超时计数
    unsigned char buf[4];  
          unsigned int i;
    /* 初始化IO */
          SD_SPI_Init();       
          /* 设置到低速模式 */
          SD_SPI_SpeedLow();       
       
          for(i=0;i<10;i++)
          {
                          /* 发送最少74个脉冲 */
                          SD_SPI_ReadWriteByte(0XFF);
                }
       
          retry=20;
               
          do
          {
                         /* 进入IDLE状态 */
                   r1=SD_SendCmd(CMD0,0,0x95);
                       
          }while((r1!=0X01) && retry--);
               
                /* 默认无卡 */
                SD_Type=0;
               
                if(r1==0X01)
                {
                        if(SD_SendCmd(CMD8,0x1AA,0x87)==1)//SD V2.0
                        {
                                for(i=0;i<4;i++)
                                {   
                                          /* 等待应答 */
                                          buf[i]=SD_SPI_ReadWriteByte(0XFF);       
                                }
                               
                                /* 卡是否支持2.7~3.6V */
                                if(buf[2]==0X01&&buf[3]==0XAA)
                                {
                                        retry=0XFFFE;
                                        do
                                        {
                                                /* 发送CMD55 */
                                                SD_SendCmd(CMD55,0,0X01);       
                                                /* 发送CMD41 */
                                                r1=SD_SendCmd(CMD41,0x40000000,0X01);
                                               
                                        }while(r1&&retry--);
                                        /* 鉴别SD2.0卡版本开始 */
                                        if(retry&&SD_SendCmd(CMD58,0,0X01)==0)
                                        {
                                                for(i=0;i<4;i++)
                                                {
                                                          /* 得到OCR值 */
                                                          buf[i]=SD_SPI_ReadWriteByte(0XFF);
                                                }
                                                /* 检查CCS */
                                                if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC;  
                                                else           SD_Type=SD_TYPE_V2;   
                                        }
                                }
                        }
                        else//SD V1.x/ MMC        V3
                        {
                                /* 发送CMD55 */
                                SD_SendCmd(CMD55,0,0X01);               
                                /* 发送CMD41 */
                                r1=SD_SendCmd(CMD41,0,0X01);       
                               
                                if(r1<=1)
                                {               
                                        SD_Type=SD_TYPE_V1;
                                        retry=0XFFFE;
                                       
                                        do //等待退出IDLE模式
                                        {
                                                   /* 发送CMD55 */
                                                   SD_SendCmd(CMD55,0,0X01);       
                                                   /* 发送CMD41 */
                                                r1=SD_SendCmd(CMD41,0,0X01);  
                                        }while(r1&&retry--);
                                       
                                }
                                else//MMC卡不支持CMD55+CMD41识别
                                {
                                        /* MMC V3 */
                                        SD_Type=SD_TYPE_MMC;
                                        retry=0XFFFE;
                                       
                                        /* 等待退出IDLE模式 */
                                        do
                                        {                       
            /* 发送CMD1 */                                               
                                                r1=SD_SendCmd(CMD1,0,0X01);
                                               
                                        }while(r1&&retry--);  
                                }
                               
                                if(retry==0||SD_SendCmd(CMD16,512,0X01)!=0)
                                {  
                                         /* 错误的卡 */
                                         SD_Type=SD_TYPE_ERR;
                                }
                               
                        }
                }
                /* 取消片选 */
                SD_DisSelect();   
                /* 高速 */
                SD_SPI_SpeedHigh();
               
                if(SD_Type)return 0;
                if(r1)     return r1;
                /* 其他错误 */
                return 0xAA;
}
/****************************************
*函数名称:SD_ReadDisk
*输    入:buf   -数据缓存区
          sector-扇区
          cnt   -扇区数
*输    出:0     -成功
          其他  -失败
*功    能:SD卡读
******************************************/
unsigned char SD_ReadDisk(unsigned char*buf,unsigned int sector,unsigned char cnt)
{
        unsigned char r1;
       
        if(SD_Type!=SD_TYPE_V2HC)
        {
                 /* 转换为字节地址 */
                 sector <<= 9;
        }
               
        if(cnt==1)
        {
                /* 读命令 */
                r1=SD_SendCmd(CMD17,sector,0X01);
                /* 指令发送成功 */
                if(r1==0)
                {
                        /* 接收512个字节 */       
                        r1=SD_RecvData(buf,512);   
                }
               
        }
        else
        {
                /* 连续读命令 */
                r1=SD_SendCmd(CMD18,sector,0X01);
               
                do
                {
                        /* 接收512个字节 */       
                        r1=SD_RecvData(buf,512);
                        buf+=512;  
                       
                }while(--cnt && r1==0);        
                /* 发送停止命令 */
                SD_SendCmd(CMD12,0,0X01);       
        }   
        /* 取消片选 */
        SD_DisSelect();
       
        return r1;
}
/****************************************
*函数名称:SD_WriteDisk
*输    入:buf   -数据缓存区
          sector-扇区
          cnt   -扇区数
*输    出:0     -成功
          其他  -失败
*功    能:SD卡写
******************************************/
unsigned char SD_WriteDisk(unsigned char*buf,unsigned int sector,unsigned char cnt)
{
        unsigned short int r1;
       
        /* 转换为字节地址 */
        if(SD_Type!=SD_TYPE_V2HC)
        {
                 sector *= 512;
        }
               
        if(cnt==1)
        {  
                 /* 读命令 */
                 r1=SD_SendCmd(CMD24,sector,0X01);
                 /* 指令发送成功 */
                 if(r1==0)
                 {
                          /* 写512个字节 */       
                          r1=SD_SendBlock(buf,0xFE);   
                 }
        }
        else
        {
                if(SD_Type!=SD_TYPE_MMC)
                {
                        SD_SendCmd(CMD55,0,0X01);       
                        SD_SendCmd(CMD23,cnt,0X01);//发送指令       
                }
               
                r1=SD_SendCmd(CMD25,sector,0X01);//连续读命令
               
                if(r1==0)
                {
                        do
                        {
                                r1=SD_SendBlock(buf,0xFC);//接收512个字节         
                                buf+=512;  
                        }while(--cnt && r1==0);
                       
                        r1=SD_SendBlock(0,0xFD);//接收512个字节
                }
        }   
       
        SD_DisSelect();//取消片选
       
        return r1;
}          

出0入0汤圆

发表于 2014-12-29 16:27:48 | 显示全部楼层
秒级的都有,很正常。越小块越随机读写,这情况越糟糕。
大缓冲加对齐会好些

出0入0汤圆

 楼主| 发表于 2014-12-29 17:05:54 | 显示全部楼层
aozima 发表于 2014-12-29 16:27
秒级的都有,很正常。越小块越随机读写,这情况越糟糕。
大缓冲加对齐会好些 ...

谢谢回复!

你的意思是这种情况是正常的?如果这样,不是要换有大容量RAM的CPU,备大容量内存来应付?

出0入0汤圆

 楼主| 发表于 2014-12-29 18:55:11 | 显示全部楼层
算了一下,缓存1秒不是需要25K的RAM。

这种写入速度不高的尚且如此,速度更高不是缓存得更大, 难道就只有缓存的办法了吗?

出0入0汤圆

发表于 2014-12-29 21:38:02 | 显示全部楼层
QZDZ 发表于 2014-12-29 18:55
算了一下,缓存1秒不是需要25K的RAM。

这种写入速度不高的尚且如此,速度更高不是缓存得更大, 难道就只 ...

以C10的卡为例,大块连续写至少可以达到10MB每秒才算合格,所以大块连续写时,并不需要再多的缓冲了。
越新越快的卡,处理非对齐的随机读写反而更慢。

出0入0汤圆

 楼主| 发表于 2014-12-30 08:35:08 | 显示全部楼层
本帖最后由 QZDZ 于 2014-12-30 08:37 编辑

开始我也怀疑卡的问题,所以用读卡器插到电脑里测试,发现小块读写速度的确非常慢。

早上再将两张卡放到电脑进行读写速度测试,结果如下:

Kingston 4G TF卡



杂牌 128M TF卡


这里测试的两张卡,4G的卡容易出现轮询时间太长的情况,而128M的卡基本没有。

(轮询时间太长与卡容量不一定成正比关系,我测试的另外一张128M卡,也出现与这张4G卡一样的情况,而还有一张2G的也没有问题)

因为我写入时是以整个扇区写入的,至于对齐方面,因为芯片RAM有限,感觉也就只能做到这点。

我这样理解,不同卡内存储芯片不同,其最小擦除、写入的页大小是不同的,所以无法以这个为参考。
(因为不同卡页大小,擦除、改写、回写时间上应该是有差别的)

当然如果有大的RAM,做个2K或4K对齐的缓存,估计会好一些。

如果,我以25K/S连续写入(采集时间可能达几十分钟),

大家觉得,应该设多大的缓存,可以保障不丢数据的写卡。



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2015-1-6 11:41:42 | 显示全部楼层
其他网友,有遇到过类似的情况吗?
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-10 02:38

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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