AAVVRR 发表于 2011-9-15 00:02:48

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

初学这个东西,用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; //发送缓冲
         uint32_t rxbuff; //接收缓冲
         DrvSPI_SetSS(eDRVSPI_PORT0); //SS高
        txbuff=0;
        for(i=0;i<5;i++){
          DrvSPI_SingleWrite(eDRVSPI_PORT0,&txbuff);//32*5=160clocks
          while (DrvSPI_IsBusy(eDRVSPI_PORT0)) {}
        }
2.SS低,发送CMD0 即0X40:
         DrvSPI_ClrSS(eDRVSPI_PORT0);
        txbuff=0x40;
        DrvSPI_SetBitLength(eDRVSPI_PORT0, 8);
        DrvSPI_SingleWrite(eDRVSPI_PORT0, &txbuff);
        while (DrvSPI_IsBusy(eDRVSPI_PORT0)) {}
3.接着发0X00000000:
         txbuff=0;
         DrvSPI_SetBitLength(eDRVSPI_PORT0, 32);
        DrvSPI_SingleWrite(eDRVSPI_PORT0, &txbuff);
        while (DrvSPI_IsBusy(eDRVSPI_PORT0)) {}
4.接着发CRC 0X95:
          txbuff=0x95;
        DrvSPI_SetBitLength(eDRVSPI_PORT0, 8);
        DrvSPI_SingleWrite(eDRVSPI_PORT0, &txbuff);
        while (DrvSPI_IsBusy(eDRVSPI_PORT0)) {}
5.等待SD卡回应0X01:
         rxbuff=0;
          while(rxbuff!=0x01){
        DrvSPI_SingleRead(eDRVSPI_PORT0, &rxbuff);   
        }
到此卡住,SD卡不能返回0X01,程序陷入死循环。如果改成while(rxbuff==0){DrvSPI_SingleRead(eDRVSPI_PORTT0,&rxbuff}则可以往下走,说明rxbuff已经不是0了,但是复位SD卡没有成功。请问马老师这么写行吗?恭听指教,谢谢!

machao 发表于 2011-9-16 20:23:50

我实在没法回答,里面这么多的函数都是你自己写的,能正确使用吗?如果是官方提供的函数,也不保证没有BUG,需要仔细的看和实物调试。

AAVVRR 发表于 2011-9-17 17:35:33

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

machao 发表于 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音乐。

AAVVRR 发表于 2011-9-22 01:03:08

谢谢老师指导!以前没用到这个SPI,也不关心,现在用到了,临时抱佛脚。先仔细研究下你的书上的例子,然后看能不能转到M051上。要有问题再来请教!是,AVR的SPI也是全双工,(根本就没有半双工的SPI)先看看,不懂再问。

ffshen 发表于 2011-9-25 10:33:47

SPI 读取SD卡的 教程 能否穿点资料看看呢感谢

machao 发表于 2011-9-25 20:07:59

回复【5楼】ffshen
spi 读取sd卡的 教程 能否穿点资料看看呢感谢
-----------------------------------------------------------------------

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

tanghk 发表于 2011-10-6 16:29:10

马老师,什么时候会出M051的书?

tanghk 发表于 2011-11-10 16:57:37

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

AAVVRR 发表于 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的标准库函数

tanghk 发表于 2011-11-30 11:19:59

回复【9楼】AAVVRR
-----------------------------------------------------------------------

楼主,你好,想请教3个问题。 1、SD卡对应模式5。看了M051的资料,好像没有讲到SPI模式这个概念。
                            2、大头朝前。是指什么意思?高位在前吗?
                            3、读就是一边发送0XFF一边把数据从SD卡逼出来的。SD卡收到了命令以后不应该是自动回
                               应的吗?为什么还要发送0XFF?
页: [1]
查看完整版本: 马老师:用M051库来写一个SPI简单的SD卡复位程序,总是不对头