#include <avr/io.h>
#include<util/delay.h>
#define SPI_DDR DDRB
#define SPI_PORT PORTB
#define SPI_PIN PINB
#define SPI_SS PB0
#define SPI_MOSI PB2
#define SPI_MISO PB3
#define SPI_SCK PB1
#define SET_SD_CS PORTG |= (1<<PG3)
#define CLR_SD_CS PORTG &= ~(1<<PG3)
#define SDCSDPORT() PORTG |= (1 << PG3) //SD卡的CS端口为高
#define SDCSDDDR() DDRG |= (1 << PG3) //SD卡的CD端口为输出
#define SET_SPI_MOSI PORTB |= (1 << PB2) //没数据时SD_MI应保持为高电平
///-------------------------------以下是子程序-----------------------------------//
void SPI_Low(void)
{//SPI低速模式
SPCR = 0;
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0) | (1 << SPR1);
//使能SPI,主机方式,MSB在前,模式0,128分频
}
void SPI_High(void)
{//SPI高速模式
SPCR = 0;
SPCR = (1 << SPE) | (1 << MSTR);
SPSR |= (1 << SPI2X);
//使能SPI,主机方式,MSB在前,模式0,4分频,2倍频
}
void SPI_Init(void)
{//SPI初始化
SPI_PORT = (1 << SPI_SS) | (1 << SPI_MISO); //将SS置位输出拉高,MISO输入带上拉
SPI_DDR = (1 << SPI_SS) | (1 << SPI_MOSI) | (1 << SPI_SCK);
//将SS SCK MOSI置为输出
}
uint8_t SPI_RW(uint8_t dat)
{//SPI读写1Byte(欲想读之必先与之)
SPDR = dat;
while(!(SPSR & (1 << SPIF)))
;
return (SPDR);
}
uint8_t SD_Write_Com(uint8_t *cmd)
{
uint8_t i,temp,retry;
SET_SD_CS; //关片选
SPI_RW(0XFF); //提高兼容性,如果没有这里,有些SD卡可能不支持
CLR_SD_CS; //开片选(后面的读写扇区可以省去开片选)
asm("nop");
for(i=0;i<6;i++)
{
SPI_RW(*cmd);
cmd++;
}
SPI_RW(0XFF); //写入指令后附加8个时钟脉冲
retry = 0;
do //不断地读
{
temp = SPI_RW(0XFF);
retry++;
}while((temp == 0XFF) && (retry < 254));
return (temp);
}
uint8_t SD_Init(void)
{
uint8_t i,temp;
uint16_t retry; //重试次数16位数据(适应大容量SD卡)
uint8_t CMD[] = {0x40,0x00,0x00,0x00,0x00,0x95};
SDCSDPORT(); //G口设为输出
SDCSDDDR();
SPI_Init();
SPI_Low(); //SPI初始化低速模式
for(i=0;i<200;i++) //Wait MMC/SD ready...
{
asm("nop");
}
retry=0;
do
{
SET_SD_CS;
for (i=0;i<0x0f;i++)
{
SPI_RW(0xff); //至少74个信号脉冲
}
temp = SD_Write_Com(CMD); //写入CMD0号命令,最大重试次数254
retry++;
if(retry == 600)
{
SET_SD_CS;
return(1); //写入CMD0号命令错误
}
}while(temp!=1);
CMD[0] = 0x41; //CMD1号命令
CMD[5] = 0xFF;
retry = 0;
do
{
temp = SD_Write_Com(CMD);
retry++;
if(retry > 60000) //写入CMD1号命令,最大重试次数60000
{
SET_SD_CS; //关闭片选
return(1); //写CMD1命令失败
}
}while(temp != 0);
SET_SD_CS; //关闭片选
SPI_High(); //初始化成功,SPI高速模式
SET_SPI_MOSI; //没数据时SD_MI应保持为高电平
return(0); //成功初始化
}
uint8_t SD_Write_Sector(uint32_t addr , uint8_t *buffer)
{//向SD卡中的指定地址的扇区写入512个字节,使用CMD24(24号命令,单块写命令)
uint8_t temp,retry;
uint16_t i;
uint8_t cmd24[] = {0x58,0x00,0x00,0x00,0x00,0xff};
addr <<= 9;
//addr = addr * 512 将块地址(扇区地址)转为字节地址(所以SD卡的最大容量为4G)
cmd24[1] = ((addr & 0xFF000000) >> 24);
cmd24[2] = ((addr & 0x00FF0000) >> 16);
cmd24[3] = ((addr & 0x0000FF00) >> 8);
//cmd24[4] = ((addr & 0x000000FF) >> 0); //可以省去
CLR_SD_CS;//打开SD卡片选
retry = 0;
do
{
temp = SD_Write_Com(cmd24);
retry++;
if(retry > 200)
{
return(1); //CMD24命令写入失败
}
}while(temp != 0);
SPI_RW(0XFF);
SPI_RW(0XFF);
SPI_RW(0XFF); //按照时序要求插入若干时序信号
SPI_RW(0XFE); //写入开始字节 0xfe,后面就是要写入的512个字节的数据
for (i=0;i<512;i++) //将缓冲区中要写入的512个字节写入SD卡
{
SPI_RW(buffer[i]);
}
SPI_RW(0xFF);
SPI_RW(0xFF); //两个字节的CRC校验码,不用关心
temp = SPI_RW(0XFF); //读取返回值
if((temp & 0x1F) != 0x05) //如果返回值是 XXX0 0101b 说明数据已经被SD卡接受了
{
SET_SD_CS;
return(1); //写块数据失败
}
while(SPI_RW(0XFF) != 0xff)
;
//等待SD卡不忙
//等到SD卡不忙(数据被接受以后,SD卡要将这些数据写入到自身的FLASH中,需要一个时间)
//忙时,读回来的值为0x00,不忙时,为0xff
SET_SD_CS; //关闭片选
SPI_RW(0XFF); //按照SD卡的操作时序在这里补8个时钟
SET_SPI_MOSI; //没数据时SD_MI应保持为高电平
return(0); //返回0,说明写扇区操作成功
}
uint8_t SD_Read_Sector(uint32_t addr , uint8_t *buffer)
{//从SD卡的指定扇区中读出512个字节,使用CMD17(17号命令)
uint16_t i;
uint8_t retry,temp;
uint8_t cmd17[]={0x51,0x00,0x00,0x00,0x00,0xFF}; //CMD17的字节序列
addr <<= 9; //sector = sector * 512 将块地址(扇区地址)转为字节地址
cmd17[1] = ((addr & 0xFF000000) >> 24);
cmd17[2] = ((addr & 0x00FF0000) >> 16);
cmd17[3] = ((addr & 0x0000FF00) >> 8);
//cmd17[4] = ((addr & 0x000000FF) >> 0);//可以省去
CLR_SD_CS; //打开片选
retry = 0;
do
{
temp = SD_Write_Com(cmd17); //写入CMD17
retry++;
if(retry > 250) //最大重试次数250
{
SET_SD_CS;
return(1); //读块失败
}
}while(temp != 0);
while (SPI_RW(0XFF) != 0xfe); //一直读,当读到0xfe时,说明后面的是512字节的数据了
for(i=0;i<512;i++) //将数据写入到数据缓冲区中
{
buffer[i] = SPI_RW(0XFF);
}
SPI_RW(0XFE);
SPI_RW(0XFE); //读取两个字节的CRC校验码,不用关心它们
SET_SD_CS; //SD卡关闭片选
SPI_RW(0xFF); //按照SD卡的操作时序在这里补8个时钟
SET_SPI_MOSI; //没数据时SD_MI应保持为高电平
return 0; //返回0,说明读扇区成功
}
int main()
{
int i=0;
unsigned int *buffer;
SD_Init();
for(i=0;i<512;i++)
{
buffer[i]=i; //向缓冲区中写入数据
}
SD_Write_Sector(80,buffer);
for(i=0;i<512;i++)
{
buffer[i]=0; //向缓冲区中写入数据
}
SD_Read_Sector(80,buffer);
_delay_us(1000);
while(1);
}
|