一、?? SD卡引腳接口功能介紹
1.1 SD卡引腳
接口圖
圖1-1 SD卡引腳圖
圖1-2 SD卡引腳說明
SD卡支持兩種總線方式:SD方式與SPI方式。其中SD方式采用6線制,使用CLK、CMD、DAT0~DAT3進行數據通信。而SPI方式采用4線制,使用CS、CLK、DataIn、DataOut進行數據通信。
SD方式時的數據傳輸速度與SPI方式要快,采用單片機對SD卡進行讀寫時一般都采用SPI模式。采用不同的初始化方式可以使SD卡工作于SD方式或SPI方式。
1.2 SPI方式驅動SD卡介紹
SD卡的SPI通信接口使其可以通過SPI通道進行數據讀寫。
從應用的角度來看,采用SPI接口的好處在于,很多單片機內部自帶SPI控制器,不光給開發(fā)上帶來方便,同時也見降低了開發(fā)成本。然而,它也有不好的地方,如失去了SD卡的性能優(yōu)勢,要解決這一問題,就要用SD方式,因為它提供更大的總線數據帶寬。SPI接口的選用是在上電初始時向其寫入第一個命令時進行的。以下介紹SD卡的驅動方法,只實現(xiàn)簡單的扇區(qū)讀寫。
1.3 開發(fā)板接口定義
根據當前所用開發(fā)板原理圖為例,SD卡卡槽的接口與STM32 IO口對應如下:
PC11 片選 SDCardCS
PC12 時鐘 SDCardSCLK
PD2 輸出 SPI_MOSI--主機輸出從機輸入
PC8 輸入 SPI_MISO--主機輸入從機輸出
SD卡與開發(fā)板的SPI方式接線關系如下:
DATA0---PC8-----OUT---MISO---主機輸入從機輸出
DATA1---PC9
DATA2---PC10
DATA3---PC11----CS
CLK-----PC12-------SCLK
CMD-----PD2------INPUT--MOSI--主機輸出從機輸入
二、MMC卡、SD卡介紹
2.1 SD卡和MMC兩者間區(qū)別
SD卡和MMC(多媒體卡)似乎可以使用同一個插槽,兩者間有什么區(qū)別呢?
不過,雖說外型幾乎一致,但還是有點差異的。MMC比SD卡要薄一些。
圖2-1 MMC卡與SD卡
首先得從MMC卡的發(fā)展談起。 MMC卡是由西門子設計,和SanDisk合作開發(fā)的小型存儲卡標準。 在1997年,作為使用閃存的存儲卡(I / O卡或ROM卡都可以)開始發(fā)售,日立和NEC,摩托羅拉,諾基亞等共同建立了MMCA(多媒體卡協(xié)會)。 并促進了標準??化和市場推廣。
SanDisk公司,也是在94年提出小型閃存卡(以下簡稱CF)的廠商,但是CF在用于緊湊型概念的產品時,采用了和廣泛使用的PC卡的ATA兼容的接口。 這種設計消除了不需要的信號線,管腳數也由68針減少到50針,電氣方面可以相互兼容,并且被設計為僅僅通過簡單的適配器就可以安裝在PC卡插槽中。 然而,CF雖然了PC卡容易替換的好處,但由于管腳的數量巨大,寬度達到43毫米,這樣就不太能減下去。?這個在涉及到??移動電話時,你將無法容納CF。 因此,在新的設計中,硬件兼容性被舍棄,只致力于小型化的MMC。
CF卡和PC卡的接口,多個并行傳輸的地址信號和數據信號,各種控制線緊密被布置一起。規(guī)格上就是PC的擴展總線這樣的接口,但在MMC中,據傳輸方式變更為串行傳輸,地址的指定和各種控制也用的是通過一個串行接口交換數據包的方式。 其結果是,主要的信號變成三個:數據,命令,時鐘,接口可以降低到僅7針。 接口也從兩排的針/socket這種面接觸類型變換成小型的薄卡片。體積比的話,CF是PC卡(基于Ⅰ型)的三分之一,而MMC的話則是十四分之一,這么看來MMC已經變得非常緊湊。
以MMC為基礎實現(xiàn)了安全(安全性)功能的是東芝,松下,SanDisk三家公司共同研發(fā)的SD卡。 該標準本身不是MMC卡的擴展,而是另一種標準,雖然該標準成立了另一個叫SDA(SD卡協(xié)會)組織,但它的一大特色是被設計成能夠和MMC卡共享插槽?。SD卡的表面積和MMC卡是相同大小的,但是厚度比1.4毫米的MMC增大了0.7毫米,變成2.1毫米。 然而,SD卡的左右部分和MMC卡的厚度一樣的,為1.4毫米,所以MMC卡可以直接插入SD卡插槽。(相反,SD卡不能插入MMC卡插槽)?接口的規(guī)格也是在MMC卡的管腳排列基礎上添加的兩條信號線到兩側,傳輸方法因為和MMC相兼容,也可以從SD卡host訪問到MMC。 記錄數據的邏輯規(guī)范,因為它們用的是相同的FAT文件系統(tǒng),只要是它被用作簡單的記錄媒體那就是兼容的。
然而,實際上SD卡主機端的應用程序能否使用的MMC上的數據,因為是涉及到安全和文件格式的問題,所以是由應用程序決定。 特別是用到安全性的情況下,基本上沒有兼容性。 SD卡的版權保護機制用到的松下和東芝倡導的是CPRM(內容保護可記錄媒體)。 此外,作為MMC卡的安全版本,MMCA發(fā)布了安全MMC的版本,它是與MMC完全兼容的更高的標準,但是這里用到是的日立倡導的UDAC MB(Universal Distribution with Access Control-Media Base)的版權保護機制,所以與SD卡不兼容。 此外,現(xiàn)在還沒有支持UDAC-MB和CPRM的商品。
此外,SD里添加的兩條信號線都是用于數據的信號線。MMC中只有一個數據信號通道,但在SD中MMC中的7號管腳(數據信號)和一號管腳(在MMC中未使用),加上新加的8,9號管腳一共4個通道可以使用,這樣就能達到更高的傳輸速度。?MMC的傳輸時鐘最大是20MHz(時鐘可變),所以傳輸速度最大為20Mbps(2.5MB/s)。?雖說這是和閃存讀出速度相當的速度,做為存儲卡的規(guī)格來說是夠了,但是用到I/O卡的情況下,它可能是不夠的。?而用到所有四個管腳的SD卡,目前可達到80Mbps(10MB / s)速度。
2.2 SD卡版本說明
SD卡版本:SD V1.X(即SD標準卡)最大容量2GB
SD V2.0 2.0版本的標準卡,最多2GB
SD V2.0HC 2.0高容量卡,最多32GB
說明: 本程序主要針對SD卡2.0 HC 2.0高容量卡協(xié)議進行說明。
SD卡默認操作的扇區(qū)大小是512字節(jié)。扇區(qū)大小,可以通過指令設置。就算不是512,也可以通過指令設置成512,因為這個值不太大,占用內存不太多,適合單片機使用。
2.3 SD卡常用的指令表
#define SDCard_CMD0 0 //卡復位
#define SDCard_CMD8 8 //命令8 ,SEND_IF_COND
#define SDCard_CMD9 9 //命令9 ,讀CSD數據
#define SDCard_CMD12 12 //命令12,停止數據傳輸
#define SDCard_CMD13 16 //命令16,設置扇區(qū)大小 應返回0x00
#define SDCard_CMD17 17 //命令17,讀扇區(qū)
#define SDCard_CMD18 18 //命令18,讀多個扇區(qū)
#define SDCard_CMD23 23 //命令23,設置多扇區(qū)寫入前預先擦除block
#define SDCard_CMD24 24 //命令24,寫扇區(qū)
#define SDCard_CMD25 25 //命令25,寫多個扇區(qū)
#define SDCard_CMD41 41 //命令41,應返回0x00
#define SDCard_CMD55 55 //命令55,應返回0x01
#define SDCard_CMD58 58 //命令58,讀OCR信息
三、向SD卡發(fā)送命令的步驟介紹(SendSDCardCmd)
3.1 取消選中SD卡(SDCardCancelCS)
發(fā)送新的命令之前,需要取消之前的片選,額外發(fā)多 8個 CLK (發(fā)送0xFF無效數據),結束之前的操作。 (1). 取消片選 (2). 最多發(fā)送 8個 CLK
圖3-1 時序圖
3.2 選中SD卡(SDCardSelectCS)
(1).選中片選 (2).等待SD卡忙狀態(tài)
說明: 向SD卡發(fā)送0xFF,如果SD卡也返回0xFF就表示SD卡已經準備好,如果返回不是0xFF就表示SD卡還沒有準備好,需要等待。
圖3-2 時序圖
3.3 向SD卡發(fā)送操作命令cmd
將要發(fā)送的命令 |0x40 發(fā)給SD卡。 示例: cmd | 0x40
命令是8位數據。
圖3-3 時序圖
3.4 向SD卡發(fā)送命令參數
命令參數是32位數據,SPI每次發(fā)送8位,需要發(fā)送4次,先發(fā)送最高8位,依次再發(fā)送低位。
圖3-4 時序圖
3.5 發(fā)送CRC校驗
CRC是8位數據。
注意: 如果發(fā)送的是CMD12命令(停止數據傳輸),在發(fā)送CRC校驗之后,需要再發(fā)送一個0xFF數據。
圖3-5-1 時序圖
圖3-5-2 時序圖
3.6 等待SD卡響應
向SD卡發(fā)送0xFF數據,如果SD卡返回的數據最高位為0,就是表示SD卡響應完成,否則就繼續(xù)發(fā)送0xFF,再判斷,直到SD卡響應成功。
SD卡響應之后,完成一次命令發(fā)送,并將返回的數據當做返回值返回去。
推薦: 使用do{…}while()循環(huán)結構,更加方便判斷。
圖3-6 時序圖
四、SD卡的寄存器與操作命令介紹
4.1 SDCard_CMD0:卡復位命令
圖 4-1 時序圖
4.2 SDCard_CMD8:檢測是否是2.0版本的SD卡
發(fā)送只有V2.0版的SD卡才具有的命令CMD8,然后檢測返回值: 返回值若是0x01,則表示此卡為V2.0卡,然后再發(fā)送循環(huán)命令CMD55+CMD41,直到返回0x00,確定SD2.0卡初始化成功;
然后再發(fā)送CMD58命令,接收返回的OCR寄存器的數據,其中第31位用于判斷V2.0的卡是否為SDHC類型。
若返回值不為0x01,則進一步判斷是V1.0卡還是MMC卡:先發(fā)送循環(huán)命令CMD55+CMD41進行復位,如果復位不成功則考慮是MMC卡,如果復位成功,則為V1.0卡。在復位不成功的情況下,再使用CMD1進行復位,如果復位成功,則表明是MMC卡,如果復位不成功,則表示是無法識別的卡。
圖4-2-1 時序圖
圖4-2-2 時序圖
圖4-2-3 時序圖
圖4-2-4 時序圖
鑒別到v2.0版本之后,可以讀取OCR 寄存器的值,繼續(xù)判斷是否是V2.0高速卡。
SD卡響應命令成功,可以繼續(xù)接收4字節(jié)的OCR寄存器值;
OCR寄存器的第30位(CCS)指示了卡的類型是否為SDHC,此位為1則為SDHC,為0則為SDSC。
OCR 寄存器,儲存了卡的 VDD 電壓輪廓圖。任何標準的 SD 卡主控制器可以使用 2V 至 3.6V 的工作電壓來讓 SD 卡能執(zhí)行這個電壓識別操作(CMD1)。而訪問存儲器的陣列操作無論如何都需要 2.7V 至 3.6V 的工作電壓。OCR 寄存器顯示了在訪問卡的數據時所需要的電壓范圍。
OCR 寄存器的結構描述:
圖4-2-5 時序圖
圖4-2-6 時序圖
4.3 SDCard_CMD9: 獲取SD卡的CSD信息
CSD包括容量和速度信息,存放CID的內存,至少16Byte
CMD9的命令:
圖4-3
4.4 SDCard_CMD17: 設置單個讀取的扇區(qū)
圖4-4
4.5 SDCard_CMD18: 設置讀扇區(qū)(連續(xù)讀扇區(qū)使用)
圖4-5
4.6 SDCard_CMD12: 停止數據傳輸
圖4-6
4.7 SDCard_CMD24: 設置寫單個扇區(qū)
圖4-7
4.8 SDCard_CMD55
圖4-8
4.9 SDCard_CMD23: 多扇區(qū)寫入前預先擦除塊數量
圖4-9
4.10 SDCard_CMD25: 設置寫多個扇區(qū)
圖4-10
4.11 SDCard_CMD41
圖4-11
4.12 SDCard_CMD58
圖4-12
五、SD卡SPI接口命令
5.1 SPI接口時序
圖5-1 SPI時序圖
圖5-2 SPI時序圖
數據先發(fā)/先收高位
從圖上得知(SD卡在SPI模式下): 下降沿寫數據、時鐘的上升沿讀數據。
示例:
u8 SDCardReadWriteOneByte(u8 DataTx)
{
u8 i;
u8 data=0;
for(i=0;i<8;i++)
{
SDCARD_SCK=0;
if(DataTx&0x80)SDCARD_MOSI=1;
else SDCARD_MOSI=0;
SDCARD_SCK=1;
DataTx<<=1;
data<<=1;
if(SDCARD_MISO)data|=0x01;
}
return data;
}
5.2 SPI模式下: SD卡初始化步驟(SDCardDeviceInit)
SD卡的初始化是非常重要的,只有進行了正確的初始化,才能進行后面的各項操作。在初始化過程中,SPI的時鐘不能太快,否則會造初始化失敗。在初始化成功后,應盡量提高SPI的速率。在剛開始要先發(fā)送至少74個時鐘信號,這是必須的。如果接到復位命令(CMD0)時,CS信號有效(低電平),SPI模式啟用。
詳細步驟:
1. 初始化與 SD卡連接的硬件條件(MCU的 SPI配置,IO口配置等等)
2. 向總線最少發(fā)送74個脈沖,為了讓SD卡正常啟動 (喚醒SD卡)
(解釋: 就是時鐘線至少需要74個跳變,向MOSI發(fā)送0xFF數據即可,這是無效數據)
3. 復位卡(CMD0),進入 IDLE(閑置)狀態(tài)。
說明: 最后的返回值等于0x01就表示復位成功。
4. 發(fā)送 CMD8,檢查是否支持 2.0協(xié)議,因為這個命令是在2.0的協(xié)議里面才添加的
說明: 發(fā)送 CMD8命令之后,返回值等于0x01表示就是2.0版本的SD卡。
5. 如果是2.0版本的SD卡,就需要循環(huán)發(fā)送CMD55+ CMD41命令等待2.0卡初始化成功,如果CMD41命令的返回值等于0就表示卡復位成功。(先發(fā)CMD55,再發(fā)CMD41)
6. 2.0卡初始化成功后,再發(fā)送CMD58命令,繼續(xù)判斷是否是高速卡。
說明: CMD58命令返回值等于0,表示執(zhí)行成功。然后就可以讀取4字節(jié)的OCR 寄存器的值。OCR寄存器的第30位(CCS)指示了卡的類型是否為SDHC,此位為1則為SDHC,為0則為SDSC。
如果只是為了判斷是否是高速卡,可以只讀取1個字節(jié)數據即可,因為SD返回的數據先返回的是高位數據(24~31),后面的數據可以不讀取。
7. 取消片選,結束初始化。
說明: 取消片選之后,需要再額外發(fā)送8個時鐘信號,結束本次操作。
5.3 SPI模式下: 向SD卡發(fā)送數據包步驟(SDCardSendData)
每次發(fā)送數據包默認為512字節(jié)。
1. 等待SD卡忙狀態(tài)
向SD卡發(fā)送一個0xFF數據,如果SD卡也原路返回0xFF就表示SD卡處于閑置狀態(tài)。
2. 發(fā)送(開始傳輸)/(結束傳輸)的標志
寫一個扇區(qū)的情況下發(fā)送0xFE開始傳輸數據。
寫多個扇區(qū)的情況下發(fā)送0xFC開始傳輸數據。
寫多個扇區(qū)的情況下,連續(xù)發(fā)送數據完成之后,發(fā)送0xFD結束數據發(fā)送。
3. 如果不是結束標志(0xFD),就是表示發(fā)送的是正常的數據包,就進行循環(huán)發(fā)送512字節(jié)的數據。
注意: 每次發(fā)送數據包的單位是按扇區(qū)為單位的,也就是512字節(jié),一包數據長度固定為512字節(jié)。
4. 數據發(fā)送完之后,再接著發(fā)送0xFF忽略CRC校驗(連續(xù)發(fā)送3個0xFF)。
圖5-3-1
5.4 SPI模式下: 從SD卡讀取數據包步驟(SDCardRecvData)
1、等待SD卡發(fā)回數據起始令牌0xFE
向SD卡發(fā)送0xFF,如果SD卡返回0xFE就表示等待成功。
圖5-4-1
2、收到返回的數據起始令牌之后就可以連續(xù)讀取數據了(接收的數量以傳入的cnt為準),讀完數據發(fā)送兩個偽CRC
圖5-4-2
5.5 SPI模式下: 向SD卡指定扇區(qū)寫數據(SDCardWriteData)
封裝的函數原型: SDCardWriteData(u8*buf,u32 sector,u32 cnt)
寫一個扇區(qū)步驟:
1、發(fā)送CMD24命令,設置要寫的扇區(qū)。(寫單個扇區(qū)使用CMD24命令)
2、接著向SD卡寫數據包(參考5.3小節(jié))。
圖5-5-1
寫多個扇區(qū)的步驟:
1、發(fā)送CMD55命令(正常應返回0x01)
2、? 發(fā)送CMD23命令(設置多扇區(qū)寫入前預先擦除N個block)---寫入的數量
3、? 發(fā)送CMD25命令,設置寫入的扇區(qū)位置。(設置寫多個扇區(qū))
4、 接著向SD卡寫數據包(參考5.3小節(jié))。
圖5-5-2
5、寫結束指令0xFD,完成寫入。
圖5-5-3
6、 取消片選
5.6 SPI模式下: 從SD卡讀取指定扇區(qū)數據(SDCardReadData)
讀取一個扇區(qū)的步驟:
1、? 發(fā)送CM17命令,設置讀取的扇區(qū)
2、?接著進行接收SD卡返回的數據包。(參考5.4小節(jié))
每次固定接收512字節(jié),以扇區(qū)為單位。
3、 取消片選,完成數據讀取
說明: 取消片選之后,需要再額外發(fā)送8個時鐘信號,結束本次操作。
讀取多個扇區(qū)的步驟:
1、 發(fā)送CMD18命令,設置讀取的扇區(qū)(連續(xù)讀多個扇區(qū)使用)
2、? 接著循環(huán)接收SD卡返回的數據包。(參考5.4小節(jié))
每次固定接收512字節(jié),以扇區(qū)為單位。
3、?發(fā)送CMD12指令,停止數據傳輸
4、 取消片選,完成數據讀取
說明: 取消片選之后,需要再額外發(fā)送8個時鐘信號,結束本次操作。
5.7 SPI模式下: 獲取SD卡的總扇區(qū)數(GetSDCardSectorCount)
1、 發(fā)送CMD9命令,讀取CSD信息
2、 連續(xù)接收16個字節(jié)數據包。(參考5.4小節(jié))
3、 取消片選,完成讀取
4、 判斷是否是v2.0 SDHC高速卡。
使用讀取的第一個字節(jié)數據csd[0]&0xC0判斷是否等于0x40,如果等于0x40就是v2.0高速卡。
5、?如果是v2.0 SDHC高速卡就按照以下公式計算得到扇區(qū)數量
csize=csd[9]+(csd[8]<<8)+1;
Capacity=csize<<10;//得到總扇區(qū)數
六、示例代碼
6.1 sdcard.c文件
#include "sdcard.h"
/*
函數功能:SD卡底層接口,通過SPI時序向SD卡讀寫一個字節(jié)
函數參數:data是要寫入的數據
返 回 值:讀到的數據
*/
u8 SDCardReadWriteOneByte(u8 DataTx)
{
u8 i;
u8 data=0;
for(i=0;i<8;i++)
{
SDCARD_SCK=0;
if(DataTx&0x80)SDCARD_MOSI=1;
else SDCARD_MOSI=0;
SDCARD_SCK=1;
DataTx<<=1;
data<<=1;
if(SDCARD_MISO)data|=0x01;
}
return data;
}
//4種: 邊沿兩種、電平是兩種
/*
函數功能:底層SD卡接口初始化
本程序SPI接口如下:
PC11 片選 SDCardCS
PC12 時鐘 SDCardSCLK
PD2 輸出 SPI_MOSI--主機輸出從機輸入
PC8 輸入 SPI_MISO--主機輸入從機輸出
*/
void SDCardSpiInit(void)
{
/*1. 開啟時鐘*/
RCC->APB2ENR|=1<<5; //使能PORTD時鐘
RCC->APB2ENR|=1<<4; //使能PORTC時鐘
/*2. 配置GPIO口模式*/
GPIOC->CRH&=0xFFF00FF0;
GPIOC->CRH|=0x00033008;
GPIOD->CRL&=0xFFFFF0FF;
GPIOD->CRL|=0x00000300;
/*3. 上拉*/
GPIOC->ODR|=1<<8;
GPIOC->ODR|=1<<11;
GPIOC->ODR|=1<<12;
GPIOD->ODR|=1<<2;
}
/*
函數功能:從sd卡讀取一個數據包的內容
函數參數:
buf:數據緩存區(qū)
len:要讀取的數據長度.
返回值:
0,成功;其他,失敗;
*/
u8 SDCardRecvData(u8*buf,u16 len)
{
while(SDCardReadWriteOneByte(0xFF)!=0xFE){}//等待SD卡發(fā)回數據起始令牌0xFE
while(len--)//開始接收數據
{
*buf=SDCardReadWriteOneByte(0xFF);
buf++;
}
//下面是2個偽CRC(dummy CRC)
SDCardReadWriteOneByte(0xFF);
SDCardReadWriteOneByte(0xFF);
return 0;//讀取成功
}
/*
函數功能:向sd卡寫入一個數據包的內容 512字節(jié)
函數參數:
buf 數據緩存區(qū)
cmd 指令
返 回 值:0表示成功;其他值表示失敗;
*/
u8 SDCardSendData(u8*buf,u8 cmd)
{
u16 t;
while(SDCardReadWriteOneByte(0xFF)!=0xFF){} //等待忙狀態(tài)
SDCardReadWriteOneByte(cmd);
if(cmd!=0xFD)//不是結束指令
{
for(t=0;t<512;t++)SDCardReadWriteOneByte(buf[t]);//提高速度,減少函數傳參時間
SDCardReadWriteOneByte(0xFF); //忽略crc
SDCardReadWriteOneByte(0xFF);
SDCardReadWriteOneByte(0xFF); //接收響應
}
return 0;//寫入成功
}
/*
函數功能:向SD卡發(fā)送一個命令
函數參數:
u8 cmd 命令
u32 arg 命令參數
u8 crc crc校驗值
返回值:SD卡返回的響應
*/
u8 SendSDCardCmd(u8 cmd, u32 arg, u8 crc)
{
u8 r1;
SDCARD_CS=1; //取消上次片選
SDCardReadWriteOneByte(0xff);//提供額外的8個時鐘
SDCARD_CS=0; //選中SD卡
while(SDCardReadWriteOneByte(0xFF)!=0xFF){};//等待成功
//發(fā)送數據
SDCardReadWriteOneByte(cmd | 0x40);//分別寫入命令
SDCardReadWriteOneByte(arg >> 24);
SDCardReadWriteOneByte(arg >> 16);
SDCardReadWriteOneByte(arg >> 8);
SDCardReadWriteOneByte(arg);
SDCardReadWriteOneByte(crc);
if(cmd==SDCard_CMD12)SDCardReadWriteOneByte(0xff);//Skip a stuff byte when stop reading
do
{
r1=SDCardReadWriteOneByte(0xFF);
}while(r1&0x80); //等待響應,或超時退出
return r1;
}
/*
函數功能:獲取SD卡的總扇區(qū)數(扇區(qū)數)
返 回 值:
0表示容量檢測出錯,其他值表示SD卡的容量(扇區(qū)數/512字節(jié))
說 明:
每扇區(qū)的字節(jié)數必為512字節(jié),如果不是512字節(jié),則初始化不能通過.
*/
u32 GetSDCardSectorCount(void)
{
u8 csd[16];
u32 Capacity=0;
u16 csize;
//獲取SD卡的CSD信息,包括容量和速度信息,存放CID的內存,至少16Byte
SendSDCardCmd(SDCard_CMD9,0,0x01);//發(fā)SDCard_CMD9命令,讀CSD
SDCardRecvData(csd,16);//接收16個字節(jié)的數據
SDCARD_CS=1;//取消片選
SDCardReadWriteOneByte(0xff);//提供額外的8個時鐘
if((csd[0]&0xC0)==0x40) //SDHC卡,按照下面方式計算
{
csize=csd[9]+(csd[8]<<8)+1;
Capacity=csize<<10;//得到扇區(qū)數
}
return Capacity;
}
/*
函數功能: 初始化SD卡
返 回 值: 非0表示初始化失敗!
*/
u8 SDCardDeviceInit(void)
{
u8 r1=0; // 存放SD卡的返回值
u8 data;
u16 i;
/*1. 初始化底層IO口*/
SDCardSpiInit();
/*2. 發(fā)送最少74個脈沖*/
for(i=0;i<10;i++)SDCardReadWriteOneByte(0xFF);
/*3. 進入閑置狀態(tài)*/
do
{
r1=SendSDCardCmd(SDCard_CMD0,0,0x95);
}while(r1!=0x01);
/*4. 鑒別是否是2.0版本的SD卡*/
if(SendSDCardCmd(SDCard_CMD8,0x1AA,0x87)==0x01)
{
do
{
SendSDCardCmd(SDCard_CMD55,0,0x01); //發(fā)送SDCard_CMD55
r1=SendSDCardCmd(SDCard_CMD41,0x40000000,0x01);//發(fā)送SDCard_CMD41
}while(r1);
if(SendSDCardCmd(SDCard_CMD58,0,0x01)==0)//鑒別SD2.0卡版本開始
{
data=SDCardReadWriteOneByte(0xFF);//得到OCR值
if(data&0x40)
{
r1=0; //高速卡
}
}
}
SDCARD_CS=1;//取消片選
SDCardReadWriteOneByte(0xff);//提供額外的8個時鐘
return r1; //其他錯誤
}
/*
函數功能:讀SD卡
函數參數:
buf:數據緩存區(qū)
sector:扇區(qū)
cnt:扇區(qū)數
返回值:
0,ok;其他,失敗.
說 明:
SD卡一個扇區(qū)大小512字節(jié)
*/
void SDCardReadData(u8*buf,u32 sector,u32 cnt)
{
u32 i=0;
if(cnt==1)
{
SendSDCardCmd(SDCard_CMD17,sector,0x01);//讀扇區(qū)
SDCardRecvData(buf,512); //接收512個字節(jié)
}
else
{
SendSDCardCmd(SDCard_CMD18,sector,0x01);//連續(xù)讀命令
for(i=0;i<cnt;i++)
{
SDCardRecvData(buf,512);//接收512個字節(jié)
buf+=512;
}
SendSDCardCmd(SDCard_CMD12,0,0x01); //停止數據傳輸
}
SDCARD_CS=1;//取消片選
SDCardReadWriteOneByte(0xff);//提供額外的8個時鐘();
}
/*
函數功能:向SD卡寫數據
函數參數:
buf:數據緩存區(qū)
sector:起始扇區(qū)
cnt:扇區(qū)數
說 明:
SD卡一個扇區(qū)大小512字節(jié)
*/
void SDCardWriteData(u8*buf,u32 sector,u32 cnt)
{
u32 i=0;
if(cnt==1)
{
SendSDCardCmd(SDCard_CMD24,sector,0x01);//寫單個扇區(qū)
SDCardSendData(buf,0xFE);//寫512個字節(jié)
}
else
{
SendSDCardCmd(SDCard_CMD55,0,0x01);
SendSDCardCmd(SDCard_CMD23,cnt,0x01); //設置多扇區(qū)寫入前預先擦除N個block
SendSDCardCmd(SDCard_CMD25,sector,0x01);//寫多個扇區(qū)
for(i=0;i<cnt;i++)
{
SDCardSendData(buf,0xFC);//寫512個字節(jié)
buf+=512;
}
SDCardSendData(0,0xFD);//寫結束指令
}
SDCARD_CS=1;
SDCardReadWriteOneByte(0xff);//提供額外的8個時鐘
6.2 sdcard.h文件
#ifndef SD_H_
#define SD_H_
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "usart.h"
/*----------------------------------------------
本程序SPI接口如下:
PC11 片選 SDCardCS
PC12 時鐘 SDCardSCLK
PD2 輸出 SPI_MOSI--主機輸出從機輸入
PC8 輸入 SPI_MISO--主機輸入從機輸出
------------------------------------------------*/
#define SDCARD_CS PCout(11)
#define SDCARD_SCK PCout(12)
#define SDCARD_MOSI PDout(2)
#define SDCARD_MISO PCin(8)
// SD卡指令表
#define SDCard_CMD0 0 //卡復位
#define SDCard_CMD8 8 //命令8 ,SEND_IF_COND
#define SDCard_CMD9 9 //命令9 ,讀CSD數據
#define SDCard_CMD12 12 //命令12,停止數據傳輸
//#define SDCard_CMD13 16 //命令16,設置扇區(qū)大小 應返回0x00
#define SDCard_CMD17 17 //命令17,讀扇區(qū)
#define SDCard_CMD18 18 //命令18,讀多個扇區(qū)
#define SDCard_CMD23 23 //命令23,設置多扇區(qū)寫入前預先擦除N個block
#define SDCard_CMD24 24 //命令24,寫扇區(qū)
#define SDCard_CMD25 25 //命令25,寫多個扇區(qū)
#define SDCard_CMD41 41 //命令41,應返回0x00
#define SDCard_CMD55 55 //命令55,應返回0x01
#define SDCard_CMD58 58 //命令58,讀OCR信息
//函數聲明
u8 SDCardReadWriteOneByte(u8 data); //底層接口,SPI讀寫字節(jié)函數
u8 SDCardDeviceInit(void); //初始化
void SDCardReadData(u8*buf,u32 sector,u32 cnt); //讀塊(扇區(qū))
void SDCardWriteData(u8*buf,u32 sector,u32 cnt); //寫塊(扇區(qū))
u8 SDCardSendData(u8*buf,u8 cmd); //發(fā)送數據包
u8 SDCardRecvData(u8*buf,u16 len); //接收數據包
u32 GetSDCardSectorCount(void); //讀扇區(qū)數
#endif