打印
[其他MCU]

MC9S12XS128的SPI模式读写SD卡底层驱动

[复制链接]
1158|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
energy1|  楼主 | 2015-3-21 22:47 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

今天就拿出一个老古董的程序分享一下,说是老古董是因为这是我N年前(当时写的DG128后来我又移植到XS128了)给俺们学校智能车工作室写的SD卡驱动(当时时势所逼呀,迫切需要建立一个SD卡调试系统,事后证明当时的决定还是很英明神武的,呼呼,看着小弟们现在调车这么方便,想起俺们当年全凭感觉调车,那个惨啊,哎,又往事重提了...),其实还写了一个简单的FAT32的文件系统,不过由于文件系统介绍起来太麻烦(其实还有一个原因是当时自己水平太差,代码风格太烂,怕献丑,咳咳)这里就不提了,本篇就只介绍SPI模式读写SD卡的底层驱动,下面进入正题:

(1)首先简单介绍一下SD卡,SD卡(Secure Digital Memory Card)是一种基于半导体技术的快速闪存**卡,被广泛用于数码相机,手机,PDA等便携式设备上,拥有高容量**,数据传输率快,灵活性好和安全性强等特点,哈哈,更细致的介绍搜搜百度百科就知道了。

(2)接下来就介绍设计到技术上的问题了,对SD卡的操作有两种模式,一种是SD模式,一种是SPI模式。SD模式速度快,安全性好,不过它需要引脚多而且主要是驱动起来复杂,当然有些片子自带SD模式的硬件接口(Kineits一些系列就自带)这样就方便多了,不过对HCS12XS这类没有SD接口系列来说就麻烦了,而SPI模式就方便许多了,虽然速度上没有SD模式快,不过贵在目前大多数片子都自带SPI硬件资源,就更别提SPI总线定义的祖宗(Motorola)的“亲儿子”飞思卡尔了,然后。。。就有了然后了,哈哈。下面就是两种模式的接口图:


相关帖子

沙发
energy1|  楼主 | 2015-3-21 22:48 | 只看该作者

(3)硬件接口电路,由于SD卡是3.3v供电的,而XS128IO电平是标准的TTL电平的,这就涉及到电平匹配的问题了,最安全可靠的办法是在MCU和SD卡之间加电平转换电路(专门的芯片或者加三极管转换),不过为了简化电路,一般在数据线之间串接10~100欧姆的电阻就可以使用了,实际应用中证明没有问题。硬件电路如下图:


使用特权

评论回复
板凳
energy1|  楼主 | 2015-3-21 22:48 | 只看该作者

(4)驱动代码部分,为最底层驱动部分,经过测试,该代码在SPI10M时钟以下数据传输正常,高于10M不正常,所以建议如果要求不高的话选择5M左右的时钟,下面为主要代码部分:

#include"mc9s12xs128.h"

#include"Sd_System.h"

#define  sd_cs PTJ_PTJ6

void spi_init(void)
{

  MODRR_MODRR4=1;    //把SPI0加到PM口
// RDRM|=0x34;      //PORTM的2,4,5口将驱动能力1/5,用来驱动sd卡,减少损坏SD卡的几率
  DDRJ_DDRJ6=1;     //PTJ的6口为输出用于SS

  SPI0CR1=0x5e;
  SPI0BR  = 0x07;    //慢速,用于sd初始化
  //SPI0BR=0x00;     //快速,用于spi数据传输,越快越好
}
/*********************************************************/
//function:spi_write spi_read
//description:spi模式读写数据(查询方式)
/***********************************************************/
void spi_write(byte jc_data)
{
  while(!SPI0SR_SPTEF);
  (void)SPI0SR;      //刷新标志
  SPI0DRL=jc_data;   // 清除标志
  while (!SPI0SR_SPIF);
  return( (void)SPI0DRL);
}
byte spi_read(byte jc_data)
{
  while(!SPI0SR_SPTEF);
  (void)SPI0SR;      //刷新标志
  SPI0DRL=jc_data;  // 清除标志
  while (!SPI0SR_SPIF);
  return(SPI0DRL);
}
/*********************************************************/
//function:jc_sd_cmd
//description:给SD卡发送命令
//input: 48个字节,前8位为CMD指令,接着32位为地址参数,
//       最后8位为CRC校验(该模式在SPI模式下无效)
/***********************************************************/
byte jc_sd_cmd(byte cmd,dword arg,byte crc)
{
byte r1,retry=0;

spi_write(0xff);
sd_cs=0;
spi_write(cmd|0x40);
arg=arg << 9;
spi_write(arg >> 24); //传送32位地址
spi_write(arg >> 16);
spi_write(arg >> 8);
spi_write(arg);
spi_write(crc);
do
{
    r1=spi_read(0xff);
    retry++;
    if(retry==250)
    {
      retry=0;
      break;
    }

}while(r1==0xff);

sd_cs=1;
spi_write(0xff);
return(r1);
}


使用特权

评论回复
地板
energy1|  楼主 | 2015-3-21 22:48 | 只看该作者
/*********************************************************/
//function: sd_init
//description:sd卡初始化函数
/***********************************************************/
byte sd_init(void)
{

  byte i=0,r1=0;
  word retry=0;
  for(i=0;i<10;i++)
      spi_write(0xff);      //等待74个时钟周期,sd工作电压升至正常值
  do
    {
       //发送CMD0,让SD卡进入IDLE状态

       r1 = jc_sd_cmd(0,0,0x95);
       retry++;

    } while ((r1 != 0x01) && (retry < 1000));
  if (retry==1000) return 1;
  retry=0;
  //发送cmd55+acmd41使sd卡工作在spi模式
do
    {
        r1=jc_sd_cmd(55,0,0xff);
       if(r1==0x01)
        r1=jc_sd_cmd(41,0,0xff);
        retry++;
    } while ((r1 != 0x00) && (retry < 1000));
   if (retry==1000) return 1;
  retry=0;

  SPI0CR1=0;
  SPI0BR  = 0x00;         //sd初始化完后转入快速模式
  SPI0CR1=0x5e;

  return 0;
}
/*********************************************************/
//function:sd_rdata
//description:从sd卡读取指定长度数据
/***********************************************************/
byte sd_rdata(byte * data,word len)
{
  byte r1=0,retry=0;
  sd_cs=0;
  do
  {
    r1=spi_read(0xff);
    retry++;
  }while(r1!=0xfe&&retry<200);
  if(retry==200) return r1;
  retry=0;
  while(len--)
  {
    * data=spi_read(0xff);
    data++;
  }
  spi_write(0xff);          //这两句是读伪指令
  spi_write(0xff);
  sd_cs=1;
  spi_write(0xff);
  return 0;
}

使用特权

评论回复
5
energy1|  楼主 | 2015-3-21 22:49 | 只看该作者
/*********************************************************/
//function:sd_writesingleblock
//description:sd卡写单块数据
//input:预留ram区的指针,扇区号(注意为物理扇区号)
/***********************************************************/
byte sd_writesingleblock(byte *data,dword sector)
{
  byte r1=0,retry=0;
  word i=0;
  r1=jc_sd_cmd(24,sector,0);

  if(r1!=0) return r1;
  sd_cs=0;
  spi_write(0xff);       //先发三个空数据等待sd卡准备好
  spi_write(0xff);
  spi_write(0xff);
  spi_write(0xfe);     //发送起始令牌
  for(i=0;i<512;i++)
  {
    spi_write(*data++);
  }

  spi_write(0xff);
  spi_write(0xff);
  r1=spi_read(0xff);
  if((r1&0x1f)!=0x05)
  return (r1);
  while(spi_read(0xff)==0);
  sd_cs=1;
  spi_write(0xff);
  return 0;  
}

    至此为本篇SD卡底层驱动,代码为早期写的可能不是很正规

使用特权

评论回复
6
FSL_TICS_ZJJ| | 2015-4-10 15:25 | 只看该作者
非常感谢你的经验分享 !

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

94

主题

422

帖子

10

粉丝