MSP430的SPI通訊例程(SD卡初始化-理論解說)

liujj05發表於2015-03-31

最近需要做一個資料儲存,發現SD卡這一塊還不太好弄
現在的微控制器有相當一部分還不支援SDIO,比如MSP430(據我所知,如果有支援的型號了還請及時告訴我~),所以只好用SPI通訊來進行SD卡的操作,雖然後續涉及到更為複雜的FAT等等,但是首先需要解決的仍然是建立通訊的問題。

採用的微控制器型號為MSP430F5438A,用了一個開發板。

SPI通訊基本例程

例程及解釋如下:

//===========================================================================//
//                                                                           //
// 檔案:  MAIN.C                                                            //
// 說明:  BW-DK5438開發板微控制器SPI埠實驗程式                              //
//         設定SPI埠工作模式,通過SPI埠傳送資料                          //
//         可在P3.1/P3.3埠觀察資料、時鐘波形                               //
// 編譯:  IAR Embedded Workbench IDE for MSP430 v4.21                       //
// 版本:  v1.1                                                              //
// 編寫:  JASON.ZHANG                                                       //
// 版權:  北京拓普博維電子技術有限公司                                      //
//                                                                           //
//===========================================================================//

#include "msp430x54x.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "PIN_DEF.H"

#define  FLL_FACTOR     649                                 // FLL_FACTOR: DCO倍頻係數    
char  event, RXBuffer[2]                                  ;


//***************************************************************************//
//                                                                           //
//                 初始化主時鐘: MCLK = XT1×(FLL_FACTOR+1)                  //
// 主時鐘這部分來說,應該沒有什麼額外的問題
//                                                                           //
//***************************************************************************//
void Init_CLK(void)
{
  WDTCTL     = WDTPW + WDTHOLD                            ; // 關看門狗
  P7SEL     |= 0x03                                       ; // 埠選擇外部低頻晶振XT1(32.768kHz)
  UCSCTL6   &=~XT1OFF                                     ; // 使能外部晶振 
  UCSCTL6   |= XCAP_3                                     ; // 設定內部負載電容
  UCSCTL3   |= SELREF_2                                   ; // DCOref = REFO
  UCSCTL4   |= SELA_0                                     ; // ACLK   = XT1  
  __bis_SR_register(SCG0)                                 ; // 關閉FLL控制迴路
  UCSCTL0    = 0x0000                                     ; // 設定DCOx, MODx
  UCSCTL1    = DCORSEL_7                                  ; // 設定DCO振盪範圍
  UCSCTL2    = FLLD__1 + FLL_FACTOR                       ; // Fdco = ( FLL_FACTOR + 1)×FLLRef = (649 + 1) * 32768 = 21.2992MHz
  __bic_SR_register(SCG0)                                 ; // 開啟FLL控制迴路                                                            
  __delay_cycles(1024000)                                 ; 
  do
  {
    UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + XT1HFOFFG + DCOFFG); // 清除 XT2,XT1,DCO 錯誤標誌                                                            
    SFRIFG1 &= ~OFIFG                                     ; 
  }while(SFRIFG1&OFIFG)                                   ; // 檢測振盪器錯誤標誌 
}

//***************************************************************************//
//                                                                           //
//  Init_Port(void): 設定IO埠                                              //
//                                                                           //
//***************************************************************************//
void Init_Port(void)
{
  P5DIR  |= POWER                                         ; // 主電源
  MAIN_POWER_ON                                           ;
  P7DIR  |= LED_PWR                                       ; // 發光二極體電源
  P7OUT  &=~LED_PWR                                       ;
  INTERNAL_PULL_UP                                        ; // 使能鍵盤埠內部上拉電阻 
  ROW_IN_COL_OUT                                          ; // 設定行輸入,列輸出0
}

//***************************************************************************//
//                                                                           //
//  Init_SPI(void): 設定SPI埠                                              //
//                                                                           //
//***************************************************************************//
void Init_SPI(void)
{  

//這裡對P8的操作很奇怪,似乎並不需要
  P8OUT    |= LCD_NCS                                     ; // SPI埠複用,DisableTFT液晶埠
  P8DIR    |= LCD_NCS                                     ;

//P3的1,2,3是一個SPI埠
  P3OUT    |= NCS25                                       ; 
  P3DIR    |= NCS25                                       ;
  P3SEL    &= 0xF0                                        ;
  P3SEL    |= 0x0E                                        ; // // P3.1/2/3功能選擇為SPI

  UCB0CTL1 |= UCSWRST                                     ; // 復位SPI狀態機

  UCB0CTL0 |= UCMST+UCSYNC+UCCKPL+UCMSB                   ; // 3-pin, 8-bit SPI master, Clock polarity high, MSB

  UCB0CTL1 |= UCSSEL_2                                    ; // 選擇SCK參考源為SMCLK

  UCB0BR1   = 0                                           ; 
  UCB0CTL1 &=~UCSWRST                                     ; // SPI狀態機使能
}


void main( void )
{
  WDTCTL = WDTPW + WDTHOLD                                ;
  Init_CLK()                                              ;
  Init_Port()                                             ;
  Init_SPI()                                              ;
  for(;;)
  {
     UCB0TXBUF = 0x55                                     ; 
     __delay_cycles(280)                                  ; 
  }
}

鑑於CSDN的Markdown程式碼高亮做的實在是……唉,這裡貼一個容易看的吧:
SPI通訊程式碼

SD卡通訊方式

以上是給出的例程,SD卡有自己的一套通訊方法:
這裡引述了:

http://elm-chan.org/docs/mmc/mmc_e.html

當中的內容並予以節選翻譯,以便日後查詢方面。

SD卡和MMC卡介面

MMC卡和SD卡先後推出,介面可以說基本差不多,供電是2.7~3.6V,不要供5V點,否則按照原文說法是“馬上壞”(@_@)

SD卡和微控制器的電氣連線方式如下圖
電氣連線方式
其中有一個上拉電阻有點令人費解

SPI通訊模式共有0~3四種,他們的區別是時鐘相位和極性。適用於MMC和SD的模式是mode 0(CPHA=0,CPOL=0),但是mode 3多數情況下也管用

SD卡初始化

下面的流程是整個初始化的步驟:
為了避免翻譯出現差錯,這裡也附上英文原文。

上電或卡剛插入時
當供電電壓達到2.7V以上時,至少等待1ms,將SPI時鐘訊號的頻率設定在100kHz~400kHz之間,並設定DI和CS(片選訊號)為高,並向SCLK輸入74個以上時鐘脈衝,記憶體卡將會進入到其預設操作模式並做好接收指令準備

軟體復位
保持CS(片選訊號)為低電平的情況下,傳送一個CMD0指令(就是reset指令,完整指令集的解釋附於文後)。SD卡將會在CMD0指令接收之後取樣CS訊號,如果CS訊號為低電平,SD卡將會進入SPI模式,並回復0x01。由於CMD0必須作為:“原生指令”(Native Command)傳送,所以CRC部分必須有一個有效(valid)的值(譯者注:原生指令中,CRC是固定格式中的一個部分,這裡的有效指的是佔位還是必須要校驗計算不清楚,還要再次確認,這裡,CRC作為一個校驗位應該和串列埠的奇偶校驗差不多,是隻需要選擇控制位就可以的,並不需要單獨程式設計計算)。一旦SD卡進入SPI模式之後,這個CRC部分將不再被使用,也不會再被檢查。所以,命令中的CRC可以相對固定,但是這個固定值要能夠在傳送CMD0 和 CMD8 這兩個命令(命令引數argument為0的情況下)時保證正確(CMD0的正確CRC,在argument為0的時候是0x95,CMD8待查)。
另外CRC這個特徵也可以使用CMD59進行切換,(切換什麼和怎樣切換沒有詳細交代)。

補充說明:CMD8命令的稍微詳細一些的介紹可以參看:
百度文庫

Power ON or card insersion
After supply voltage reached 2.2 volts, wait for one millisecond at least. Set SPI clock rate between 100 kHz and 400 kHz. Set DI and CS high and apply 74 or more clock pulses to SCLK. The card will enter its native operating mode and go ready to accept native command.

Software reset
Send a CMD0 with CS low to reset the card. The card samples CS signal on a CMD0 is received successfully. If the CS signal is low, the card enters SPI mode and responds R1 with In Idle State bit (0x01). Since the CMD0 must be sent as a native command, the CRC field must have a valid value. When once the card enters SPI mode, the CRC feature is disabled and the CRC is not checked by the card, so that command transmission routine can be written with the hardcorded CRC value that valid for only CMD0 and CMD8 with the argument of zero. The CRC feature can also be switched with CMD59.

初始化
SD卡在空閒狀態下,只接受CMD0,CMD1,ACMD41,CMD58以及CMD59這幾個指令,而拒絕其它任何指令。這是,讀取OCR暫存器並檢查卡片的工作電壓範圍(似乎通過CMD58進行查詢),如果供電超出範圍要彈出SD卡。
卡片在接收到CMD1命令時啟動初始化流程,主控晶片需要持續傳送CMD1並檢測回覆以確定初始化過程完成。R1回覆從0x01變為0x00(Idle State bit 清零)時,意味著初始化成功。初始化過程可能需要數百ms(卡片儲存空間越大時間越長),所以需要考慮一個判定超時的閾值。
Idle State bit 清零之後,一般的讀寫命令就可以執行了。此外因為在對SDC進行操作時,ACMD41被推薦代替CMD1,所以首先嚐試傳送ACMD41,被拒絕之後再試CMD1,這樣的一個流程在理想狀況下應該能夠適應所有的兩種卡。

Initialization
In idle state, the card accepts only CMD0, CMD1, ACMD41,CMD58 and CMD59. Any other commands will be rejected. In this time, read OCR register and check working voltage range of the card. In case of the system sypply voltage is out of working voltage range, the card must be rejected. Note that all cards work at supply voltage range of 2.7 to 3.6 volts at least, so that the host contoller needs not check the OCR if supply voltage is in this range. The card initiates the initialization process when a CMD1 is received. To detect end of the initialization process, the host controller must send CMD1 and check the response until end of the initialization. When the card is initialized successfuly, In Idle State bit in the R1 response is cleared (R1 resp changes 0x01 to 0x00). The initialization process can take hundreds of milliseconds (large cards tend to longer), so that this is a consideration to determin the time out value. After the In Idle State bit cleared, generic read/write commands will able to be accepted.
Because ACMD41 instead of CMD1 is recommended for SDC, trying ACMD41 first and retry with CMD1 if rejected, is ideal to support both type of the cards.

SCLK頻率應該設定的儘可能快,這樣可以使得讀寫的表現更好(應該是速度更快吧)。CSD暫存器中的TRAN_SPEED區域標明瞭卡片的最大時脈頻率。大部分情況下,MMC為20MHz,SDC則為25MHz。注意時脈頻率可以是在這兩個值之間的值,沒有中間出現開集的其它限制。
在2GB卡上,初始的讀寫 block length可能是1024,所以block size應該被重新初始化到512(使用CMD16命令)以配合FAT檔案系統的工作。

The SCLK rate should be changed to fast as possible to maximize the read/write performance. The TRAN_SPEED field in the CSD register indicates the maximum clock rate of the card. The maximum clock rate is 20MHz for MMC, 25MHz for SDC in most case. Note that the clock rate will able to be fixed to 20/25MHz in SPI mode because there is no open-drain condition that restricts the clock rate.
The initial read/write block length can be set 1024 on 2GB cards, so that the block size should be re-initialized to 512 with CMD16 to work with FAT file system.

初始化 high-capacity cards (不知道高階在什麼地方)
在傳送給卡片一個CMD0指令使其進入空閒模式後,在初始化之前傳送一個CMD8(語句為0x000001AA,需要有正確的校驗位CRC)。如果CMD8被拒絕(表現是會回覆一個命令錯誤提示0x05),那麼這個卡片是一個SDC一代卡或者MMC三代卡。如果命令被接受,會收到一個R7回覆(R1(0x10)+32位返回值)。這個32位返回值的低12位意味著這個卡是一個SDC二代卡。(接著又絮叨了一遍電壓範圍的事兒,似乎沒什麼意義)。在確認之後,啟動初始化:利用帶HCS flag(bit30)的ACMD41命令。後面似乎講的是如果這個bit 30 被置位,則這個卡就是眾所周知的SDHC/SDXC卡(但是it is set當中的這個it指的是什麼還是有些拿不準)。以下描述的資料讀取/寫入操作是按照block定址的而非按照byte定址。資料block大小固定在512bytes。

Initializing high-capacity cards
After the card enters idle state with a CMD0, send a CMD8 with argument of 0x000001AA and correct CRC prior to initialization process. If the CMD8 is rejected with illigal command error (0x05), the card is SDC version 1 or MMC version 3. If accepted, R7 response (R1(0x01) + 32-bit return value) will be returned. The lower 12 bits in the return value 0x1AA means that the card is SDC version 2 and it can work at voltage range of 2.7 to 3.6 volts. If not the case, the card should be rejected. And then initiate initialization with ACMD41 with HCS flag (bit 30). After the initialization completed, read OCR register with CMD58 and check CCS flag (bit 30). When it is set, the card is a high-capacity card known as SDHC/SDXC. The data read/write operations described below are commanded in block addressing insted of byte addressing. The size of data block at block addressing mode is fixed to 512 bytes

相關文章