每日一貼,天今的内容關鍵字為卡函數
這篇blog來說說基于simple-spi這個ipcore編寫spi模式的SD Card裸機的驅動程式,植移依附分不清什麼SD卡啊,micro SD啊,miniSD,MMC,SDIO啊,SDHC啊等等一大堆的觀點,天今抽了點時光百科和wiki掃盲去了,順便把結總的貼出來,留自己後以回想~
MMC:Multimedia Card(多媒體記憶卡),基于NAND-Flash技巧,衍生版有出RS-MMC(小尺寸的多媒體卡)、雙電壓小尺寸多媒體卡(DV-RS-MMC)。4.x準标引入更新版MMC plus,MMC4卡和RS-MMC4(移動式MMC,老式RS-MMC的盜窟),并且引入secureMMC準标
SD:Secure Digital Memory Card,基于MMC卡格式展發而來,同樣是NAND-Flash技巧,SD裝置相容MMC卡存取,而MMC裝置不能存取SD卡(因為卡槽不一緻,不能互插)。
miniSD:個人解理是小型的SD卡,相容SD卡,電器性屬略微有點區分。
microSD:原名Trans-flash Card(TF卡),04年名更microSD Card,比miniSD尺寸更小的SD卡。
SDHC:Secure Digital High Capacity(高容量SD卡4G~32G),判定終了(本人機手用的也隻microSD咧,SDHC用在什麼方面?求釋解),劃定用使FAT32檔案系統。
SDIO:Secure Digital Input and Output Card(安全字數輸入輸出卡),在SD準标上定義的一種外設接口。(接口?我能不能解理成似類USB一樣,不過就是把外設的接口做成SDIO,那有USB了為毛還要用這個接口,求大神釋解)
現在SD卡差不多代取完MMC卡了,不過SD的卡槽可以卡得進MMC,是以是相容MMC的,也因為這樣MMC卡,因為用MMC卡可以不交SD議協的稅版,是以還在用,至于miniSD和microSD套進去插到SD卡槽就能夠用了,SDHC沒見過,釋解不了了,SDIO的裝置也沒用過,可以去寶淘YY一下,SDIO接口的wifi,藍牙等等的,但是比起USB接口的來得貴,大神,求釋解啊,應用合場是什麼?
這界世貌似多好西東我不能夠解理啊~除了坑爹還是坑爹~
再說說那個作操接口和時序準标的問題
(轉一下wiki面下關于各種技巧比對的圖,很然了目一)
SD卡比起MMC多了2pins,都是用來做資料線的;
miniSD比起SD又多了2pins,當初隻做預留,沒什麼用途,其他和SD卡一樣
microSD比SD少了1pin?貌似比對我手上的那些原理圖是少了個VSS?
因為曆史因原,SPI時序基本在這面裡都市支撐,還有SD總線的1bit mode是支撐的,但是SD卡用于高速情況是一般我們擇選是4bit mode,我自己是沒用過SD總線的1 bit mode,平日我會擇選跑高速資料擇選SD總線的4 bit mode,低速跑SPI。
有這個SD 1 bit mode為毛還要SPI mode咧?是以這個界世除了坑爹還是坑爹,但是像我們這些讀過一點點書的人都道知:“存在就是理合”這個堡碉了的名言。
1.你見過大部分SOC(特别是低端)成集SPI controller還是支撐SD Bus 和controller多
2.寫過SD卡驅動的友朋們處置CRC校驗你耗資源多不多(件硬無CRC校驗)
3.SD的CMD線與DATA線之間有可能同時生産資料,對沒有SD件硬子產品的機主支撐起來難度較高(引用别人blog的原話)
挑點重點來講講,也因為我隻道知這麼多了,見笑見笑!!!
面下就是講講SD/MMC準标SPI時序的SD卡寫讀程序了,至于SD 4bit mode就先不細說了,後以有時光編寫程式再發個blog~
至于官方的SD specs我沒怎麼看過,太長了~心真看不動~google一下有沒有别人結總過的驗經了,就算把官方的specs翻譯成中文也懶得看了~
SD展發了這麼多頭年,官方結總的資料我認為比起看官方的spces來得際實很多,至于關于SD卡的資料推薦《ALIENTEK戰艦STM32開發闆》面裡的盤光,這裡絕對我不是托,隻是友朋再用這款開發闆,而面裡整理好關于SD卡的資料我心真認為不錯。
到此,根據寫好的基于simple-spi這個core的SD驅動,結總最簡略的流程
一、Initialize:
1.開發闆上電
2.延時>74clocks
3.寫CMD0,複位SD卡
4.寫CMD8,檢查版本
5.寫CMD58,查詢OCR,獲得卡供電情況
6.寫CMD1,激活卡
7.8 clocks後,止禁SD的CS
二,read block:
1.寫CMD17
2.等到R1格式的令命應答
3.取讀始起令牌0xFE
4.讀512 bytes
5.棄丢2 bytes的CRC校驗bytes
6.8 clocks後,止禁SD的CS
三、write block:
1.寫CMD24
2.等到R1格式的令命應答
3.插入幹若clocks
4.寫始起令牌0xFE
5.寫512 bytes
6.寫2 bytes CRC校驗bytes(dummy bytes)
7.收接響應資料0x05
8.8 clocks後,止禁SD的CS
四、write blocks:
1.寫CMD25
2.等到R1格式的令命應答
3.取讀始起令牌0xFC
4.寫512 bytes
5.寫2 bytes CRC校驗bytes(dummy bytes)
6.收接響應資料0x05
7.等待SD card閑暇
8.重複第2步
至于關于SD卡的令命集google一下一大堆,解了一下經常使用的令命就OK啦,或者參看官方的SD specs 2.x的第7 chapter關于SPI Mode的述描,面裡有關于令命集合時序圖。
空話不多了,關于SD的基礎知識和SPI的作操釋解到這裡,至于友朋們還須要更多的SD的知識可以去wiki關于SD的詳解。
http://en.wikipedia.org/wiki/Secure_Digital
面下就根據SD卡SPI的時序圖和simple-spi這個core的spces來敲SD card的driver
還是來講講編寫好的simple-spi的幾個封裝函數先了,這些函數咧,是opencore區社的牛牛寫好的,我就不另外自己去編了~省點力精~
首先在高速寫讀SD卡的時候咧,用輪詢方法比用斷中方法去寫讀SD卡效率要高(真的嗎?我自己沒究研過),但是我輕信了這句話,是以沒用斷中去寫SD的driver,是以這裡僅僅紹介用到的API啦~
這個驅動檔案在u-boot/driver/spi中可以找到oc_simple_spi.c~
至于對應的simple-spi的代碼我用的是orpsocv2面裡的,orpsocv2修改了simple-spi支撐slave的擇選功能,這份驅動就是按照修過改的core編寫的,上代碼了~
隻貼出來用到的函數,體具參考simple-spi的specs把寄存器置配都略微看懂。
void
spi_core_enable(int core)
{
REG8((SPI_BASE_ADR[core] + SIMPLESPI_SPCR)) |= SIMPLESPI_SPCR_SPE;
}
void
spi_core_disable(int core)
{
REG8((SPI_BASE_ADR[core] + SIMPLESPI_SPCR)) &= ~SIMPLESPI_SPCR_SPE;
}
void
spi_core_clock_setup(int core, char polarity, char phase, char rate,
char ext_rate)
{
char spcr = REG8((SPI_BASE_ADR[core] + SIMPLESPI_SPCR));
if (polarity)
spcr |= SIMPLESPI_SPCR_CPOL;
else
spcr &= ~SIMPLESPI_SPCR_CPOL;
if (phase)
spcr |= SIMPLESPI_SPCR_CPHA;
else
spcr &= ~SIMPLESPI_SPCR_CPHA;
spcr = (spcr & ~SIMPLESPI_SPCR_SPR) | (rate & SIMPLESPI_SPCR_SPR);
REG8((SPI_BASE_ADR[core] + SIMPLESPI_SPCR)) = spcr;
char sper = REG8((SPI_BASE_ADR[core] + SIMPLESPI_SPER));
sper = (sper & ~SIMPLESPI_SPER_ESPR) | (ext_rate & SIMPLESPI_SPER_ESPR);
REG8((SPI_BASE_ADR[core] + SIMPLESPI_SPER)) = sper;
}
// No decode on slave select lines, so assert correct bit to select slave
void
spi_core_slave_select(int core, char slave_sel_dec)
{
REG8((SPI_BASE_ADR[core] + SIMPLESPI_SSPU)) = slave_sel_dec;
}
int
spi_core_data_avail(int core)
{
return !!!(REG8((SPI_BASE_ADR[core]+SIMPLESPI_SPSR))&SIMPLESPI_SPSR_RFEMPTY);
}
int
spi_core_write_avail(int core)
{
return !!!(REG8((SPI_BASE_ADR[core]+SIMPLESPI_SPSR))&SIMPLESPI_SPSR_WFFULL);
}
// Should call spi_core_write_avail() before calling this, we don't check
void
spi_core_write_data(int core, char data)
{
REG8((SPI_BASE_ADR[core] + SIMPLESPI_SPDR)) = data;
}
char
spi_core_read_data(int core)
{
return REG8((SPI_BASE_ADR[core] + SIMPLESPI_SPDR));
}
然後略微解理用到的最平日的寫讀函數,就能夠轉到SD卡driver的編寫了~驅動參考振南兄的znFAT裡SD驅動流程編寫
好,第一個,reset程序函數SDReset(),對着時序圖來談話
按照面下述描過的初始化流程,送74clocks,cs拉低,送令命CMD0(0x4000000095),距離8*clock*n後,等待從裝置的0x1應答,cs再次拉高
必須注意的是,reset和初始化時時鐘率速必須下降,至于率速降到多少貌似還沒有定論,我初始化的時候時降到了300K閣下,貌似400K一下都可以作操。
unsigned char SDReset(void){
unsigned char times, temp, i;
unsigned char pcmd[] = {0x40,0x00,0x00,0x00,0x00,0x95}; // CMD0
// send SDCard 74 clocks
spi_core_slave_select(SD, SDDisable);
SetSDClockInitRate(SD); // slow down sdcard clock speed
for(i=0; i<0x0f; i++){
spi_write_ignore_read(SD, 0xff); // 8*15 = 120 clocks
}
// send CMD0
spi_core_slave_select(SD, SDEnable);
times = 0;
do{
temp = SDWriteCmd(pcmd);
times++;
if(times == TRY_TIME){
return INIT_CMD0_ERROR;
}
}while(temp!=0x01);
spi_core_slave_select(SD, SDDisable);
spi_write_ignore_read(SD, 0xff);
return 0;
}
初始化SD卡時序:cs拉低,送令命CMD0(0x41000000ff),距離8*clock*n後,等待從裝置的0x0應答,cs再次拉高
初始化代碼:
unsigned char SDInit(void){
unsigned char times, temp;
unsigned char pcmd[] = {0x41,0x40,0x00,0x00,0x00,0xff};
spi_core_slave_select(SD, SDEnable);
times = 0;
do{
temp = SDWriteCmd(pcmd);
times++;
if(times==TRY_TIME){
return INIT_CMD1_ERROR;
}
}while(temp!=0x00);
SetSDClockTransferRate(SD); //set sdcrad clock as transfre clock
spi_core_slave_select(SD, SDDisable);
spi_write_ignore_read(SD, 0xff);
return 0;
}
至于CID和CSD的取讀,程式裡沒有實作,有興緻的友朋可以根據CMD0和CMD1的時序自己敲個代碼上去彌補完全
每日一道理
人生是潔白的畫紙,我們每個人就是手握各色筆的畫師;人生也是一條看不到盡頭的長路,我們每個人則是人生道路的遠足者;人生還像是一塊神奇的土地,我們每個人則是手握農具的耕耘者;但人生更像一本難懂的書,我們每個人則是孜孜不倦的讀書郎。
在認默情況下,SD卡的寫讀block巨細都是512bytes,是以就按照最平日的block巨細行進寫讀作操。
block讀作操:cs拉低,送令命CMD17(0x51000000ff),距離8*clock*n後,等待從裝置的0x0應答,再次距離距離8*clock*n,取讀開始位元組(0xfe),取讀512bytes,棄丢2bytes的CRC,cs再次拉高
寫讀一個section的代碼:
unsigned char SDReadSector(unsigned long addr,unsigned char *buffer){
unsigned char temp,times;
unsigned char i;
unsigned char pcmd[]={0x51,0x00,0x00,0x00,0x00,0xff}; // CMD17
addr<<=9;
pcmd[1]=addr>>24;
pcmd[2]=addr>>16;
pcmd[3]=addr>>8;
pcmd[4]=addr;
spi_core_slave_select(SD, SDEnable);
times = 0;
do{
temp = SDWriteCmd(pcmd);
times++;
if(times==TRY_TIME){
return READ_BLOCK_ERROR;
}
}while(temp!=0x00);
// check if datas ready ,then recevie datas and two bytes CRC(ignored)
do{
temp = spi_read_ignore_write(SD);
}while(temp!=0xfe);
for(i=0; i<64; i++){
*(buffer++) = spi_read_ignore_write(SD);
*(buffer++) = spi_read_ignore_write(SD);
*(buffer++) = spi_read_ignore_write(SD);
*(buffer++) = spi_read_ignore_write(SD);
*(buffer++) = spi_read_ignore_write(SD);
*(buffer++) = spi_read_ignore_write(SD);
*(buffer++) = spi_read_ignore_write(SD);
*(buffer++) = spi_read_ignore_write(SD);
}
spi_read_ignore_write(SD);
spi_read_ignore_write(SD);
spi_core_slave_select(SD, SDDisable);
spi_write_ignore_read(SD, 0xff);
return 0;
}
block寫作操:cs拉低,送令命CMD24(0x58000000ff),距離8*clock*n後,等待從裝置的0x0應答,再次距離距離8*clock*n,入寫開始位元組(0xfe),入寫512bytes,寫2bytes的dummy CRC,取讀應答位元組0x05,等待SD忙狀态束結,cs再次拉高。
寫作操代碼:
unsigned char SDWriteSector(unsigned long addr,unsigned char *buffer){
unsigned char temp,times;
unsigned char i;
unsigned char pcmd[] = {0x58,0x00,0x00,0x00,0x00,0xff}; // CMD24
addr<<=9;
// *((unsigned long *)(pcmd+1))=addr;
pcmd[1]=addr>>24;
pcmd[2]=addr>>16;
pcmd[3]=addr>>8;
pcmd[4]=addr;
spi_core_slave_select(SD, SDEnable);
times = 0;
do{
temp = SDWriteCmd(pcmd);
times++;
if(times==TRY_TIME){
return temp;
}
}while(temp!=0x00);
// insert some clocks
for(i=0; i<10; i++){
spi_read_ignore_write(SD);
}
// write 512 bytes to SD Card ,and two bytes CRC(ignored)
spi_write_ignore_read(SD, 0xfe);
for(i=0; i<64; i++){
spi_write_ignore_read(SD, *buffer++);
spi_write_ignore_read(SD, *buffer++);
spi_write_ignore_read(SD, *buffer++);
spi_write_ignore_read(SD, *buffer++);
spi_write_ignore_read(SD, *buffer++);
spi_write_ignore_read(SD, *buffer++);
spi_write_ignore_read(SD, *buffer++);
spi_write_ignore_read(SD, *buffer++);
}
spi_write_ignore_read(SD, 0xff);
spi_write_ignore_read(SD, 0xff);
// check if datas have been written to SD Card
temp = spi_read_ignore_write(SD);
if((temp & 0x1F)!=0x05){
spi_core_slave_select(SD, SDDisable);
return WRITE_BLOCK_ERROR;
}
do{
temp = spi_read_ignore_write(SD);
}while(temp!=0xff);
spi_core_slave_select(SD, SDDisable);
spi_write_ignore_read(SD, 0xff);
return 0;
}
對于多個blocks的連續寫作操,直接參考代碼吧,隻是用連續寫作操令命CMD18,令命的數參段位入寫datas時的address,好吧,有了寫作操程序連續寫的程序也不難,上代碼咯~
連續寫n個sections代碼:
unsigned char SDWritenSector(unsigned long nsec,unsigned long addr,unsigned char *buffer){
unsigned char temp,times;
unsigned long i, j;
unsigned char pcmd[] = {0x59,0x00,0x00,0x00,0x00,0xff};
unsigned char *temp_buf = buffer;
if(sd_ver==0x05 || !addr_mode) addr<<=9;
pcmd[1]=addr>>24;
pcmd[2]=addr>>16;
pcmd[3]=addr>>8;
pcmd[4]=addr;
spi_core_slave_select(SD, SDEnable);
times = 0;
do{
temp = SDWriteCmd(pcmd);
times++;
if(times==TRY_TIME){
return temp;
}
}while(temp!=0x00);
// insert some clocks
for(i=0; i<10; i++){
spi_read_ignore_write(SD);
}
// write datas to sections
for(j=0; j<nsec; j++){
// write 512 bytes to SD Card ,and two bytes CRC(ignored)
spi_write_ignore_read(SD, 0xfc);
for(i=0; i<64; i++){
spi_write_ignore_read(SD, *buffer++);
spi_write_ignore_read(SD, *buffer++);
spi_write_ignore_read(SD, *buffer++);
spi_write_ignore_read(SD, *buffer++);
spi_write_ignore_read(SD, *buffer++);
spi_write_ignore_read(SD, *buffer++);
spi_write_ignore_read(SD, *buffer++);
spi_write_ignore_read(SD, *buffer++);
}
spi_write_ignore_read(SD, 0xff);
spi_write_ignore_read(SD, 0xff);
// check if datas have been write to SD Card
temp = spi_read_ignore_write(SD);
if((temp & 0x1F)!=0x05){
spi_core_slave_select(SD, SDDisable);
return WRITE_BLOCK_ERROR;
}
while(spi_read_ignore_write(SD)!=0xff);
buffer=temp_buf;
}
spi_write_ignore_read(SD, 0xfd);
while(spi_read_ignore_write(SD)!=0xff);
spi_core_slave_select(SD, SDDisable);
spi_write_ignore_read(SD, 0xff);
return 0;
}
OK,到這裡SD卡的驅動就基本可以用了,部全的碼源的話可以有興緻的友朋郵件我,接下來就是還有一些用到的函數都講講吧。
SD驅動的寫讀函數
void spi_write_ignore_read(int core, char dat){
spi_core_write_data(core, dat);
while (!(spi_core_data_avail(core))); // Wait for the transaction (should generate a byte)
spi_core_read_data(core);
}
char spi_read_ignore_write(int core){
spi_core_write_data(core, 0xff);
while (!(spi_core_data_avail(core))); // Wait for the transaction (should generate a byte)
return spi_core_read_data(core);
}
Oc-simple-spi的初始化函數
void SpiCoreInit(int core){
// disable spi core, and deselect sdcard
spi_core_enable(core);
spi_core_slave_select(core, SDDisable);
// clear read buffer
while (spi_core_data_avail(core)){
spi_core_read_data(core);
}
// setup default clock = sysclk/128
spi_core_clock_setup(core, 0, 0, 0x01, 0x02);
// enable spi core
spi_core_slave_select(core, SDEnable);
spi_core_enable(core);
}
Oc-simple-spi的時鐘率速切換函數
void SetSDClockInitRate(int core){
spi_core_disable(core);
// set sdcard clock = sysclk/128
spi_core_clock_setup(core, 0, 0, 0x01, 0x02);
spi_core_enable(core);
}
void SetSDClockTransferRate(int core){
spi_core_disable(core);
// set sdcard clock = sysclk/2
spi_core_clock_setup(core, 0, 0, 0x00, 0x00);
spi_core_enable(core);
}
以上那些函數都和體具的oc-simple-spi core相幹,可以參考這個core的specs來看
SD驅動的初始化函數,這個函數用于下一節提到的在植移znFat時須要駁接的SD卡複位函數
unsigned char SDReady(void){
SDReset();
SDCheckVersion();
SDGetAddrMode();
return SDInit();
}
好,沒了,驅動寫到這裡就OK了,下節根據znFat的植移教程植移這個檔案系統
文章結束給大家分享下程式員的一些笑話語錄: 開發時間
項目經理: 如果我再給你一個人,那可以什麼時候可以完工?程式員: 3個月吧!項目經理: 那給兩個呢?程式員: 1個月吧!
項目經理: 那100呢?程式員: 1年吧!
項目經理: 那10000呢?程式員: 那我将永遠無法完成任務.