SD卡驱动(基于XS128)

之前一直在做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写1byte
void SPI_Write(unsigned char date)
{  
    while (!SPI0SR_SPTEF);      //等待发送器空
    (void)SPI0SR; 
    SPI0DRL = date;
    while (!SPI0SR_SPIF);       //等待发送完成
    (void)SPI0DRL;   
}  

//用SPI读1byte
unsigned 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的程序我已经贴出来了,但是以上要注意的地方再改动一下就可以了。完整的程序我还没有,以后做的话,在贴出来分享吧。

 

 

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页