SD卡驱动(基于XS128)

    技术2022-05-20  32

    之前一直在做SD卡,一开始是基于8366的,但后面为了给同学方便,直接做成XS的了。现在可以进行SD卡的读写,TXT的创建与BMP的创建。

    下面是SD卡的驱动程序(基于XS128)。

     

    #include "SD_Card.h" 

    #define  SD_CS    PTM_PTM3

    //PLL初始化void PLL_Init(void) //PLLCLK=2*OSCCLK*(SYNR+1)/(REFDV+1){                   //PLLCLK=2*16M*(2+1)/(0+1)= 96M   SYNR  = 0xC2;        //总线频率为PLL频率的一半48M   REFDV = 0xC0;   ECLKCTL &= ~0x80;   while(!(CRGFLG&0x08)); //自循环语句,等待时钟频率稳定后允许锁相环时钟作为系统时钟   CLKSEL=0x80;           //选择锁相环频率为系统时钟频率}

    //SPI初始化void SPI_Init(void) {   MODRR   = 0x00;     MODRR_MODRR4=1;  //使用PM 口    DDRM   |= 0x38;  //可知SCK0=1,MOSI=1,SS0=1 本步可以忽略,这样做复位可以抗干扰下,并且直接让                    //片选无效    SPI0CR1 = 0x5e;  //CPOL=1,时钟选择低有效,spsck空闲时为高电平                     //CPHA=1 会在发送 8 位数据开始 sck就发生一次跳变    SPI0CR2 = 0x10;  //modfen=1 .和上面 ssoe=1  确定 spi在 master 模式下 ss 位从机选择输出。并允许 modf标志设置    SPI0BR  = 0x17;  //波特率设置波特率= BR=busclk/((SPPR + 1)· 2^(SPR + 1))=48M / (1+1)*2^(7+1) = 93KHz  }

    //设置 spi 高速 void SPI_High(void) {    SPI0BR = 0x01; //BR=busclk/((SPPR + 1)· 2^(SPR + 1))=48M / 4= 12m }

    //用SPI写1bytevoid SPI_Write(unsigned char date) {       while (!SPI0SR_SPTEF);      //等待发送器空     (void)SPI0SR;      SPI0DRL = date;     while (!SPI0SR_SPIF);       //等待发送完成     (void)SPI0DRL;    }  

    //用SPI读1byteunsigned char SPI_Read(void)  {       while (!SPI0SR_SPTEF);     //等待发送器空    (void)SPI0SR;     SPI0DRL = 0xff;       while (!SPI0SR_SPIF);      //等待发送完成    (void)SPI0DRL;    return SPI0DRL;    }

    //SPI写命令uchar SD_Send_Cmd(uchar cmd,ulong arg,uchar crc,uchar Res){    volatile unsigned char r1;    unsigned char Retry = 200;   

        unsigned char addr_1;    unsigned char addr_2;    unsigned char addr_3;    unsigned char addr_4;            arg <<= 9;   //arg = arg * 512;        addr_1 = arg & 0xff;    addr_2 = (arg >> 8 ) & 0xff;    addr_3 = (arg >> 16) & 0xff;    addr_4 = (arg >> 24) & 0xff;          SD_CS=1;     SPI_Write(0xff); //提高兼容性,如果没有这里,有些SD卡可能不支持        SD_CS=0;         //SPI_Write(0xff);

        //发送    SPI_Write(cmd | 0x40);                         //分别写入命令    SPI_Write(addr_4);    SPI_Write(addr_3);    SPI_Write(addr_2);    SPI_Write(addr_1);    SPI_Write(crc);

        //等待响应,或超时退出   do   {      r1 = SPI_Read();     Retry--;     if(r1 == Res) break;   }    while(Retry>0);

        //在总线上额外增加个时钟,让SD卡完成剩下的工作    SPI_Write(0xFF);

        //返回状态值    if(Retry>0)  return 0;        //成功返回0    else         return 1;        //失败返回1}

    void SDCard_Init(void){    unsigned char Success_Flag = 0;    unsigned int  Count = 0;        //最高要大于600,所以选择16位    unsigned char r1 = 0xFF;

       SPI_Init();       SD_CS = 1;                      //CS抬高,补74以上时钟   for(Count=0;Count<10;Count++) SPI_Write(0xFF);    SD_CS = 0;                      //CS拉低,补16以上时钟   for(Count=0;Count<4;Count++)  SPI_Write(0xFF);      //-----------------------复位SD卡,并选择为SPI模式-----------------------------    do    {        SD_CS = 0;         //SD卡片选拉低   ,使能       for(Count=0;Count<4;Count++)  SPI_Write(0xFF); //补时钟(大于16个时钟)       r1 = SD_Send_Cmd(SD_CMD0,0x00000000,0x95,SD_IDLE);       Count++;    }    while((r1 != 0x00) && Count < 20);    SD_CS = 1;         //SD卡禁能    (void)SPI_Read();  // Dummy SPI cycle   //相当于补8个时钟        //--------------------------激活SD卡------------------------------------------    //备注:此种激活使用APP类命令,首先发送CMD55然后发送ACMD41    if(Count < 20)    {     SD_CS = 0;          //SD卡使能     do     {      r1 = SD_Send_Cmd(SD_CMD55,0x00000000,0x95,SD_IDLE);   //写CMD55      r1 = SD_Send_Cmd(SD_CMD41,0x00000000,0x95,SD_OK  );   //写ACMD41      Count++;     }      while((r1 != 0x00) && Count <300);       SD_CS = 1;         //SD卡禁能         (void)SPI_Read();  // Dummy SPI cycle    }    }

    //-----------------------设置读块写块长度-----------------------------unsigned char Set_BL(void){    unsigned int Count = 0;   //计数    unsigned int r1    = 0;   //响应    SD_CS = 0;          //SD卡使能   do     {    r1 = SD_Send_Cmd(SD_CMD16,0x00000200,0xFF,SD_OK);   //写CMD16,设置块长度为512    Count ++;   }    while((r1 != 0x00) && Count <100);     SD_CS = 1;          //SD卡使能       (void)SPI_Read();  // Dummy SPI cycle     if(Count <  100)  return 0;    //成功返回0    if(Count >= 100)  return 1;    //成功返回1}

    //求2的N次方 unsigned long Match_2N(unsigned int number){   unsigned int count  = 0;   unsigned int answer = 1;  for(count = 0;count < number;count++)  {    answer *= 2;  }    return answer;}

    //----------------------读CSD寄存器-----------------------unsigned char SD_Read_CSD(void){    unsigned int   count = 0;    unsigned char  r1 = 0;    volatile unsigned int   buffer[16] = {0};    volatile unsigned long C_SIZE = 0;    //设备大小    volatile unsigned long MULT   = 0;    //乘数    volatile unsigned long BL_LEN = 0;    //块长度    volatile float         SD_SIZE= 0;    //经过计算后的SD卡大小            SD_CS = 0;         //SD卡使能   do   {      r1 = SD_Send_Cmd(SD_CMD9,0x00000000,0xFF,SD_OK); //发送"CMD9"指令读取寄存器CSD,不需要CRC验证(CRC位置为0xFF)       count++;   }    while((r1 != 0x00) && count <200);         if(count>=200)    {       SD_CS = 1;           //SD卡禁能          (void)SPI_Read();    // Dummy SPI cycle        return 1;            //失败         }        while((SPI_Read() != 0xFE));        //等待数据起始位        for(count = 0;count <16; count++)    {      buffer[count] = SPI_Read();    }                            SD_CS = 0;            //SD卡禁能    (void)SPI_Read();  // Dummy SPI cycle        //-------------------------计算尺寸,块数量-------------------//       C_SIZE = (((buffer[6] & 0x03) << 10) + (buffer[7]<<2) + (buffer[8]>>6));    MULT   = Match_2N(((buffer[9]&0x03)<<1) + (buffer[10]>>7)+2);     BL_LEN = Match_2N((buffer[5] & 0x0f));        SD_SIZE  = (float)(((C_SIZE+1)*(BL_LEN)*(MULT))/1048576);

        return 0; }

     

    //-------------------------读块函数----------------------------------unsigned char SD_Read_BL(unsigned long Addr,unsigned char *buffer) { unsigned int r1 = 0; unsigned int count = 0;  SD_CS = 0;         //SD卡使能 do {     r1 = SD_Send_Cmd(SD_CMD17,Addr,0xFF,SD_OK); //发送"CMD17"指令按地址读块     count++; }  while((r1 != 0x00) && count <100);       if(count>=100)  {     SD_CS = 1;         //SD卡禁能         (void)SPI_Read();  // Dummy SPI cycle      return 1;            //失败       }      while((SPI_Read() != 0xFE));        //等待数据起始位      for(count = 0;count <512; count++)  {     *buffer++ = SPI_Read();  }    (void)SPI_Read();  // 读取两个校验码,然后扔掉  (void)SPI_Read();        SD_CS = 1;             //SD卡禁能    SPI_Write(0xFF);       return 0;}

     

    //-------------------------写块函数----------------------------------unsigned char SD_Write_BL(unsigned long Addr,unsigned char *buffer) {   unsigned int r1 = 0;   unsigned int count = 0;   unsigned char temp;   unsigned char i = 0;    SD_CS = 0;                    //SD卡使能   do   {     r1 = SD_Send_Cmd(SD_CMD24,Addr,0xFF,SD_OK); //发送"CMD24"指令按地址写块   }    while((r1 != 0x00) && count <100);         if(count>=100)    {      SD_CS = 1;           //SD卡禁能         //(void)SPI_Read();  // Dummy SPI cycle      return 1;            //失败         }            for(i=0;i<100;i++) //这里要插入若干时钟信号    {        SPI_Write(0xFF);    }

            SPI_Write(0xFE);        //写入数据起始位        for(count = 0;count <512; count++)    {        SPI_Write(*buffer++);        //写入数据起始位    }        SPI_Write(0xFF);     SPI_Write(0xFF);     //两个字节的CRC校验码,不用关心        temp = SPI_Read();        //读返回值        if((temp&0x1F)!=0x05)      {        SD_CS = 1;           //SD卡禁能           return 1;            //失败       }      while(SPI_Read()!=0xff);//等到SD卡不忙(数据被接受以后,SD卡要将这些数据写入到自身的FLASH中,需要一个时间)                         //忙时,读回来的值为0x00,不忙时,为0xff        SD_CS = 1;             //SD卡禁能      SPI_Write(0xFF);         //按照时序,补8个时钟        return 0;}

     

     

    有些地方有些改动。

    改动1.写命令时候,地址自动乘了512(右移9位),为了方便读块与写块。读写某个块时候,只要输入块就可以,不用折算为地址。

    改动2.写命令之前,将时钟拉高,补了8个时钟,再将其拉低,为了提高兼容性。(此处来源于振南老师的源代码,实际效果未知,我手上的卡基本都兼容。)

    改动3.读块时候,把512字节数据读出后多读了两次,为了读出CRC校验码,之后补了8个时钟。

    改动4.写块时候,写完512字节数据后,多写了两个0xFF作为校验码,之后增加了等待0x05响应(数据成功接受),之后拉高CS端口补了8个时钟。

     

     

          按照这个,可以完成SD卡的读写。如果要加入读写TXT与BMP,就要加入FAT32文件层的相关东西了。TXT不需要文件头,只需要在FAT表中标明数据启始与结束。BMP文件则需要特定的文件头,那么通过单片机写入文件头与数据,PC端口才能打开。如果想通过嵌入式设备需要读出BMP文件的数据,则要去掉文件头。

     

          其实8366用来驱动SD卡,程序都差不多,只是寄存器不同而已。之前8366的程序我已经贴出来了,但是以上要注意的地方再改动一下就可以了。完整的程序我还没有,以后做的话,在贴出来分享吧。

     

     


    最新回复(0)