1、 簡介: SD卡(Secure Digital Memory Card)是一種為滿足安全性、容量、性能和使用環(huán)境等各方面的需求而設(shè)計的一種新型存儲器件,SD卡允許在兩種模式下工作,即SD模式和SPI模式,本 系統(tǒng)采用SPI模式。本小節(jié)僅簡要介紹在SPI模式下,STM32處理器如何讀寫SD卡,如果讀者如希望詳細(xì)了解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:雙向命令和回復(fù)線,命令是一次主機到從卡操作的開始,命令可以是從主機到單卡尋址,也可以是到所有卡;回復(fù)是對之前命令的回答,回復(fù)可以來自單 卡或所有卡; 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,當(dāng)SD插座上沒插入SD卡時,單 片機從這個管腳能讀到高電平(前提是使用單片機內(nèi)部上拉輸入,或者外部增加一個上拉電阻),一旦插入SD卡,這個管腳就變成低電平,這個功能用來檢測是否 插入SD卡。 RSV1和RSV2是保留功能管腳,不需要操作。 MicroSD卡的連接和SD卡大同小異,只是MicroSD卡比SD卡少 一個GND管腳,所以不能使用上面做的這種插入卡的檢測,實際上現(xiàn)在很多SD卡/MicroSD卡插座都有插入檢測管腳,當(dāng)然,一分錢一分貨,價格上當(dāng)然 也要貴一些 順便提一下,普通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ā)送復(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ù)開始標(biāo)志(預(yù)設(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; //返回開始標(biāo)志的下一個扇區(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ù)開始標(biāo)志,返回下一扇區(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復(fù)雜的結(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干嗎呢?
|