SD卡底層驅動 SPI模式(轉載)二 作者 騎蝸牛找浪漫 日期 2011-5-6 11:14:00 硬體平台:stm32
編譯環境:MDK401
驅動方式:SPI總線

下載下傳 (12.88 KB)
2010-11-8 19:50
編寫的函數如下:
1、 u8 SD_Init() SD卡初始化(複位和激活)
2、 u8 SD_SendCommand(u8 cmd, u32 arg, u8 crc) 向SD卡發送一個指令
3、 u32 SD_GetCapacity(void) 擷取SD卡的容量
4、 u8 SD_GetCID(u8 *cid_data) 擷取SD卡的CID資訊,包括制造商資訊
5、 u8 SD_GetCSD(u8 *csd_data) 擷取SD卡的CSD資訊,包括容量和速度資訊
6、 u8 SD_ReadMultiBlock(u32 sector, u8 *buffer, u8 count) 讀SD卡的多個block
7、 u8 SD_ReadSingleBlock(u32 sector, u8 *buffer) 讀SD卡的一個block
8、 u8 SD_ReceiveData(u8 *data, u16 len, u8 release) 從SD卡中讀回指定長度的資料,放置在給定位置
9、 u8 SD_WaitReady(void) 等待SD卡Ready
10、 u8 SD_WriteMultiBlock(u32 sector, const u8 *data, u8 count) 寫入SD卡的N個block
11、 u8 SD_WriteSingleBlock(u32 sector, const u8 *data) 寫入SD卡的一個block
SD卡指令 推薦SD卡指令共分為12類,分别為class0到class11,不同的SDd卡,主要根據其功能,支援不同的指令集 如下:
Class0 (卡的識别、初始化等基本指令集)
CMD0:複位SD 卡.
CMD1:讀OCR寄存器.
CMD9:讀CSD寄存器.
CMD10:讀CID寄存器.
CMD12:停止讀多塊時的資料傳輸
CMD13:讀 Card_Status 寄存器
Class2 (讀卡指令集):
CMD16:設定塊的長度
CMD17:讀單塊.
CMD18:讀多塊,直至主機發送CMD12為止 .
Class4(寫卡指令集) :
CMD24:寫單塊.
CMD25:寫多塊.
CMD27:寫CSD寄存器 .
Class5 (擦除卡指令集):
CMD32:設定擦除塊的起始位址.
CMD33:設定擦除塊的終止位址.
CMD38: 擦除所選擇的塊.
Class6(寫保護指令集):
CMD28:設定寫保護塊的位址.
CMD29:擦除寫保護塊的位址.
CMD30: Ask the card for the status of the write protection bits
class7:卡的鎖定,解鎖功能指令集
class8:申請特定指令集 。
class10 -11 :保留
其中 class1, class3,class9:SPI模式不支援
SD卡指令格式如下:
下載下傳 (48.37 KB)
2010-11-8 19:45
解釋:6個位元組,第一個位元組最高兩個BIT為01,2-5BYTE為參數,還有一個位元組的CRC校驗
SD卡SPI模式硬體連接配接:(典型連接配接)
下載下傳 (30.29 KB)2010-11-8 19:50
SD卡兩種模式,引角使用情況( 注意:本帖所用SPI模式)
下載下傳 (78.52 KB)
2010-11-8 19:48
SPI時序如下:
下載下傳 (57.03 KB)
2010-11-8 19:48
下載下傳 (48.2 KB)
2010-11-8 19:48
SD卡時序如下:
下載下傳 (76.34 KB)
2010-11-8 19:48
下載下傳 (72.82 KB)
2010-11-8 19:48
下載下傳 (19.86 KB)
2010-11-8 19:48
下載下傳 (108.18 KB)
2010-11-8 19:48
下載下傳 (104.43 KB)
2010-11-8 19:48
時序分析(這裡我舉一個例子)
SD卡讀扇區時序
1、拉低CS,選中SD卡
2、發送指令17(從本帖的上面查)
3、指令發送完畢後不斷的讀取輸出線,讀到0xFE,表示指令成功
4、準備接收資料,0xFE是資料的開始,此後就可以接收512個位元組了 (當然在初始化的時候有用指令16設定塊的大小為512)
5、資料接收完畢,拉高CS線
6、 最後等8個時鐘,你可以随便發一個BYTE就可以了(嚴格按照時序,大家别偷懶哦)
SD卡讀扇區時序
- u8 SD_ReceiveData(u8 *data, u16 len, u8 release)
- {
- u16 retry;
- u8 r1;
- SD_CS_Reset(); // 啟動一次傳輸
- //等待SD卡發回資料起始令牌0xFE
- retry = 0;
- do
- {
- r1 = SPI_ReadWriteByte(0xFF);
- retry++;
- if(retry>200) //200次等待後沒有應答,退出報錯
- {
- SD_CS_Set();
- return 1;
- }
- }while(r1 != 0xFE);
- //開始接收資料
- while(len--)
- {
- *data = SPI_ReadWriteByte(0xFF);
- data++;
- }
- //下面是2個僞CRC(dummy CRC)
- SPI_ReadWriteByte(0xFF);
- SPI_ReadWriteByte(0xFF);
- //按需釋放總線,将CS置高
- if(release == RELEASE)
- {
- //傳輸結束
- SD_CS_Set();
- SPI_ReadWriteByte(0xFF);
- }
- return 0;
- }
複制代碼
SD_SendCommand
- u8 SD_SendCommand(u8 cmd, u32 arg, u8 crc)
- {
- unsigned char r1;
- unsigned char Retry = 0;
- // SPI_ReadWriteByte(0xff);
- //片選端置低,選中SD卡
- SD_CS_Reset();
- //發送
- SPI_ReadWriteByte(cmd | 0x40); //分别寫入指令
- SPI_ReadWriteByte(arg >> 24);
- SPI_ReadWriteByte(arg >> 16);
- SPI_ReadWriteByte(arg >> 8);
- SPI_ReadWriteByte(arg);
- SPI_ReadWriteByte(crc);
- //等待響應,或逾時退出
- while((r1 = SPI_ReadWriteByte(0xFF))==0xFF)
- {
- Retry++;
- if(Retry > 200)
- {
- break;
- }
- }
- //關閉片選
- //在總線上額外增加8個時鐘,讓SD卡完成剩下的工作
- SPI_ReadWriteByte(0xFF);
- SD_CS_Set();
- //傳回狀态值
- return r1;
- }
複制代碼
SD卡初始化代碼:
這裡我不敢保證大家的卡都能初始化成功,我自己的是可以,我發送CMD0和CMD1就可以初始化啦
網上有更相容的初始化代碼,大家可以看看哦!
調試的時候大家看傳回值就可以了,哪裡沒成功就修改哪裡哦!
- u8 SD_Init()
- {
- unsigned char time,temp,i;
- SD_CS_Set(); //關閉片選
- for(i=0;i<0x0A;i++) //初始時,首先要發送最少74個時鐘信号,這是必須的!!!
- {
- SPI_ReadWriteByte(0xff); //120個時鐘
- }
- SD_CS_Reset(); //打開片選
- time=0;
- do
- {
- temp=SD_SendCommand(CMD0, 0 ,0x95);//寫入CMD0 複位SD卡
- time++;
- if(time==200)
- {
- SD_CS_Set(); //關閉片選
- }
- }while(temp!=0x01);
- time=0;
- do
- {
- temp=SD_SendCommand(CMD1, 0 , 0xff); //寫入CMD1 激活SD卡
- time++;
- if(time==200)
- {
- SD_CS_Set(); //關閉片選
- }
- }while(temp!=0);
- SPI_SetSpeed(1);
- temp = SD_SendCommand(CMD59, 0, 0x01);
- if(temp != 0x00)
- {
- return temp; //指令錯誤,傳回r1
- }
- temp=SD_SendCommand(CMD16,512,0xff);
- if(temp!=0x00)
- {
- return temp ;
- //指令錯誤,傳回r1
- }
- SD_CS_Set(); //關閉片選
- SPI_ReadWriteByte(0xff); //按照SD卡的操作時序在這裡補8個時鐘
- return 0;
- }
複制代碼
寫入SD卡的N個block
- u8 SD_WriteMultiBlock(u32 sector, const u8 *data, u8 count)
- {
- u8 r1;
- u16 i;
- //設定為高速模式
- SPI_SetSpeed(SPI_SPEED_HIGH);
- //如果不是SDHC,給定的是sector位址,将其轉換成byte位址
- if(SD_Type != SD_TYPE_V2HC)
- {
- sector = sector<<9;
- }
- //如果目标卡不是MMC卡,啟用ACMD23指令使能預擦除
- if(SD_Type != SD_TYPE_MMC)
- {
- r1 = SD_SendCommand(ACMD23, count, 0x00);
- }
- //發多塊寫入指令
- r1 = SD_SendCommand(CMD25, sector, 0x00);
- if(r1 != 0x00)
- {
- return r1; //應答不正确,直接傳回
- }
- //開始準備資料傳輸
- SD_CS_Reset();
- //先放3個空資料,等待SD卡準備好
- SPI_ReadWriteByte(0xff);
- SPI_ReadWriteByte(0xff);
- //--------下面是N個sector寫入的循環部分
- do
- {
- //放起始令牌0xFC 表明是多塊寫入
- SPI_ReadWriteByte(0xFC);
- //放一個sector的資料
- for(i=0;i<512;i++)
- {
- SPI_ReadWriteByte(*data++);
- }
- //發2個Byte的dummy CRC
- SPI_ReadWriteByte(0xff);
- SPI_ReadWriteByte(0xff);
- //等待SD卡應答
- r1 = SPI_ReadWriteByte(0xff);
- if((r1&0x1F)!=0x05)
- {
- SD_CS_Set(); //如果應答為報錯,則帶錯誤代碼直接退出
- return r1;
- }
- //等待SD卡寫入完成
- if(SD_WaitReady()==1)
- {
- SD_CS_Set(); //等待SD卡寫入完成逾時,直接退出報錯
- return 1;
- }
- //本sector資料傳輸完成
- }while(--count);
- //發結束傳輸令牌0xFD
- r1 = SPI_ReadWriteByte(0xFD);
- if(r1==0x00)
- {
- count = 0xfe;
- }
- if(SD_WaitReady())
- {
- while(1)
- {
- }
- }
- //寫入完成,片選置1
- SD_CS_Set();
- SPI_ReadWriteByte(0xff);
- return count; //傳回count值,如果寫完則count=0,否則count=1
- }
複制代碼