[原創][連載].基於SOPC的簡易數位相框 – Nios II SBTE部分(軟體部分) – SD卡(SPI模式)驅動

來源:互聯網
上載者:User
文章目錄
  • 步驟1 添加sd_card檔案夾到APP工程路徑
  • 步驟2 編寫代碼
  • 步驟3 調用SD卡驅動函數

上一講,我們完成了Nios II SBTE的配置工作。下面講解如何根據已有參考資料(手冊及代碼)編寫SD卡驅動。

準備工具及資料

1. WinHex

2. Efronc的博文SD/MMC 介面及上電時序、SD/MMC 內部寄存器、SD/MMC SPI模式下命令集

驅動編寫及調試步驟1 添加sd_card檔案夾到APP工程路徑

如何添加,請參考[原創][連載].基於SOPC的簡易數位相框 – Nios II SBTE部分(軟體部分) - 配置工作。

步驟2 編寫代碼

SD卡有很多標準,此處選用最簡單的SD 1-線模式,即SPI模式。

代碼2.1 sd_card.h

#ifndef SD_CARD_H_#define SD_CARD_H_#include "my_types.h"#include "my_regs.h"#define ENABLE_SD_CARD_DEBUG // turn on debug messagevoid SD_CARD_Port_Init();void SD_CARD_Write_Byte(u8 byte);u8 SD_CARD_Read_Byte();u8 SD_CARD_Write_CMD(u8 *CMD);//u8 SD_CARD_Init();u8 SD_CARD_Write_Sector(u32 addr,u8 *buf);u8 SD_CARD_Read_Sector(u8 *CMD,u8 *buf,u16 n_bytes);u8 SD_CARD_Read_Sector_Start(u32 sector);void SD_CARD_Read_Data(u16 n_bytes,u8 *buf);void SD_CARD_Read_Data_LBA(u32 LBA,u16 n_bytes,u8 *buf);void SD_CARD_Read_Sector_End();u8 SD_CARD_Read_CSD(u8 *buf);u8 SD_CARD_Read_CID(u8 *buf);void SD_CARD_Get_Info(void);void SD_CARD_DEMO(void);#endif /* SD_CARD_H_ */

第5~6行,加入自訂的宏,統一代碼風格。第9行,開啟調試資訊顯示開關。調試正確後,可用添加註釋的方式的關閉開關。

第12行void SD_CARD_Port_Init(),為SPI介面的初始函數。

第13~14行void SD_CARD_Write_Byte(u8 byte)和u8 SD_CARD_Read_Byte(),為SPI寫位元組和讀位元組函數。

第15行u8 SD_CARD_Write_CMD(u8 *CMD),為SD卡寫命令函數。

第17行u8 SD_CARD_Init(),為SD卡的初始化函數。這個函數需要特別注意,因為SPI模式的模式的SD卡需要低速率收發資料來初始化SD卡。

第18~19行u8 SD_CARD_Write_Sector(u32 addr,u8 *buf)和u8 SD_CARD_Read_Sector(u8 *CMD,u8 *buf,u16 n_bytes)為SD卡寫塊和讀塊函數;需要注意的是,一般的SD卡的塊有512位元組,而通過WinHex 查看的SD卡的每個扇區也是512位元組。為了統一風格,此處一律寫作Sector。

第21行void SD_CARD_Read_Data_LBA(u32 LBA,u16 n_bytes,u8 *buf),比較好用,其參數LBA為Winhex中可查看的扇區地址,及邏輯塊地址;有了這個函數,我們後續的工作就方便了需要。

第24~25行u8 SD_CARD_Read_CSD(u8 *buf)和u8 SD_CARD_Read_CID(u8 *buf),為讀取SD卡的CSD和CID寄存器函數。

其他的函數請參考原始碼自行解析。

代碼2.2 sd_card.c

#include <unistd.h>#include "sd_card.h"// debug switch#ifdef ENABLE_SD_CARD_DEBUG  #include "debug.h"  #define SD_CARD_DEBUG(x)  DEBUG(x)#else  #define SD_CARD_DEBUG(x)#endif// error macro#define INIT_CMD0_ERROR   0x01#define INIT_CMD1_ERROR   0x02#define WRITE_BLOCK_ERROR 0x03#define READ_BLOCK_ERROR  0x04// SD-CARD(SPI mode) initial with low speed// insert a certain delay#define SD_CARD_INIT_DELAY usleep(10)// CID info structuretypedef union{  u8 data[16];  struct  {    u8 MID;   // Manufacture ID; Binary    u8 OLD[2];// OEM/Application ID; ASCII    u8 PNM[5];// Product Name; ASCII    u8 PRV;   // Product Revision; BCD    u8 PSN[4];// Serial Number; Binary    u8 MDT[2];// Manufacture Data Code; BCD; upper 4 bits of first byte are reserved    u8 CRC;   // CRC7_checksum; Binary; LSB are reserved  };}CID_Info_STR;// CSD info structuretypedef struct{  u8 data[16];  u32 capacity_MB;  u8 READ_BL_LEN;  u16 C_SIZE;  u8 C_SIZE_MULT;}CSD_Info_STR;// flagsu16 gByteOffset=0;       // byte offset in one sectoru16 gSectorOffset=0;     // sector offset in SD-CARDbool gSectorOpened=FALSE;// set to 1 when a sector is opened.bool gSD_CARDInit=FALSE; // set it to 1 when SD-CARD is initialized// SD-CARD port initvoid SD_CARD_Port_Init(){  sd_CLK=1;  sd_DOUT=1;  sd_nCS=1;}// write a byte to SD-CARDvoid SD_CARD_Write_Byte(u8 byte){  u8 i;  for(i=0;i<8;i++)  { // MSB First    sd_DIN=(byte >> (7-i)) & 0x1;    sd_CLK=0;if(gSD_CARDInit) SD_CARD_INIT_DELAY;    sd_CLK=1;if(gSD_CARDInit) SD_CARD_INIT_DELAY;  }}// read a byte to SD-CARDu8 SD_CARD_Read_Byte(){  u8 i,byte;  byte=0;  for(i=0;i<8;i++)  { // MSB First    sd_CLK=0;if(gSD_CARDInit) SD_CARD_INIT_DELAY;    byte<<=1;if(sd_DOUT) byte++;    sd_CLK=1;if(gSD_CARDInit) SD_CARD_INIT_DELAY;  }  return byte;}// write a command to SD-CARD// return: the second byte of response register of SD-CARDu8 SD_CARD_Write_CMD(u8 *CMD){  u8 temp,retry;  u8 i;  sd_nCS=1; // set chipselect (disable SD-CARD)  SD_CARD_Write_Byte(0xFF); // send 8 clock impulse  sd_nCS=0; // clear chipselect (enable SD-CARD)  // write 6 bytes command to SD-CARD  for(i=0;i<6;i++) SD_CARD_Write_Byte(*CMD++);  // get 16 bits response  SD_CARD_Read_Byte(); // read the first byte, ignore it.  retry=0;  do  { // only last 8 bits is valid    temp=SD_CARD_Read_Byte();    retry++;  }  while((temp==0xff) && (retry<100));  return temp;}// SD-CARD initialization(SPI mode)u8 SD_CARD_Init(){  u8 retry,temp;  u8 i;  u8 CMD[]={0x40,0x00,0x00,0x00,0x00,0x95};  SD_CARD_Port_Init();    usleep(1000);  SD_CARD_DEBUG(("SD-CARD Init!\n"));  gSD_CARDInit=TRUE; // Set init flag of SD-CARD    for(i=0;i<10;i++) SD_CARD_Write_Byte(0xff);// send 74 clock at least!!!  // write CMD0 to SD-CARD  retry=0;  do  { // retry 200 times to write CMD0    temp=SD_CARD_Write_CMD(CMD);    retry++;    if(retry==200) return INIT_CMD0_ERROR;// CMD0 error!  }  while(temp!=1);  //write CMD1 to SD-CARD  CMD[0]=0x41;// Command 1  CMD[5]=0xFF;  retry=0;  do  { // retry 100 times to write CMD1    temp=SD_CARD_Write_CMD(CMD);    retry++;    if(retry==100)  return INIT_CMD1_ERROR;// CMD1 error!  }  while(temp!=0);  gSD_CARDInit=FALSE; // clear init flag of SD-CARD  sd_nCS=1; // disable SD-CARD  SD_CARD_DEBUG(("SD-CARD Init Suc!\n"));  return 0x55;// All commands have been taken.}// writing a Block(512Byte, 1 sector) to SD-CARD// return 0 if sector writing is completed.u8 SD_CARD_Write_Sector(u32 addr,u8 *buf){  u8 temp,retry;  u16 i;  // CMD24 for writing blocks  u8 CMD[]={0x58,0x00,0x00,0x00,0x00,0xFF};  SD_CARD_DEBUG(("Write A Sector Starts!!\n"));  addr=addr << 9;// addr=addr * 512  CMD[1]=((addr & 0xFF000000) >>24 );  CMD[2]=((addr & 0x00FF0000) >>16 );  CMD[3]=((addr & 0x0000FF00) >>8 );  // write CMD24 to SD-CARD(write 1 block/512 bytes, 1 sector)  retry=0;  do  { // retry 100 times to write CMD24    temp=SD_CARD_Write_CMD(CMD);    retry++;    if(retry==100) return(temp);//CMD24 error!  }  while(temp!=0);  // before writing, send 100 clock to SD-CARD  for(i=0;i<100;i++) SD_CARD_Read_Byte();  // write start byte to SD-CARD  SD_CARD_Write_Byte(0xFE);  SD_CARD_DEBUG(("\n"));  // now write real bolck data(512 bytes) to SD-CARD  for(i=0;i<512;i++) SD_CARD_Write_Byte(*buf++);  SD_CARD_DEBUG(("CRC-Byte\n"));  SD_CARD_Write_Byte(0xFF);// dummy CRC  SD_CARD_Write_Byte(0xFF);// dummy CRC  // read response  temp=SD_CARD_Read_Byte();  if( (temp & 0x1F)!=0x05 ) // data block accepted ?  {    sd_nCS=1; // disable SD-CARD    return WRITE_BLOCK_ERROR;// error!  }  // wait till SD-CARD is not busy  while(SD_CARD_Read_Byte()!=0xff){};  sd_nCS=1; // disable SD-CARD  SD_CARD_DEBUG(("Write Sector suc!!\n"));  return 0;}// read bytes in a block(normally 512KB, 1 sector) from SD-CARD// return 0 if no error.u8 SD_CARD_Read_Sector(u8 *CMD,u8 *buf,u16 n_bytes){  u16 i;  u8 retry,temp;  // write CMD to SD-CARD  retry=0;  do  { // Retry 100 times to write CMD    temp=SD_CARD_Write_CMD(CMD);    retry++;    if(retry==100) return READ_BLOCK_ERROR;// block read error!  }  while(temp!=0);  // read start byte form SD-CARD (0xFE/Start Byte)  while(SD_CARD_Read_Byte()!=0xfe);  // read bytes in a block(normally 512KB, 1 sector) from SD-CARD  for(i=0;i<n_bytes;i++)  *buf++=SD_CARD_Read_Byte();  SD_CARD_Read_Byte();// dummy CRC  SD_CARD_Read_Byte();// dummy CRC  sd_nCS=1; // disable SD-CARD  return 0;}// return: [0]-success or something error!u8 SD_CARD_Read_Sector_Start(u32 sector){  u8 retry;  // CMD16 for reading Blocks  u8 CMD[]={0x51,0x00,0x00,0x00,0x00,0xFF};  u8 temp;  // address conversation(logic block address-->byte address)  sector=sector << 9;// sector=sector * 512  CMD[1]=((sector & 0xFF000000) >>24 );  CMD[2]=((sector & 0x00FF0000) >>16 );  CMD[3]=((sector & 0x0000FF00) >>8 );  // write CMD16 to SD-CARD  retry=0;  do  {    temp=SD_CARD_Write_CMD(CMD);    retry++;    if(retry==100) return READ_BLOCK_ERROR;// READ_BLOCK_ERROR  }  while( temp!=0 );    // read start byte form SD-CARD (feh/start byte)  while (SD_CARD_Read_Byte() != 0xfe);  SD_CARD_DEBUG(("Open a Sector Succ!\n"));  gSectorOpened=TRUE;  return 0;}void SD_CARD_Read_Data(u16 n_bytes,u8 *buf){  u16 i;  for(i=0;((i<n_bytes) && (gByteOffset<512));i++)  {    *buf++=SD_CARD_Read_Byte();    gByteOffset++;// increase byte offset in a sector  }  if(gByteOffset==512)  {     SD_CARD_Read_Byte(); // Dummy CRC    SD_CARD_Read_Byte(); // Dummy CRC    gByteOffset=0;       // clear byte offset in a sector    gSectorOffset++;     // one sector is read completely    gSectorOpened=FALSE; // set to 1 when a sector is opened    sd_nCS=1;            // disable SD-CARD  }}// read block date by logic block address(sector offset)void SD_CARD_Read_Data_LBA(u32 LBA,u16 n_bytes,u8 *buf){ // if one sector is read completely; open the next sector  if(gByteOffset==0) SD_CARD_Read_Sector_Start(LBA);  SD_CARD_Read_Data(n_bytes,buf);}// dummy read out the rest bytes in a sectorvoid SD_CARD_Read_Sector_End(){  u8 temp[1];  while((gByteOffset!=0x00) | (gSectorOpened==TRUE))    SD_CARD_Read_Data(1,temp); // dummy read  }// read CSD registers of SD-CARD// return 0 if no error.u8 SD_CARD_Read_CSD(u8 *buf){ // command for reading CSD registers  u8 CMD[]={0x49,0x00,0x00,0x00,0x00,0xFF};  return SD_CARD_Read_Sector(CMD,buf,16);// read 16 bytes}// read CID register of SD-CARD// return 0 if no error.u8 SD_CARD_Read_CID(u8 *buf){ // command for reading CID registers  u8 CMD[]={0x4A,0x00,0x00,0x00,0x00,0xFF};  return SD_CARD_Read_Sector(CMD,buf,16);//read 16 bytes}void SD_CARD_Get_Info(void){  CID_Info_STR CID;  CSD_Info_STR CSD;  SD_CARD_Read_CID(CID.data);  SD_CARD_DEBUG(("SD-CARD CID:\n"));  SD_CARD_DEBUG(("  Manufacturer ID(MID): 0x%.2X\n", CID.MID));  SD_CARD_DEBUG(("  OEM/Application ID(OLD): %c%c\n", CID.OLD[0], CID.OLD[1]));  SD_CARD_DEBUG(("  Product Name(PNM): %c%c%c%c%c\n", CID.PNM[0], CID.PNM[1], CID.PNM[2], CID.PNM[3], CID.PNM[4]));  SD_CARD_DEBUG(("  Product Revision: 0x%.2X\n", CID.PRV));  SD_CARD_DEBUG(("  Serial Number(PSN): 0x%.2X%.2X%.2X%.2X\n", CID.PSN[0], CID.PSN[1], CID.PSN[2], CID.PSN[3]));  SD_CARD_DEBUG(("  Manufacture Date Code(MDT): 0x%.1X%.2X\n", CID.MDT[0] & 0x0F, CID.MDT[1]));  SD_CARD_DEBUG(("  CRC-7 Checksum(CRC7):0x%.2X\n", CID.CRC >> 1));  SD_CARD_Read_CSD(CSD.data);  CSD.C_SIZE = ((CSD.data[6]&0x03) << 10) | (CSD.data[7] << 2) | ((CSD.data[8]&0xC0) >>6);  CSD.C_SIZE_MULT = ((CSD.data[9]&0x03) << 1) | ((CSD.data[10]&0x80) >> 7);  CSD.READ_BL_LEN = (CSD.data[5]&0x0F);  CSD.capacity_MB = (((CSD.C_SIZE)+1) << (((CSD.C_SIZE_MULT) +2) + (CSD.READ_BL_LEN))) >> 20;  SD_CARD_DEBUG(("SD-CARD CSD:\n"));  SD_CARD_DEBUG(("  max.read data block length: %d\n", 1<<CSD.READ_BL_LEN));  SD_CARD_DEBUG(("  device size: %d\n", CSD.C_SIZE));  SD_CARD_DEBUG(("  device size multiplier: %d\n", CSD.C_SIZE_MULT));  SD_CARD_DEBUG(("  device capacity: %d MB\n", CSD.capacity_MB));}void SD_CARD_DEMO(void){  u16 i;  u8 buf[512];  // init SD-CARD  while(SD_CARD_Init() != 0x55);  // Get CID & CSD  SD_CARD_Get_Info();  // read the 1st block(sector) of SD-Card  SD_CARD_Read_Data_LBA(0,512,buf);  for(i=0; i<512; i++)  {    SD_CARD_DEBUG(("%.2X ", buf[i]));    if((i+1) % 16 == 0) SD_CARD_DEBUG(("\n"));  }}

源碼很長,我簡單說明其中比較重要的幾點。

第58行,申明一個bool型的全域變數bool gSD_CARDInit=FALSE;我們在u8 SD_CARD_Init()函數中將此變數置一或清零,然後在函數void SD_CARD_Write_Byte(u8 byte)和u8 SD_CARD_Read_Byte()檢測此變數,以實現慢速率SPI初始化SD卡。

我們拿void SD_CARD_Write_Byte(u8 byte)做說明。

// read a byte to SD-CARDu8 SD_CARD_Read_Byte(){  u8 i,byte;  byte=0;  for(i=0;i<8;i++)  { // MSB First    sd_CLK=0;if(gSD_CARDInit) SD_CARD_INIT_DELAY;    byte<<=1;if(sd_DOUT) byte++;    sd_CLK=1;if(gSD_CARDInit) SD_CARD_INIT_DELAY;  }  return byte;}

由於是採用GPIO類比SPI匯流排,而Nios II(100MHz nios/f)的GPIO比較慢,因此無需延時即可實現25MHz的速率。但是初始化SD卡的時候必須採用

低於400KHz的時鐘,需要插入適當延時。我以前也說過Nios II的延時不準,故此延時需要多次調試。我在第23行,使用一個宏來設定需要插入的延時。

由於CID寄存器的資訊與位元組比較對齊,因此第27~40行,使用了聯合體來儲存CID寄存器。而CSD寄存器內容比較零散,就沒有採用聯合體,而是使用了結構體(第44~51行)來儲存資訊。這樣做的目的主要是為了理解方便,但是對儲存空間是比較浪費的。

第326行void SD_CARD_DEMO(void)函數中,先初始化SD卡,然後讀取其第0個塊(扇區)的內容。

關於SD(SPI)的寄存器結構、儲存結構和指令體系等,請自行認真閱讀相關資料,此處不解析。

步驟3 調用SD卡驅動函數

代碼3.1 main.c

#include <stdio.h>                    // printf()#include <unistd.h>                   // usleep()#include "my_types.h"                 // 資料類型#include "debug.h"                    // debug#include "sd_card.h"#define ENABLE_APP_DEBUG // turn on debug message#ifdef ENABLE_APP_DEBUG    #define APP_DEBUG(x)    DEBUG(x)#else    #define APP_DEBUG(x)#endifint main(void){  SD_CARD_DEMO();  while(1)  {  }  return 0;}

jtag-uart列印的資訊如下。

(黃色為SD卡初始化調試資訊;綠色為CID寄存器資訊;青色為CSD寄存器資訊)

(第0扇區的內容)

下面我們通過WinHex讀取SD卡的第一扇區的內容,注意與對比。

 

對比資料顯示,SD_CARD_Read_Data_LBA函數可實現SD卡塊讀取動作。

其他問題

下面講下如何在SD卡內讀取二進位檔案。我先使用Notspad++(或記事本)建立一個檔案,儲存為SD卡的某個位置,命名為test.bin。

簡單起見,我直接把sd_card.h另存到我的SD卡內(FAT32格式),命名為test.bin。

 

  (查看test.bin的屬性)

在FAT16/32內,檔案的資料總是從某個扇區的0位元組開始連續儲存的,若檔案較大則需要連續儲存n個扇區;需要注意的是最後的一個扇區如果沒有存滿,則補0。上面我們通過查看屬性,得知test.bin的檔案大小為718位元組,即需要佔用718/512=1.4,取2,即2個扇區。下面使用WinHex來查看檔案的資料如何儲存。Crtl+F7,開啟目錄查看器,選擇test.bin檔案。注意到test.bin的標識id和左下角顯示的扇區地址移植。拖動

拖動文本,直到文本的結尾。 觀察 和 ,即佔用了第81336和81337兩個扇區。知道了扇區地址和扇區內的位元組位移,即可使用void SD_CARD_Read_Data_LBA(u32 LBA,u16 n_bytes,u8 *buf)函數讀取到想要的資料。

 

源碼下載

 lcd_at_nios_nii_part.zip 

目錄

1 [原創][連載].基於SOPC的簡易數位相框 -  Quartus II部分(硬體部分)

2 [原創][連載].基於SOPC的簡易數位相框 -  Nios II SBTE部分(軟體部分)-  配置工作

3 [原創][連載].基於SOPC的簡易數位相框 -  Nios II SBTE部分(軟體部分)-  SD卡(SPI模式)驅動

4 [原創][連載].基於SOPC的簡易數位相框 -  Nios II SBTE部分(軟體部分)-  TFT-LCD(控制器為ILI9325)驅動

5 [原創][連載].基於SOPC的簡易數位相框 -  Nios II SBTE部分(軟體部分)-  從SD卡內讀取圖片檔案,然後顯示在TFT-LCD上

6 [原創][連載].基於SOPC的簡易數位相框 -  Nios II SBTE部分(軟體部分)-  最佳化工作

7 [原創][連載].基於SOPC的簡易數位相框 -  Nios II SBTE部分(軟體部分)-  ADS7843觸控螢幕驅動測試

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.