|
![](static/image/common/ico_lz.png)
楼主 |
发表于 2009-10-17 10:14:54
|
显示全部楼层
每进入一个函数都有相关的打印信息,这样可以有利于查程序那里有问题.
/*-------------------------------------
1.本程序主要为了实现以下功能:
1)通过对SPI方式对SD卡进行读与写
2)简单的FAT操作
3)对写入的数据进行打印
2.编写日期:09.10.13
3.版本号:V1.0
4.作者:andyluo
----------------------------------------*/
#include<reg52.h> //添加头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define uchar unsigned char //宏定义数据类型
#define uint unsigned int
/*-------------------------------------
与指示灯有关的IO
--------------------------------------*/
sbit power=P0^4;
/*-------------------------------------
与SD卡通信有关的IO
---------------------------------------*/
sbit SCL=P1^5;//时钟信号
sbit CS=P1^6;//片选信号
sbit SI=P1^7;//数据输入
sbit SO=P3^3;//数据输出
//错误码定义
#define INIT_CMD0_ERROR 0X01
#define INIT_CMD1_ERROR 0X02
#define WRITE_BLOCK_ERROR 0X03
#define READ_BLOCK_ERROR 0X04
/*--------------------------------------
与通信和定时器相关的变量
---------------------------------------- */
uchar cn=0;
/*--------------------------------------
变量定义
-----------------------------------------*/
uchar is_init;//用于控制SPI的速度,通过为1与0进行选择
uchar xdata pbuf[512];//定义512个数据缓冲区
/*--------------------------------------
函数名称:delay()
函数作用:用于某些程序中的延时
函数特点:无返回值,带入口参数
-----------------------------------------*/
void delay(uint k)
{
uint m,n;
for(m=0;m<5;m++)
for(n=0;n<k;n++);
}
/*----------------------------------------
函数名称:IO_init()
函数作用:对相关IO进行初始化
函数特点:无返回值,无入口参数
------------------------------------------*/
void IO_init()
{
SCL=1;
CS=1;
SO=1;
power=1;
}
/*----------------------------------------
函数名称:send_byte(uchar i)
函数作用:对相关数据进行打印
函数特点:无返回值,带入口参数
--------------------------------------------*/
void send_byte(uchar i)
{
TI=0;
SBUF=i;
while(!TI);
TI=0;
}
/*-------------------------------------------------------
函数名:send_s()
功能:用户函数,发送一个字符
----------------------------------------------------------*/
void send_s(char *s)
{
int len=strlen(s);
int i;
for(i=0;i<len;i++)
send_byte(s);
send_byte(0x0d);
send_byte(0x0a);
}
/*-------------------------------
SPI写一个字节
----------------------------------*/
void spi_write(uchar x)//SPI写一个字节,其中is_init为1
{
uchar i;
for(i=0;i<8;i++)
{
SI=((x<<i)&0x80);
SCL=0;
if(is_init)
{
delay(8);
}
SCL=1;
if(is_init)
{
delay(8);
}
}
}
/*--------------------------------
SPI读一个字节
----------------------------------*/
uchar spi_read()
{
uchar temp=0,i;
SO=1;
for(i=0;i<8;i++)
{
SCL=0;
if(is_init)
{
delay(8);
}
// if(SO)
// {
// temp+=(0x80>>i);
// }
temp=(temp<<1)+(uchar)SO;
SCL=1;
if(is_init)//放慢速度
{
delay(8);
}
}
return(temp);
}
/*--------------------------------
向SD卡写命令
---------------------------------*/
uchar write_cmd(uchar *pcmd)//pcmd为命令字
{
uchar temp,i,time=0;
CS=1;
spi_write(0XFF);
CS=0;
for(i=0;i<6;i++) //发送6个字节的命令字节序列
{
spi_write(*pcmd++);
}
spi_read();
do
{
temp=spi_read();//一直读,直到读到的不是0XFF或者超时
time++;
}
while((temp==0xff)&&(time<100));
return temp;
}
/*------------------------------------
SD卡复位,进入SPI模式,使用CMD0命令
-------------------------------------*/
uchar SD_Reset()//SD卡复位
{
uchar time,temp,i;
uchar pcmd[]={0x40,0x00,0x00,0x00,0x00,0x95};//0号命令对应的6个字节
send_s("SD_Reset start\n");
is_init=1;//set is_init flag
CS=1;
for(i=0;i<0x0f;i++)//初始化时,首先要发送至少74个时钟信号,这是必须的
{
spi_write(0xff);//实质发了120个时钟(15*8)
}
send_s("send 120 clk succeed\n");
CS=0;
time=0;
do
{
temp=write_cmd(pcmd);
time++;
if(time==200)
{
return(INIT_CMD0_ERROR );//cmd0写入失败
send_s("send CMD0 ERROR\n");
}
}
while(temp!=0x01);
CS=1;
spi_write(0xff);//按照SD的操作时序在这里需要补8个时钟
send_s("send CMD0 succeed\n");
return 0;//返回0,说明SD卡复位操作成功
}
/*-------------------------------------
SD卡初始化,使用CMD1号命令
---------------------------------------*/
uchar SD_Init()//SD卡初始化,使用CMD1号命令
{
uchar time,temp;
uchar pcmd[]={0x41,0x00,0x00,0x00,0x00,0xff};
send_s("SD_Init start\n");
CS=0;
time=0;
do
{
temp=write_cmd(pcmd);
time++;
if(time==100)
{
return(INIT_CMD1_ERROR );
send_s("CMD1 ERROR\n");
}
}
while(temp!=0x00);
is_init=0;//初始化完成,将is_init设置为0,以提高后面数据的传输速度
send_s("send CMD1 succeed\n");
CS=1;
spi_write(0xff);
send_s("SD_Init succeed\n");
return 0;//说明初始化成功
}
/*-----------------------------
向SD卡扇区中写数据,每一个扇区中有512个字节
---------------------------------*/
uchar SD_write_sector(unsigned long addr,uchar *Buffer)
{
uchar temp,time;
uint i;
uchar pcmd[]={0x58,0x00,0x00,0x00,0x00,0xff};//向SD卡中写入24号命令
addr=addr<<9;//addr=addr+512,将块地址(扇区地址)转化为字节地址
pcmd[1]=((addr&0xff000000)>>24);//将字节地址写入到24号命令的时序中
pcmd[2]=((addr&0x00ff0000)>>16);
pcmd[3]=((addr&0x0000ff00)>>8);//SD卡最大容量4G
// pcmd[4]=(addr&0x000000FF);//此行为增加项
send_s("SD_write_sector start\n");
CS=0;
time=0;
do
{
temp=write_cmd(pcmd);
time++;
if(time==100)
{
return(temp);//命令写入失败
}
}
while(temp!=0);
for(i=0;i<100;i++)//这里要插入若干个时钟信号
{
spi_read();
}
spi_write(0xfe);//写入开始字节0XFE,后面就要写入512个字节
for(i=0;i<512;i++)//将缓冲区中要写入的512个字节写入到SD卡中
{
spi_write(*Buffer++);
}
spi_write(0xff);
spi_write(0xff);//两个字节CRC校验码
temp=spi_read();//读取返回值
if((temp&0x1f)!=0x05)//如果返回值为xxx00101,说明数据已经被接收
{
CS=1;
return(WRITE_BLOCK_ERROR );//写块数据失败
}
while(spi_read()!=0xff);//等待SD卡不忙(数据被接收以后,SD卡要将这些数据写入自身的FLASH
// 中,需要一定时间,忙时为0x00,不忙是为0xff, )
send_s("SD_write_sector succeed\n");
CS=1;
spi_write(0xff);//补8个时钟
return 0;
}
/*--------------------------------
读SD卡的一个扇区
---------------------------------*/
uchar SD_read_sector(unsigned long addr,uchar *Buffer)
{
uint i;
uchar time,temp;
uchar pcmd[]={0x51,0x00,0x00,0x00,0x00,0xff};//CMD17号命令
addr=addr<<9;
pcmd[1]=((addr&0xff000000)>>24);
pcmd[2]=((addr&0x00ff0000)>>16);
pcmd[3]=((addr&0x0000ff00)>>8);
send_s("SD_read_sector start\n");
CS=0;
time=0;
do
{
temp=write_cmd(pcmd);
time++;
if(time==100)
{
return(READ_BLOCK_ERROR);//读块失败
}
}
while(temp!=0);
while(spi_read()!=0xfe);//一直读,当读到0xfe时,说明后面是512个数据字节
for(i=0;i<512;i++)
{
Buffer=spi_read();
}
spi_read();
spi_read();
CS=1;
send_s("SD_read_sector succeed\n");
spi_write(0xff);//补8个时钟
return 0;
}
/*---------------------------------
串口初始化
-----------------------------------*/
void serial_init(void) //
{
TMOD = 0x21; //使用定时器1工作在方式2,做波率发生器,定时器0方式1
TH0 = 0X3c; //设置定时初值,定时20ms,1s采用20*50算法
TL0 = 0Xb0;
ET0 = 1; //开定时器0中断标志
TR0 = 1; //启动定时器0
TH1 = 0xfd; //11.0592M,9600
TL1 = 0xfd;
TR1 = 1; //启动定时器1
SCON=0X40; //串口工作在方式1,不允许接收
REN = 1; //允许串口接收
ES = 1; //允许串口中断
EA = 1; //开总中断
}
/*-------------------------
函数名称:main()
函数作用:主函数
---------------------------*/
void main()
{
int i=0;
serial_init();
send_s("serial_init!\n");
IO_init();
send_s("Port Init!\n");
SD_Reset();
SD_Init();
for(i=0;i<512;i++)
{
pbuf=i;//向缓冲区中写入数据
}
SD_write_sector(80,pbuf);//将缓冲区中512个字节的数据写入80扇区
for(i=0;i<512;i++)
{
pbuf=0;//清空数据缓冲区
}
SD_read_sector(80,pbuf);//从SD卡的第80个扇区中读取512个字节的数据
for(i=0;i<512;i++)
{
P2=~pbuf;//将缓冲区中的数据输出在P2口,
send_byte(pbuf);
delay(1000);
}
//P2=0X00;
while(1);
}
void timer0_int(void) interrupt 1 //中断程序,注意中断类型号
{
TH0=0X3c; //重新赋初值,并且必须要这样做,不然的话定时时间会不准确的
TL0=0Xb0;
cn++; //每当进入中断程序,count++,当有关参数设置正确后,程序会自动进入中断程序
if(cn==20)
{
cn=0;
power = !power; //本机运行指示灯闪烁
}
} |
|