深入理解SD卡基礎(chǔ)原理以及內(nèi)部結(jié)構(gòu)的總結(jié)
1、 簡介:
SD卡(Secure Digital Memory Card)是一種為滿足安全性、容量、性能和使用環(huán)境等各方面的需求而設(shè)計的一種新型存儲器件,SD卡允許在兩種模式下工作,即SD模式和SPI模式,本 系統(tǒng)采用SPI模式。本小節(jié)僅簡要介紹在SPI模式下,STM32處理器如何讀寫SD卡,如果讀者如希望詳細了解SD卡,可以參考相關(guān)資料。
SD 卡內(nèi)部結(jié)構(gòu)及引腳如下圖所示:
SD卡內(nèi)部圖.JPG
2、SD卡管腳圖:
SD卡圖.JPG
3、SPI模式下SD各管腳名稱為:
sd 卡:
SPI模式下SD各管腳名稱 為.JPG
注: 一般SD有兩種模式:SD模式和SPI模式,管腳定義如下:
(A)、SD MODE 1、CD/DATA3 2、CMD 3、VSS1 4、VDD 5、CLK 6、VSS2 7、DATA0 8、DATA1 9、DATA2
(B)、SPI MODE 1、CS 2、DI 3、VSS 4、VDD 5、SCLK 6、VSS2 7、DO 8、RSV 9、RSV
SD 卡主要引腳和功能為:
CLK:時鐘信號,每個時鐘周期傳輸一個命令或數(shù)據(jù)位,頻率可在0~25MHz之間變化,SD卡的總線管理器可以不受任何限制的自由產(chǎn)生0~25MHz 的頻率;
CMD:雙向命令和回復線,命令是一次主機到從卡操作的開始,命令可以是從主機到單卡尋址,也可以是到所有卡;回復是對之前命令的回答,回復可以來自單 卡或所有卡;
DAT0~3:數(shù)據(jù)線,數(shù)據(jù)可以從卡傳向主機也可以從主機傳向卡。
SD卡以命令形式來控制SD卡的讀寫等操作??筛鶕?jù)命令對多塊或單塊進行讀寫操作。在SPI模式下其命令由6個字節(jié)構(gòu)成,其中高位在前。SD卡命令 的格式如表1所示,其中相關(guān)參數(shù)可以查閱SD卡規(guī)范。
4、MicroSD卡管腳圖:
MicroSD卡管腳圖.JPG
5、MicroSD卡管腳名稱:
MicroSD卡管腳名 稱.JPG
SD 卡與MicroSD卡僅僅是封裝上的不同,MicroSD卡更小,大小上和一個SIM卡差不多,但是協(xié)議與SD卡相同。
一般我們用單片機操作SD 卡時,都不需要對FAT分區(qū)表信息做處理,原因如下:
1)、操作FAT分區(qū)表要增加程序代碼量、增加SRAM的消耗,對于便攜應(yīng)用來說代碼大小和 占用SRAM的多少至關(guān)重要。
2)、即使我們對FAT分區(qū)表不做任何了解,實際上我們一樣可以向SD卡上寫入數(shù)據(jù),這就表明使用FAT對我們做數(shù) 據(jù)存儲應(yīng)用來說如同雞肋。
3)、耗費大量經(jīng)歷和時間去了解FAT分區(qū)表對于我們做嵌入式軟件開發(fā)的人來說有些得不償失。
4)、SD卡支持 兩種操作模式,SD模式和SPI模式,SPI模式做SD數(shù)據(jù)操作時根本不需要知道FAT,這時候SD卡對于我們來說實際上就是個大的、快速的、方便的、容 量可變的外部存儲器。
基于以上原因,一般情況下對SD卡的操作只需要了解SPI通訊就可以了,而現(xiàn)在大部分單片機都有SPI接口,那么操作SD卡 易如反掌。
以下是做SD卡試驗時使用的電路圖:
SD卡試驗時使用的電路 圖.JPG
SD_CS/ 連接到單片機的片選SD管腳,只有單片機設(shè)置SD_CS/為低電平時才可以操作SD卡。
MOSI連接單片機SPI總線的MOSI管腳(SPI數(shù)據(jù) 輸入),單片機從這個管腳讀取SD卡內(nèi)的數(shù)據(jù)。
MISO連接單片機SPI總線的MISO管腳(SPI數(shù)據(jù)輸出)、單片機通過這個管腳向SD卡內(nèi)寫 入數(shù)據(jù)。
SCK連接單片機SPI總線的SCK(SPI時鐘)
SD管腳實際上在SD卡內(nèi)部連接到了GND,當SD插座上沒插入SD卡時,單 片機從這個管腳能讀到高電平(前提是使用單片機內(nèi)部上拉輸入,或者外部增加一個上拉電阻),一旦插入SD卡,這個管腳就變成低電平,這個功能用來檢測是否 插入SD卡。
RSV1和RSV2是保留功能管腳,不需要操作。
MicroSD卡的連接和SD卡大同小異,只是MicroSD卡比SD卡少 一個GND管腳,所以不能使用上面做的這種插入卡的檢測,實際上現(xiàn)在很多SD卡/MicroSD卡插座都有插入檢測管腳,當然,一分錢一分貨,價格上當然 也要貴一些
順便提一下,普通SD卡插座最多5塊錢。
SPI命令格式
Byte 1 | Byte2-5 | Byte 6 |
7 | 6 | 5 0 | 31 0 | 7 | 0 |
0 | 1 | Command | Command Argument | CRC | 1 |
以下是一個簡單的測試SD卡讀 寫的程序,程序是基于Atmega128單片機編寫的,對于Atmega的其他單片機僅需要做管腳改動就可以使用,其他單片機更改要更大。
sd.h
//******************************************************************
//SPI 各線所占用的端口
#define SD_SS PB6
#define SD_SCK PB1
#define SD_MOSI PB2
#define SD_MISO PB3
//******************************************************************
#define SD_DDR DDRB
#define SD_PORT PORTB
#define SD_PIN PINB
#define SD_SS_H SD_PORT |= (1<
#define SDSS_L SD_PORT &= ~(1<
#define SD_SCK_H SD_PORT |= (1<
#define SD_SCK_L SD_PORT &= ~(1<
#define SD_MOSI_H SD_PORT |= (1<
#define SD_MOSI_L SD_PORT &= ~(1<
#define SD_MISO_IN (SD_PIN&(1<
//-------------------------------------------------------------
// 錯誤號
//-------------------------------------------------------------
#define INIT_CMD0_ERROR 0xFF
#define INIT_CMD1_ERROR 0xFE
#define WRITE_BLOCK_ERROR 0xFD
#define READ_BLOCK_ERROR 0xFC
#define TRUE 0x01
//-------------------------------------------------------------
// MMC/SD 命令(命令號從40開始,只列出基本命令,并沒有都使用)
//-------------------------------------------------------------
#define SD_RESET 0x40 + 0
#define SD_INIT 0x40 + 1
#define SD_READ_CSD 0x40 + 9
#define SD_READ_CID 0x40 + 10
#define SD_STOP_TRANSMISSION 0x40 + 12
#define SD_SEND_STATUS 0x40 + 13
#define SD_SET_BLOCKLEN 0x40 + 16
#define SD_READ_BLOCK 0x40 + 17
#define SD_READ_MULTI_BLOCK 0x40 + 18
#define SD_WRITE_BLOCK 0x40 + 24
#define SD_WRITE_MULTI_BLOCK 0x40 + 25
//片選關(guān)(MMC/SD-Card Invalid)
#define SD_Disable() SD_SS_H
//片選開 (MMC/SD-Card Active)
#define SD_Enable() SD_SS_L
SD_TEST.C
//****************************************************************************************/
//ICC-AVR application builder : 03-5-20 8:39:11
// Target : M128
// Crystal: 3.6864Mhz
#include
#include
#include 'sd.h'
void uart0_init(void);
void putchar(unsigned char content);
void putstr(unsigned char *s);
void SD_Port_Init(void);
unsigned char SD_Init(void);
unsigned char SD_write_sector(unsigned long addr,unsigned char *Buffer);
unsigned char SD_read_sector(unsigned long addr,unsigned char *Buffer);
unsigned char SPI_TransferByte(unsigned char byte);
unsigned char Write_Command_SD(unsigned char cmd,unsigned long address);
unsigned long SD_find(void);
//**************************************************************************
// 串口調(diào)試程序
//**************************************************************************
void uart0_init(void)
{
UCSR0B = 0x00; //disable while setting baud rate
UCSR0A = 0x00;
UCSR0C = 0x06; // 00000110 UART0設(shè)置為異步模式、無奇偶校驗、1位停止位、8位數(shù)據(jù)位
UBRR0L = 0x17; //set baud rate lo
UBRR0H = 0x00; //set baud rate hi 設(shè)置UART0口通信速率9600
UCSR0B = 0x18;
}
void putchar(unsigned char content)
{
while(!(UCSR0A & (1 << UDRE0))); /* 判斷上次發(fā)送有沒有完成 */
UDR0 = content; /* 發(fā)送數(shù)據(jù) */
}
void putstr(unsigned char *s)
{
while(*s)
{
putchar(*s);
s++;
}
}
//****************************************************************************
// 端口初始化
void SD_Port_Init(void)
//****************************************************************************
{
SD_PORT |= (1<<
SD_DDR |= (1<<<
SD_DDR &= ~(1<
}
//****************************************************************************
// 初始化 MMC/SD 卡為SPI模式
unsigned char SD_Init(void)
//****************************************************************************
{
unsigned char retry,temp;
unsigned char i;
SPCR=0x53; //設(shè)定SPI為128分頻,慢速進行初始化
SPSR=0x00;
for (i=0;i<0x0f;i++)
{
SPI_TransferByte(0xff); //延遲74個以上的時鐘
}
SD_Enable(); //開片選
SPI_TransferByte(SD_RESET); //發(fā)送復位命令
SPI_TransferByte(0x00);
SPI_TransferByte(0x00);
SPI_TransferByte(0x00);
SPI_TransferByte(0x00);
SPI_TransferByte(0x95);
SPI_TransferByte(0xff);
SPI_TransferByte(0xff);
retry=0;
do{
temp="Write"_Command_SD(SD_INIT,0); //發(fā)送初始化命令
retry++;
if(retry==100) //重試100次
{
SD_Disable(); //關(guān)片選
return(INIT_CMD1_ERROR); //如果重試100次失敗返回錯誤號
}
}while(temp!=0);
MSD_Disable(); //關(guān)片選
SPCR=0x50; //設(shè)置SPI為2分頻。進行高速讀寫
SPSR=0x01;
return(TRUE); //返回成功
}
//****************************************************************************
// 發(fā)送命令給 MMC/SD卡
//Return: 返回MMC/SD卡對命令響應(yīng)的第2字節(jié),作為命令成功判斷
unsigned char Write_Command_SD(unsigned char cmd,unsigned long address)
//****************************************************************************
{
unsigned char tmp;
unsigned char retry="0";
SD_Disable();
SPI_TransferByte(0xFF);
SD_Enable();
SPI_TransferByte(cmd); //將32位地址進行移位作為地址字節(jié)
SPI_TransferByte(address>>24);
SPI_TransferByte(address>>16);
SPI_TransferByte(address>>8);
SPI_TransferByte(address);
SPI_TransferByte(0xFF);
SPI_TransferByte(0xFF);
do{
tmp = SPI_TransferByte(0xFF); //發(fā)送8個時鐘接受最后一個字節(jié)
retry++;
}while((tmp==0xff)&&(retry<8));
return(tmp);
}
//****************************************************************************
// 寫一個扇區(qū)(512Byte) to MMC/SD-Card
//如果寫完成返回TRUE
unsigned char SD_write_sector(unsigned long addr,unsigned char *Buffer)
//****************************************************************************
{
unsigned char temp;
unsigned int i;
SPI_TransferByte(0xFF); //延遲8個時鐘
SD_Enable(); //開片選
temp = Write_Command_MMC(MMC_WRITE_BLOCK,addr<<9); //發(fā)送寫扇區(qū)命令
if(temp != 0x00)
{
SD_Disable();
return(temp);
}
SPI_TransferByte(0xFF);
SPI_TransferByte(0xFF);
SPI_TransferByte(0xFE);
for (i=0;i<512;i++)
{
SPI_TransferByte(*Buffer++); //發(fā)送512字節(jié)數(shù)據(jù)
}
//CRC-Byte
SPI_TransferByte(0xFF); //Dummy CRC
SPI_TransferByte(0xFF); //CRC Code
temp = SPI_TransferByte(0xFF); //讀SD卡運行響應(yīng)
if((temp & 0x1F)!=0x05) //如果最后4位為0101,為操作成功。否則為操作失敗。
{
SD_Disable();
return(WRITE_BLOCK_ERROR); //返回錯誤
}
while (SPI_TransferByte(0xFF) != 0xFF);
SD_Disable();
return(TRUE); //返回成功
}
//****************************************************************************
// 讀512字節(jié) from MMC/SD-Card
//如果成功返回TRUE
unsigned char SD_read_sector(unsigned long addr,unsigned char *Buffer)
//****************************************************************************
{
unsigned char temp;
unsigned int i;
unsigned char data;
SPI_TransferByte(0xff);
MMC_Enable();
temp = Write_Command_SD(SD_READ_BLOCK,addr<<9);//發(fā)送讀扇區(qū)命令
if(temp != 0x00)
{
SD_Disable();
return(READ_BLOCK_ERROR); //返回錯誤號
}
while(SPI_TransferByte(0xff) != 0xfe);
for(i=0;i<512;i++)
{
data = SPI_TransferByte(0xff); //存數(shù)據(jù)
*Buffer++=data;
}
SPI_TransferByte(0xff); //讀CRC碼
SPI_TransferByte(0xff); //讀CRC碼
SD_Disable();
return(TRUE); //返回成功
}
//**************************************************************************
// 查找數(shù)據(jù)開始標志(預設(shè)DATASTART)根據(jù)實際需要刪改
//**************************************************************************
unsigned long SD_find(void)
{
unsigned long tmp="400";
unsigned char data[512];
do
{
SD_read_sector(tmp,data); //從0扇區(qū)開始查找
tmp++; //查找DATASTART
}while(!((data[0]=='D')&&(data[1]=='A')&&(data[2]=='T')&&(data[3]=='A')&&(data[4]=='S')&&(data[5]=='T')&&(data[6]=='A')&&(data[7]=='R')&&(data[8]=='T')));
return tmp; //返回開始標志的下一個扇區(qū)
}
//**************************************************************************
// 發(fā)送一個字節(jié)
//**************************************************************************
unsigned char SPI_TransferByte(unsigned char byte)
{
SPDR = byte;
while (!(SPSR & 0x80)); //檢測線路是否空閑
return SPDR;
}
//**************************************************************************
// 主程序例子
//**************************************************************************
void main(void)
{
unsigned long temp;
unsigned char data[512];
unsigned char data2[512]={'sssssssssssssssssssssssss'};
unsigned char comm1[]={'\r\nhello world\r\n'};
unsigned char comm2[]={'\r\nSD_INIT OK\r\n'};
uart0_init();
SD_Port_Init(); //端口初始化
if(SD_Init()== 0x01)
{ //SD卡初始化,并讀取返回值
putstr(comm2);
}
temp="SD"_find(); //查找DATASTART數(shù)據(jù)開始標志,返回下一扇區(qū)地址
SD_read_sector(1001,data); //讀取temp地址的512字節(jié)數(shù)據(jù),512字節(jié)數(shù)據(jù)存入data數(shù)組
putstr(data);
SD_write_sector(temp,data2); //將data2數(shù)組512字節(jié)數(shù)據(jù)寫入temp扇區(qū)
}
測試程序很簡單,僅僅是做了一下讀寫SD卡的測試。
關(guān)于SD卡的幾點注意事項。
1、無論我們愿意不愿意,SD卡每次讀寫數(shù)據(jù)的最小單位是1個扇區(qū),即512個字節(jié)。
2、SD卡與單片機連接的 SPI總線不能太長,要盡量短。這樣的好處是速度可以更快,也不容易出錯。
3、雖然我們并不關(guān)心FAT文件表,但是我們?nèi)匀灰P(guān)心SD卡的存儲結(jié)構(gòu),如果我們不想使用PC機來讀取保存在SD卡上的數(shù)據(jù)那我們就不用關(guān)心SD存儲結(jié)構(gòu)了。但,作為一個大容量的可移動存儲設(shè)備,不能用PC機來讀取是個很大的遺憾,我解決這個遺憾的方法如下:
3-1、因為我不了解FAT復雜的結(jié)構(gòu),所以我做的程序沒法去按照FAT表的各項功能來進行創(chuàng)建文件、刪除文件、創(chuàng)建目錄等等操作。
3-2、雖然我們的單片機不能創(chuàng)建文件,但是PC機是可以創(chuàng)建文件的??!所以我使用PC機將SD卡格式化,之后在SD卡上創(chuàng)建一個大文件,比如我的128M的SD卡上我建立了一個100M的文件。這里需要注意一下,一般使用windows創(chuàng)建文件的功能時是沒有辦法指定創(chuàng)建文件的大小的,空文件就是0個字節(jié)的長度,而我們是需要一個固定長度的文件的,所以我用VC編寫了一個小軟件,這個軟件可以為我創(chuàng)建一個100M長度的空文件,記住,這點很重要:一個固定長度的空文件
3-3、雖然我們建立了個文件在SD卡上,可是我們因為不去了解FAT表,所以我們一樣不知道這個文件到底位于SD卡的什么地方,不要以為它會在0字節(jié)的地方開始,為了找到這個文件的開始位置,我們可以在建立的那個空文件的開頭寫上幾個字符,比如我程序里面寫的“DATASTART”,接下來我們要做的就是一個扇區(qū)一個扇區(qū)的去找這個幾個特殊的字符,這是個笨方法,但卻是最簡單直觀的方法。這個方法有兩個缺點:a、如果文件建立在整個SD卡的后面,那找到這個文件需要漫長的等待。b、如果碰巧某個文件里面也有我們定義的那個特殊字符串的話,那就亂套了!不過好在我們使用的SD卡一般都是專用的,并不能拿去做其他應(yīng)用,比如從公司copy點文件回家之類的,那就能保證這個SD卡上文件的簡單性,即只有我們需要的那個文件,其他文件并不存在,而且這個文件肯定會從SD卡開始的那些扇區(qū)中的某一個開始。這樣說來的話找到這個字符串也不是那么慢嘛!^_^。不過這里要建議一下,在使用SD卡之前最好用windows將它完全格式話一下。
3-4、一旦我們找到了我們要寫入文件的起始位置(它一般表示為一個扇區(qū)號),那我們就可以在這個起始扇區(qū)的下一個扇區(qū)寫入數(shù)據(jù)了。
4、OK,看起來很簡單!有了這種存儲方式我們還需要IIC接口的 EEPROM干嗎呢?
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請
點擊舉報。