jicheng0622

【原创】MC9S12XS128的SPI模式读写SD卡底层驱动(附例程)

0
阅读(14504)

博客大赛结束了,貌似飞思卡尔博客区里一下子冷清了不少,没了往日争奇斗艳百花盛开的热闹,少了往日拍手叫好的好文章和好测评,哎,为了飞思卡尔这把火能继续燃烧下去,大家的热情继续保持住,俺就继续加点柴。(咳咳,说的自己都有点觉着太伟大了,呵呵。)

说实话,自从去年11月份开始写博客,也写了十几篇了(在chinaaet真的算是少的了,不过保证都是原创的),如今突然觉着有点词穷了,当然创作和开源的热情还在,只是感觉一是自己能力有限,二是在科研方面思维不够严谨,当然也有一部分是刚放假回来没找到状态,哈哈,所以打算暂缓几天出Kinetis系列了,一个是想先找回放假前的状态,还有就是温故知新去了(钻到Kinetis的E文datasheet恶补一下)。

所以今天就拿出一个老古董的程序分享一下,说是老古董是因为这是我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)的“亲儿子”飞思卡尔了,然后。。。就有了然后了,哈哈。下面就是两种模式的接口图:

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

(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);
}
/*********************************************************/
//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;
}
/*********************************************************/
//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卡底层驱动,代码为早期写的可能不是很正规,有看不懂的地儿可以下面留言,呵呵。献丑之作,希望能对有需要的网友有所帮助。
附件为完整SD卡底层驱动C文件,需要的话可以直接下载~
Baidu
map