dsPIC33E系列单片机SPI方式实现SD卡的读写

/*写一个字节函数*/
u8 SpiWriteByte(u8 TxData)
{
    u8 RxData;
    SPI1BUF = TxData;               //待发送数据装载待发送寄存器中
    while(!SPI1STATbits.SPITBF);    //等待接收完成

    SPI1BUF = 0xFF;
    while(!SPI1STATbits.SPIRBF);
    RxData = SPI1BUF;   
    return RxData;
    //IFS0bits.SPI1IF = 0;          //清零中断标志
}

/*发送命令函数*/
u8 SdSendCommand(u8* cmd)
{
    u8 time, r;

    SpiWriteByte(cmd[0]);         //写入序列号
    SpiWriteByte(cmd[1]);         //数据段第4个字节
    SpiWriteByte(cmd[2]);         //数据段第3个字节
    SpiWriteByte(cmd[3]);         //数据段第2个字节
    SpiWriteByte(cmd[4]);         //数据段第1个字节
    SpiWriteByte(cmd[5]);         //CRC校验和

    /*获取16为的回应*/
    SpiWriteByte(0xFF);

    time = 0;
    do
    {
        r = SpiWriteByte(0xFF);   //获取后8位的回应
        time ++;
    }while((r == 0xFF) && (time < 100));
    return r;           //返回状态位
}

/*SD卡的初始化*/
u8 SdInit(void)
{
    u8 i, time, r1 = 0xFF;
    u8* pcmd;    //用于获取数组首地址

    /*初始化SPI的IO口和SPI模式*/
    Spi1Init();

    u8 cmd0[6] = {0x40, 0x00, 0x00, 0x00, 0x00, 0x95}; //CMD0使SD卡进入IDLE状态
    u8 cmd16[6] = {0x50, 0x00, 0x00, 0x02, 0x00, 0x01}; //CMD16,设置扇区大小为512字节
    u8 cmd55[6] = {0x77, 0x00, 0x00, 0x00, 0x00, 0x01}; //SD卡使用CMD55+ACMD41来进行初始化
    u8 acmd41[6] = {0x69, 0x00, 0x00, 0x00, 0x00, 0x01}; 

    /*SD卡复位,进入SPI模式,使用CMD0*/
    CS = 1;          //发送74个时钟之前要禁止SD卡
    for(i = 0; i < 10; i ++)     //在向SD卡发送数据之前,必需先SD发送至少74个时钟
    {
        SpiWriteByte(0xFF);      //发送了80个时钟
    }

    /*发送CMD0进行复位*/
    time = 0;
    CS = 0;
    do
    {
        pcmd = cmd0;
        r1 = SdSendCommand(pcmd);         //写入CMD0
        time ++;       //用来计时是否超时
    }while((r1 != 0x01) && (time < 200));
    if(time >= 200)
    {
        return (INIT_CMD0_ERROR);    //CMD0写入失败
    }
    CS = 1;
    r1 = 0xFF;      //清楚r1返回标志位
    SpiWriteByte(0xFF);    //写入指令后附加8个填充时钟

    /*发送CMD55+ACMD41对SD卡进行初始化*/
    CS = 0;
    time = 0;
    do
    {
        pcmd = cmd55;
        SdSendCommand(pcmd);    //写入CMD55
        pcmd = acmd41;
        r1 = SdSendCommand(pcmd);     //写入命令ACMD41
        time ++;
    }while((r1 != 0) && (time < 200));
    if(time >= 200)
    {
        return INIT_ACMD41_ERROR;
    }
    CS = 1;
    r1 = 0xFF;
    SpiWriteByte(0xFF);    //写入指令后附加8个填充时钟

    /*发送CMD16,设置SD卡块的大小为512字节*/
    CS = 0;
    time = 0;
    do
    {
        pcmd = cmd16;
        r1 = SdSendCommand(pcmd);
        time ++;
    }while((r1 != 0) && (time < 200));
    if(time >= 200)
    {
        return INIT_CMD16_ERROR;
    }
    CS = 1;
    r1 = 0xFF;
    SpiWriteByte(0xFF);

    /*SD卡初始化结束后需要转为高速模式*/
    SPI1CON1bits.SPRE = 6;   //辅助预分频比为2:1
    SPI1CON1bits.PPRE = 2;   //主预分频比为4:1

    return 0;
}

/*写一个扇区*/
u8 SDWriteSector(u32 sector, u8* buffer)
{
    u8 r1 = 1;
    u8* pcmd;
    u16 retry, i, j;
    u8 cmd24[6]={0x58, 0x00, 0x00, 0x00, 0x00, 0xFF}; //向SD卡中单个扇区(512字节)写入数据,用CMD24

    /*将扇区地址转换为字节地址*/
    sector = sector >> 9;

    /*将字节地址加载到命令CMD24中*/
    cmd24[1] = sector >> 24;
    cmd24[2] = sector >> 16;
    cmd24[3] = sector >> 8;
    cmd24[4] = sector;

    /*发送CMD24写命令*/
    CS = 0;
    retry = 0;
    do
    {
        pcmd = cmd24;
        r1 = SdSendCommand(pcmd);    //写入CMD24
        retry ++;
    }while((r1 != 0x0) && (retry < 200));
    if(retry >= 200)
    {
        return INIT_CMD24_ERROR;    //写入CMD24失败
    }

     //写入3个空数据,等待SD卡准备好
    SpiWriteByte(0xFF);
    SpiWriteByte(0xFF);
    SpiWriteByte(0xFF);

    SpiWriteByte(0xFE);      //发开始符

    /*发送512个字节*/
    for(i = 0; i < 4; i ++)
    {
        for(j = 0; j < 126; j ++)
        {
            SpiWriteByte(*buffer);
            buffer ++;
        }
    }

    /*16位CRC校验*/
    SpiWriteByte(0xFF);
    SpiWriteByte(0xFF);

    while((r1 = SpiWriteByte(0xFF)) == 0xFF);   //等待SD卡响应
    if((r1 & 0x1f) != 0x05)         //如果返回值是 XXX00101 说明数据已经被SD卡接收了
    {
        return 1;
    }

    /*等到SD卡不忙*/
    while(SpiWriteByte(0xFF) != 0xFF);
    CS = 1;
    SpiWriteByte(0xFF);    //写入指令后附加8个填充时钟
    return 0;
}

/*读一个扇区*/
u8 SDReadSector(u32 sector, u8* buffer)
{
    u8 r1 = 1;
    u8* pcmd;
    u16 retry, i, j;
    u8 cmd17[6] = {0x51, 0x00, 0x00, 0x00, 0x00, 0xFF}; //从SD卡中单个扇区(512字节)读取数据,用CMD17
    u8 cmd12[6] = {0x1C, 0x00, 0x00, 0x00, 0x00, 0x01}; //CMD12,强制停止命令

    /*将扇区地址转换为字节地址*/
    sector = sector >> 9;

    /*将字节地址加载到命令CMD24中*/
    cmd17[1] = sector >> 24;
    cmd17[2] = sector >> 16;
    cmd17[3] = sector >> 8;
    cmd17[4] = sector;

    /*发送CMD17写命令*/
    CS = 0;
    retry = 0;
    do
    {
        pcmd = cmd17;
        r1 = SdSendCommand(pcmd);    //写入CMD17
        retry ++;
    }while((r1 != 0x0) && (retry < 200));
    if(retry >= 200)
    {
        return INIT_CMD17_ERROR;    //写入CMD17失败
    }

    /*写入3个空数据,等待SD卡准备好*/
    SpiWriteByte(0xFF);
    SpiWriteByte(0xFF);
    SpiWriteByte(0xFF);

    while(SpiWriteByte(0xFF) != 0xFE);      //等待SD卡读准备
    /*读取SD卡buffer扇区中的512个字节*/
    for(i = 0; i < 4; i ++)
    {
        for(j = 0; j < 128; j ++)
        {
            *buffer = SpiWriteByte(0xFF);   //获取数据到buffer中
            buffer ++;
        }
    }

    /*16位CRC校验*/
    SpiWriteByte(0xFF);
    SpiWriteByte(0xFF);

    /*写12号命令终止数据读取*/
    pcmd = cmd12;
    SdSendCommand(pcmd);

    CS = 1;
    SpiWriteByte(0xFF);    //写入指令后附加8个填充时钟
    return 0;
}

粽子糖果 发表于10-19 10:17 浏览65535次
分享到:

已有0条评论

暂时还没有回复哟,快来抢沙发吧

添加一条新评论

只有登录用户才能评论,请先登录注册哦!

话题作者

粽子糖果
粽子糖果(总统)
金币:41631个|学分:51991个
立即注册
畅学电子网,带你进入电子开发学习世界
专业电子工程技术学习交流社区,加入畅学一起充电加油吧!

x

畅学电子网订阅号