|
不容易啊,整整4天时间都在搞单片机读写SD卡的程序,由于技术浅薄走了很多弯路.不过总算成功读写SD卡扇区数据了.
测试环境:ATMEGA16 16M晶振
总结下读写SD卡应该注意的地方:
1.首先烧录器一定要加上校检FLASH.
有好几次由于HEX文件比较大时,是不是的会发生烧录错误数据,导致程序最后跑飞,为此被误导了好长时间,加上校检FLASH后虽说烧录速度明显慢了,但是烧录的心安理得
2.SD卡上电复位,所以每当调试完程序后都应重插下SD卡
3.供电不足,导致SD卡只能读不能写.
一开始没找到合适的3.3V电源,就随便找了个3.3V稳压管加个小电阻给SD卡供电,导致我调试了将近两天的时间也没发现是这出了问题,SD卡写数据时比读数据是提供的电流要大,导致程序运行完全正常,SD卡返回的指令也完全正确,程序成功返回写数据完成后但在WINHEX下观看数据完全没有被写入.硬件基础差导致我浪费了这么长时间,还好没有放弃.最后更换了SD卡供电电源后便可正确读写SD卡扇区了.(我的3.3V电源是用5V减去3个1n4148串联分压的电压,用万用表测了下大约是3.3V)
4.最后说下让金士顿给害惨了
我有一个2G的金士顿SD卡,一开始我就是用我的这个卡调试程序,发现怎么调试也不好使.后来找朋友借了个256M的小卡操作,完全可以正确读写扇区.后来决定再用1天的时间解决这个问题,从早忙活到黑,程序改了一遍又一遍后来在网上查了下金士顿上面的一个"SDC"的标记就完全明白了.原来SDC 它本身是MMC卡做成跟SD形同的脚位.晕了 白耽误1天.
最后总结下:只要SD卡看仔细了,我用ATMEGA16 上16M晶振,在初始化SD卡时设成SPI 128分频完全可以成功初始化SD卡,初始化后,便可设置SPI高速模式(2分频 8M).要是大家担心初始化频率过高可以完全用一个小晶振试验.当我用6M晶振试验我的SD卡时发现我的金士顿还是照常不能初始化就明白不是SPI频率的事.本来半天可以完成的工作还得我为此付出了4天时间,希望有学习SD卡的朋友千万别犯我的错误.
SD卡扇区可以正常读写后就该学FAT32文件系统了,学会了就可以试着做个MP3了,也为我的图像采集系统项目打些基础.
![](http://cache.amobbs.com/bbs_upload782111/files_17/ourdev_469853.JPG)
成功初始化SD卡 (原文件名:成功初始化.JPG)
![](http://cache.amobbs.com/bbs_upload782111/files_17/ourdev_469854.JPG)
读出的部分0扇区数据 (原文件名:读出的部分0好扇区.JPG)
![](http://cache.amobbs.com/bbs_upload782111/files_17/ourdev_469855.JPG)
winhex显示的0扇区数据 (原文件名:WINHEX0扇区的验证.JPG)
全部测试代码ourdev_469856.rar(文件大小:29K) (原文件名:全部代码.rar)
下面是我的还没有整理的源代码,只是简单测试下读写扇区.
#include"config.h"
//ATMEGA16 SPI引脚
#define SPI_DDR DDRB
#define SPI_PORT PORTB
#define SPI_PIN PINB
#define SPI_SS PB4
#define SPI_MOSI PB5
#define SPI_MISO PB6
#define SPI_SCK PB7
#define SET_SD_CS PORTA |= (1<<PA0)
#define CLR_SD_CS PORTA &= ~(1<<PA0)
#define SET_SPI_MOSI PORTB |= (1 << PB5) //没数据时SD_MI应保持为高电平
uint8_t buf[512]; //数据缓冲区
uint16_t k;
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};
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);
}
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 = 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()
{
_delay_ms(50);
PORTA |= (1 << PA0);
DDRA |= (1 << PA0);
for(k=0;k<512;k++)
{
buf[k] = k;
}
USART_Init();
USART_Send_Str("SD卡测试程序\r\n");
if(SD_Init() == 0)
USART_Send_Str("SD卡初始化成功\r\n");
else
USART_Send_Str("SD卡初始化失败\r\n");
/*if(SD_Write_Sector(80 , buf) == 0)
{
USART_Send_Str("SD卡写扇区成功\r\n");
for(k=0;k<512;k++)
{
USART_Send_Char(buf[k]/100+0x30);
USART_Send_Char(buf[k]/10%10+0x30);
USART_Send_Char(buf[k]%10+0x30);
USART_Send_Str("\r\n");
}
}
else
USART_Send_Str("SD卡写扇区失败\r\n");*/
for(k=0;k<512;k++) //清零缓冲区
{
buf[k] = 0;
}
if(SD_Read_Sector(0 , buf) == 0)
{
USART_Send_Str("SD卡读扇区成功\r\n");
for(k=0;k<512;k++)
{
USART_Send_Char(buf[k]/100+0x30);
USART_Send_Char(buf[k]/10%10+0x30);
USART_Send_Char(buf[k]%10+0x30);
USART_Send_Str("\r\n");
}
}
else
USART_Send_Str("SD卡读扇区失败\r\n");
while(1)
{
asm("nop");
}
} |
阿莫论坛20周年了!感谢大家的支持与爱护!!
曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……
|