搜索
bottom↓
回复: 10

马老师:用M051库来写一个SPI简单的SD卡复位程序,总是不对头

[复制链接]

出0入0汤圆

发表于 2011-9-15 00:02:48 | 显示全部楼层 |阅读模式
初学这个东西,用GPIO模拟SPI正确没有问题,想使用M058的硬件SPI却总是弄不成功。顺序是
0.设置SPI:
           DrvGPIO_InitFunction (E_FUNC_SPI0);
        DrvSPI_Open(eDRVSPI_PORT0, eDRVSPI_MASTER, eDRVSPI_TYPE0,32);
        DrvSPI_SetEndian(eDRVSPI_PORT0, eDRVSPI_MSB_FIRST);
        DrvSPI_DisableAutoSS(eDRVSPI_PORT0);
        DrvSPI_SetSlaveSelectActiveLevel(eDRVSPI_PORT0, eDRVSPI_ACTIVE_LOW_FALLING);
        DrvSPI_SetTriggerMode(eDRVSPI_PORT0, eDRVSPI_LEVEL_TRIGGER);
           DrvSPI_SetClockFreq(eDRVSPI_PORT0, 100000, 0); //频率降到100kHz
然后复位SD卡是:
1.SS高,发送许多CLK脉冲,唤醒SD卡:
           uint32_t txbuff[1]; //发送缓冲
           uint32_t rxbuff[1]; //接收缓冲
           DrvSPI_SetSS(eDRVSPI_PORT0); //SS高
        txbuff[0]=0;
        for(i=0;i<5;i++){
            DrvSPI_SingleWrite(eDRVSPI_PORT0,&txbuff[0]);//32*5=160clocks
            while (DrvSPI_IsBusy(eDRVSPI_PORT0)) {}
        }
2.SS低,发送CMD0 即0X40:
           DrvSPI_ClrSS(eDRVSPI_PORT0);
        txbuff[0]=0x40;
        DrvSPI_SetBitLength(eDRVSPI_PORT0, 8);
        DrvSPI_SingleWrite(eDRVSPI_PORT0, &txbuff[0]);
        while (DrvSPI_IsBusy(eDRVSPI_PORT0)) {}
3.接着发0X00000000:
           txbuff[0]=0;
           DrvSPI_SetBitLength(eDRVSPI_PORT0, 32);
        DrvSPI_SingleWrite(eDRVSPI_PORT0, &txbuff[0]);
        while (DrvSPI_IsBusy(eDRVSPI_PORT0)) {}
4.接着发CRC 0X95:
          txbuff[0]=0x95;
        DrvSPI_SetBitLength(eDRVSPI_PORT0, 8);
        DrvSPI_SingleWrite(eDRVSPI_PORT0, &txbuff[0]);
        while (DrvSPI_IsBusy(eDRVSPI_PORT0)) {}
5.等待SD卡回应0X01:
           rxbuff[0]=0;
          while(rxbuff[0]!=0x01){
        DrvSPI_SingleRead(eDRVSPI_PORT0, &rxbuff[0]);     
        }
到此卡住,SD卡不能返回0X01,程序陷入死循环。如果改成while(rxbuff[0]==0){DrvSPI_SingleRead(eDRVSPI_PORTT0,&rxbuff[0]}则可以往下走,说明rxbuff[0]已经不是0了,但是复位SD卡没有成功。请问马老师这么写行吗?恭听指教,谢谢!

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

发表于 2011-9-16 20:23:50 | 显示全部楼层
我实在没法回答,里面这么多的函数都是你自己写的,能正确使用吗?如果是官方提供的函数,也不保证没有BUG,需要仔细的看和实物调试。

出0入0汤圆

 楼主| 发表于 2011-9-17 17:35:33 | 显示全部楼层
知道了,反复看m051资料,发现他的SPI都是按照全双工考虑的,认为发送和接收同时进行,然而对付SD卡,是收发分开的,主机和从机不是同时收发,所以这些函数都不对号。还是用GPIO线模拟好了(用移植51的模拟方法,没有问题,还可以提高工作频率。)上面的函数不是我写的,是M0的SPI库函数。好的,谢谢马老师

出0入0汤圆

发表于 2011-9-18 18:04:24 | 显示全部楼层
使用MO51提供的函数库应该是可以的,但需要首先了解库函数到底做甚么,才能正确的使用。
我编写教程的第2版中,就增加了读SD卡的例子,同样使用的是AVR的硬件SPI口。
任何MCU的SPI硬件口都是全双工工作的,发送和接受同时进行,因为这是SPI规程决定的,但不是说就不能操作SD卡的。关键还是看你是否真正掌握SPI口,掌握SD卡的协议。
用GPIO线模拟SPI口是笨的办法,并不能提高工作频率。如果需要SPI口工作在10M,通常硬件的SPI可以做到,I/O口模拟的话,至少系统的工作频率要30M,因为移出一个字节数据,至少需要2条指令把CLK变高,变低,一条指令把一位数据放到数据线上(还不算移位和循环的判断),而30M的系统时钟,其SPI口一般最高可以为15M。这里还木有考虑接收,如果考虑接收,IO口模拟还要慢的。

    现在的SD卡可以支持到25M的速率,如果你的SPI频率低,读个稍微大的文件需要很多的时间,那么许多实际应用就有问题。比如我书中的例子是读SD卡的WAV文件,并播放出来,如果你的SPI工作太慢,那么根本无法流畅的播放音乐,出现卡机的现象。

    在我书中例子,AVR工作在16M,可以流畅播放2路8位44.1k的WAV音乐,而播放2路16位44.1k的WAV音乐,就变慢了,原因就是SPI的速度还是不够。

    我使用MO51也做这个DD,就能流畅播放2路16位44.1k的WAV音乐。

出0入0汤圆

 楼主| 发表于 2011-9-22 01:03:08 | 显示全部楼层
谢谢老师指导!以前没用到这个SPI,也不关心,现在用到了,临时抱佛脚。先仔细研究下你的书上的例子,然后看能不能转到M051上。要有问题再来请教!是,AVR的SPI也是全双工,(根本就没有半双工的SPI)先看看,不懂再问。

出0入0汤圆

发表于 2011-9-25 10:33:47 | 显示全部楼层
SPI 读取SD卡的 教程 能否穿点资料看看呢  感谢

出0入0汤圆

发表于 2011-9-25 20:07:59 | 显示全部楼层
回复【5楼】ffshen
spi 读取sd卡的 教程 能否穿点资料看看呢  感谢
-----------------------------------------------------------------------

建议购买我的教程第2版。本书配套的CD资料中有大量的SD卡方面的资料,在本栏中有下载。

出0入0汤圆

发表于 2011-10-6 16:29:10 | 显示全部楼层
马老师,什么时候会出M051的书?

出0入0汤圆

发表于 2011-11-10 16:57:37 | 显示全部楼层
有问题想请教楼主,我之前用AVR模拟SPI的方法读过SD卡,很稳定。现在用M051模拟SPI的方法读SD卡,连复位都没成功(没回应)。反复看了N次代码,也没发现有什么问题。代码在AVR上实现过。时钟延时也做的跟AVR的一样,就是没回应,彻底晕了!楼主能发你模拟SPI的代码给我参考一下吗?谢谢!

出0入0汤圆

 楼主| 发表于 2011-11-22 00:31:24 | 显示全部楼层
终于搞定!关键还是先要搞清SPI通信过程,写读同时进行,读也是通过写操作。特别是几种类型,马老师书11章说的很清楚。对于M051,时序和时钟类型属于M051的SPI类型5:下降传输,上升采样/锁定以及时钟高:空闲
经硬件运行通过的SPI初始化SD/MMC卡程序如下:
/////////////////////////////////////////////////////
//SPI初始化
void Sd_spi_on(void)
{
  DrvGPIO_InitFunction (E_FUNC_SPI0);//指定SPI功能,相关几个引脚就不要再GPIO设定
  DrvSPI_Open(eDRVSPI_PORT0, eDRVSPI_MASTER, eDRVSPI_TYPE5, 32);//开启SPI,注意SD卡对应模式5
  DrvSPI_SetEndian(eDRVSPI_PORT0, eDRVSPI_MSB_FIRST);//大头朝前
  DrvSPI_DisableAutoSS(eDRVSPI_PORT0);//不用自动SS选择
  DrvSPI_SetSlaveSelectActiveLevel(eDRVSPI_PORT0, eDRVSPI_ACTIVE_LOW_FALLING);//选SD是下降沿低电平有效
}
//按字节读SD
uint8_t Sd_spi_read()     //读就是一边发送0XFF一边把数据从SD卡逼出来
{
  uint32_t rxbuff=0;       //接收缓冲器
  uint32_t txdata=0xff;  //发送器
  uint8_t reback;            //8bit读出数据
  DrvSPI_SetBitLength(eDRVSPI_PORT0, 8);//每次发送8bit
  DrvSPI_SingleWrite(eDRVSPI_PORT0, &txdata);//发0XFF
  while (DrvSPI_IsBusy(eDRVSPI_PORT0));          //等待发完
  DrvSPI_DumpRxRegister(eDRVSPI_PORT0, &rxbuff, 1);//收到的数据存入缓冲
  reback=rxbuff; //取最低8位
  return reback;
}
//读SD卡应答:初始化过程需要反复读卡,直到应答0X01或0X00
uint8_t Sd_spi_response()
{
  uint8_t i=0;
  uint8_t response;
  while(i<10)         //一般在10次以内连续读,可以得到应答
  {
    response=Sd_spi_read();
    if(response==0)
    break;
    if(response==1)
    break;
    i++;
  }
  return response;
}
//SD卡命令:是6字节命令,8位命令码,32位参数,8位校验码,8位单片机一次只能传8位,M051一次32位就很方便啦
void Sd_spi_command(uint8_t command, uint32_t argument, uint8_t CRC)
{
  uint32_t tmp1=command|0x40;  //命令号和0X40位或得到命令码
  uint32_t tmp2=argument;          //存放32位参数
  uint32_t tmp3=CRC;                  //校验码
  DrvSPI_SetBitLength(eDRVSPI_PORT0, 8);
  DrvSPI_SingleWrite(eDRVSPI_PORT0, &tmp1);     //发送8位命令码
  while(DrvSPI_IsBusy(eDRVSPI_PORT0) );
  DrvSPI_SetBitLength(eDRVSPI_PORT0, 32);
  DrvSPI_SingleWrite(eDRVSPI_PORT0, &tmp2);    //发送32位参数
  while(DrvSPI_IsBusy(eDRVSPI_PORT0) );
  DrvSPI_SetBitLength(eDRVSPI_PORT0, 8);
  DrvSPI_SingleWrite(eDRVSPI_PORT0, &tmp3);    //发送8位校验码
  while(DrvSPI_IsBusy(eDRVSPI_PORT0) );
}

//SD初始化,和MMC卡兼容需要先在SS失能时连续发74个时钟,然后SS使能发送命令0,等返回0X01后发送命令1,等返回0X00完成初始化
void Sd_spi_Init(void)
{
  uint32_t i ,u32tmp,u32tmp1=0xff;
  uint8_t response;
   Sd_spi_on();  //设置SPI
  DrvSPI_ClrSS(eDRVSPI_PORT0);//SS失能(高)
  DrvSPI_SetBitLength(eDRVSPI_PORT0, 32);//一次发送32位,
  DrvSPI_SetClockFreq(eDRVSPI_PORT0, 400000, 0);//初始化SPI频率取低
  u32tmp=0xffffffff;
  for(i=0;i<3;i++)
  {
    DrvSPI_SingleWrite(eDRVSPI_PORT0, &u32tmp);//发送
    while(DrvSPI_IsBusy(eDRVSPI_PORT0) );  //
  }
  DrvSPI_SetSS(eDRVSPI_PORT0);           //SS使能
  do{
      Sd_spi_command(0, 0, 0X95);   //命令0
      response=Sd_spi_response();
    }while(response!=0x01);          //返回01复位成功如无返回0X01就多发几次命令0
  DrvSPI_ClrSS(eDRVSPI_PORT0);
  DrvSPI_SingleWrite(eDRVSPI_PORT0, &u32tmp1);      //SS失能时加发8个时钟
  while(DrvSPI_IsBusy(eDRVSPI_PORT0));     //
  DrvSPI_SetSS(eDRVSPI_PORT0);  //SS使能
  do{
      Sd_spi_command(1, 0, 0Xff);   //命令1
      response=Sd_spi_response();
    }while(response !=0x00);        //返回0,SD卡已经初始化成功
  DrvSPI_ClrSS(eDRVSPI_PORT0);
  DrvSPI_SingleWrite(eDRVSPI_PORT0, &u32tmp1);      //SS失能时加发8个时钟,巩固战果
  while(DrvSPI_IsBusy(eDRVSPI_PORT0));
  DrvSPI_SetClockFreq(eDRVSPI_PORT0, 10000000, 0);  //SPI频率提高到10MHZ
}
这样,初始化就完成了。全部使用M051的标准库函数

出0入0汤圆

发表于 2011-11-30 11:19:59 | 显示全部楼层
回复【9楼】AAVVRR
-----------------------------------------------------------------------

楼主,你好,想请教3个问题。 1、SD卡对应模式5。看了M051的资料,好像没有讲到SPI模式这个概念。
                            2、大头朝前。是指什么意思?高位在前吗?
                            3、读就是一边发送0XFF一边把数据从SD卡逼出来的。SD卡收到了命令以后不应该是自动回
                               应的吗?为什么还要发送0XFF?
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-7-23 00:27

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

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