STM32 USB Mass Storage 例程調試筆記

from:http://www.cnblogs.com/irwin0112/archive/2011/12/20/2294426.html

一、問題起因

     近來有幾個客戶反映STM3210E的開發板的USB Mass Storage 常式有點問題,組長安排我來調試。Mass
Storage常式在PC上實現兩個U盤,一個是SD盤,一個是NAND
Flash盤,把程式下載到開發板後,PC上能夠檢測到NAND
Flash盤和SD盤,卻提示磁片要格式化,按照提示操作,格式化不成功,可用磁碟空間和已用磁碟空間都為0。
調試前的準備
二、調試前準備
  調試之前花了三天的時間,大致的看了一下USB的框架,後來才發現,沒什麼必要,不過多學點知識總是好的。
      1.USB只提供一個資料通道,USB匯流排並不知道設備具體是怎麼操作,有什麼狀態,USB設備的狀態是設備自己來決定的,這就是USB描述符的功能了。描述符中記錄了設備的類型、廠商、產品ID、端點資訊等。描述符主要要:設備描述符、介面描述符、端點描述符、字串描述符等。設備描述符記錄了該設備使用的USB版本號、廠商ID、產品ID、可能的配置數等與設備相關的資訊;配置描述符記錄了配置包含的介面數、供電方式、是否支援遠端喚醒等;介面描述符記錄了介面的端點數、介面使用的類、協定等;端點描述符記錄了端點號、資料傳輸的方向、傳輸類型、最大包長度等;字串描述符記錄了一些文字資訊,方便客戶理解。
      USB協議中規定了四種傳輸類型:批量傳輸、同步傳輸、中斷傳輸和控制傳輸
      MassStorage常式中只用到了批量傳輸和控制傳輸,所以這裡只介紹這兩種方式
      (1)批量傳輸使用bulk
transaction傳輸資料,主要應用在資料大量資料傳輸和接受資料上同時又沒有頻寬和間隔時間要求的情況下。特點:要求保證傳輸。印表機、大型存放區設備和掃描器屬於這種類型這種類型的設備。適合於傳輸非常慢和大量被延遲的傳輸,可以等到所有其它類型的資料的傳輸完成之後再傳輸和接收資料。
       一次批量事物有三個階段:權杖包階段、資料包階段和握手包階段。
  第一階段 權杖包階段
 Host端發出一個Bulk的權杖請求、權杖包中包含了設備位址、端點號。
 如果權杖是IN請求 ,則是從Device到Host的請求;
 如果權杖是OUT請求,則是從Host到Device端的請求。
  第二階段 資料包階段
 根據先前請求的權杖的類型,資料傳輸有可能是IN方向,也有可能是OUT方向。傳輸資料的時候用DATA0和DATA1權杖攜帶著資料交替傳送。
 資料傳輸格式DATA1和DATA0,這兩個是重復資料,確保在1資料丟失時0可以補上,不至於資料丟失。
 第三階段 握手包階段
 如果資料是IN 方向,握手信號應該是Host端發出;
 如果資料是OUT方向,握手信號應該是Device端發出。
 握手信號可以為ACK, 表示正常回應,
 NAK, 表示沒有正確傳送。
 STALL,表示出現主機不可預知的錯誤。
       (2)控制傳輸
  作用:USB系統用來主要進行查詢配置和給USB設備發送通用的命令,它要保證資料傳輸過程的資料完整性。設備枚舉過程中的各種設備描述符的獲取以及設置位址、設置配置都是通過控制傳輸來實現的。特點:控制傳輸是雙向傳輸,資料量通常較小;資料傳送是無損性的。
 控制傳輸也分為三個階段,即權杖階段、資料傳送階段、握手階段
     2.USB協定中規定了一類大型存放區設備 mass storage device,包括u盤、移動硬碟等。大型存放區設備介面類別代碼(bInterfaceClass)為0x08,U盤介面子類代碼(bInterfaceSubClass)使用0x06,表示使用SCSI透明指令集.協議代碼(bInterfaceProtocol)有三種:0x01、0x00和0x50,前面兩種需要使用中斷傳輸,這裡使用的是最後一種協定,即協定使用僅批量傳輸(bulk only transport)。
  僅批量傳輸協議中規定了兩個特殊的類請求:bulk-only Mass Storage Reset 和Get Max LUN,前者是重定到命令狀態的請求,後者是獲取最大邏輯單元的請求。
      1.Get Max LUN的格式如下圖
usb協議中採用的是小端格式,這一點要格外注意,比如ASCII 0x55、0x53,用小端格式表示就是0x5355
bmRequestType為0xa1,表示它是發送到介面的類輸入請求,bRequest為0xfe,wIndex為請求的介面號,傳輸的資料長度為1位元組,設備將在資料過程返回1位元組的資料,表示設備有多少個邏輯單元,0表示1個邏輯單元,1表示有兩個。
  2.bulk-only Mass Storage Reset 的格式如下圖
bulk-only Mass Storage Reset請求是通知設備接下來的批量端點輸出資料為命令快封裝包CBW(Command
Block Wrapper),在這個請求中,僅需要設置一下狀態,說明接下來的資料是CBW,然後返回一個長度為0的狀態資料包。
  3.僅批量傳輸的的資料流程
  類請求完成後,就進入了資料傳輸過程,在僅批量資料傳輸協議中規定,資料傳輸分為三個階段:命令階段、資料階段和狀態階段。命令階段是由主機通過批量端點發送一個CBW(命令封裝包)的結構,在CBW中定義了要操作的命令以及傳輸資料的方向和數量,資料階段的傳輸方向由命令階段決定,而狀態階段則總是由設備返回該命令完成的狀態。
CBW的結構如下圖
官方文檔對這些欄位的介紹:
dCBWSignature:
Signature that helps identify this data packet as a CBW. The signature field
shall contain the value
43425355h (little endian), indicating a CBW.
dCBWTag:
A Command Block Tag sent by the host. The device shall echo the contents of this
field back to the
host in the dCSWTag field of the associated CSW. The dCSWTag positively
associates a CSW with the corresponding CBW.
dCBWDataTransferLength:
The number of bytes of data that the host expects to transfer on the Bulk-In or
Bulk-Out endpoint (as indicated by the Direction bit) during the execution
of this command. If this field is zero, the device and the host shall
transfer no data between the CBW and the associated CSW, and the device shall
ignore
bmCBWFlags:
The bits of this field are defined as follows:
Bit 7 Direction – the device shall ignore this bit if the
dCBWDataTransferLength field is
zero, otherwise:
0 = Data-Out from host to the device,
1 = Data-In from the device to the host.
Bit 6 Obsolete. The host shall set this bit to zero.
Bits 5..0 Reserved – the host shall set these bits to zero.
bCBWLUN:
The device Logical Unit Number (LUN) to which the command block is being sent.
For devices that
support multiple LUNs, the host shall place into this field the LUN to which
this command block is
addressed. Otherwise, the host shall set this field to zero.
bCBWCBLength:
The valid length of the CBWCB in bytes. This defines the valid length of the
command block. The
only legal values are 1 through 16 (01h through 10h). All other values are
reserved.
CBWCB:
The command block to be executed by the device. The device shall interpret the
first bCBWCBLength
bytes in this field as a command block as defined by the command set identified
by bInterfaceSubClass .
If the command set supported by the device uses command blocks of fewer than 16
(10h) bytes in
length, the significant bytes shall be transferred first, beginning with the
byte at offset 15 (Fh). The
device shall ignore the content of the CBWCB field past the byte at offset (15 +
bCBWCBLength – 1).
  命令封裝包CSW
dCSWSignature:
Signature that helps identify this data packet as a CSW. The signature field
shall contain the value
53425355h (little endian), indicating CSW.
dCSWTag:
The device shall set this field to the value received in the dCBWTag of the
associated CBW.
bCSWStatus:
bCSWStatus indicates the success or failure of the command. The device shall
set this byte to zero if
the command completed successfully. A non-zero value shall indicate a failure
during command
execution according to the following table:
Value Description
00h Command Passed (“good status”)
01h Command Failed
02h Phase Error
03h and 04h Reserved (Obsolete)
05h to FFh Reserved
  定義一個緩衝區用來接收命令塊封裝包CBW,然後進入到資料處理階段,在資料處理中,對CBW進行解碼,返回或者接收回應的資料。資料發送或者接收完畢後,進入到狀態階段,返回命令執行的情況,然後再次進入命令階段,等待主機發送CBW包。
3.SCSI命令集
小型電腦系統介面(英語:Small Computer System
Interface; 簡寫:SCSI),一種用於電腦和智慧設備之間(硬碟、軟盤機、光碟機、印表機、掃描器等)系統級介面的獨立處理器標準。 SCSI是一種智慧的通用介面標準。它是各種電腦與外部設備之間的介面標準。
在U盤中經常用到的命令有:INQUIRY、READ
CAPACITY 、READ(10)、WRITE(10)命令等。
INQUIRY命令請求查詢目標設備的一些基本資訊,操作碼為0x12,。
READ FORMAT CAPACITIES命令可以讓主機讀取設備各種可能的格式化容量的清單,如果設備中沒有存放裝置,則設備返回最大能夠支援的格式化容量。
讀容量命令READ CAPACITY可以讓主機讀取到當前存儲媒介的容量,操作代碼為0x25,READ CAPACITY讀取的是實際的磁片容量。
主機使用READ(10)命令來讀取實際磁片的資料,使用WRITE(10)來往設備中寫入實際的磁片資料。
4.STM32 相關知識
  STM32提供的有USB全速設備介面,支援USB全速匯流排、USB掛起/恢復操作,可以停止設備時鐘實現低功耗。USB和CAN共用一個專用的512位元組的SRAM記憶體用於資料的發送和接收,不能同時使用USB和CAN匯流排。
PC主機和微控制器之間的資料傳輸是通過共用這一專用的資料緩衝區來完成的,資料緩衝區能被USB外設直接訪問。這塊專用資料緩衝區的大小由所使用的端點數目和每個端點最大的資料分組大小所決定,每個端點最大可使用512 位元組緩衝區,最多可用於16個單向或8 個雙向端點。USB模組同PC主機通信,根據USB規範實現權杖分組的檢測,資料發送/ 接收的處理,和握手分組的處理。整個傳輸的格式由硬體完成,其中包括CRC的生成和校驗。 每個端點都有一個緩衝區描述塊,描述該端點使用的緩衝區位址、大小和需要傳輸的位元組數。 當USB模組識別出一個有效的功能/ 端點的權杖分組時,( 如果需要傳輸資料並且端點已配置) 隨之發生相關的資料傳輸。USB模組通過一個內部的16位寄存器實現埠與專用緩衝區的資料交換。在所有的資料傳輸完成後,如果需要,則根據傳輸的方向,發送或接收適當的握手分組。 在資料傳輸結束時,USB模組將觸發與端點相關的中斷,通過讀狀態寄存器和/ 或者利用不同的中斷處理常式,微控制器可以確定
● 哪個端點需要得到服務
● 產生如位元填充、格式、CRC、協議、缺失ACK、緩衝區溢位/ 緩衝區未滿等錯誤時,正在進行的是哪種類型的傳輸。
USB模組對同步傳輸和高輸送量的批量傳輸提供了特殊的雙緩衝區機制,在微控制器使用一個緩衝區的時候,該機制保證了USB外設總是可以使用另一個緩衝區。 在任何不需要使用USB模組的時候,通過寫控制寄存器總可以使USB模組置於低功耗模式(SUSPEND模式) 。在這種模式下,不產生任何靜態電流消耗,同時USB時鐘也會減慢或停止。通過對USB線上資料傳輸的檢測,可以在低功耗模式下喚醒USB模組。也可以將一特定的中斷輸入源直接連接到喚醒引腳上,以使系統能立即恢復正常的時鐘系統,並支援直接啟動或停止時鐘系統。
三、常式分析
  1.首先進行系統組態,如時鐘、USB、NAND FLASH、SD卡的初始化等
      2.配置中斷,本常式中使用了三個中斷通道
  USB低優先順序中斷(通道20):可由所有USB事件觸發(正確傳輸,USB復位等)。固件在處理中斷前應當首先確定中斷源。中斷處理函數為:USB_Istr ,進入中斷處理函數後會進行usb重定操作或者usb資料傳輸(調用CTR_LP函數)。
  USB高優先順序中斷(通道19):僅能由同步和雙緩衝批量傳輸的正確傳輸事件觸發,目的是保證最大的傳輸速率。它的中斷處理函數是CTR_HP。
  說明:函數CTR_HP和CTR_LP最終都會調用Mass_Storage_In(端點1)和Mass_Storage_Out(端點2)兩個函數來和PC端的USB HOST 通信
     3.SD的I/O 中斷(通道49) 
  為了讓SD I/O卡能夠中斷多媒體卡/SD 模組,在SD介面上有一個具有中斷功能的引腳——第8腳,在4 位元SD模式下這個腳是SDIO_D1,卡用它向多媒體卡/SD 模組提出中斷申請。對於每一個卡或卡內的功能,中斷功能是可選的。SD I/O的中斷是電平有效,即在被識別並得到多媒體卡/SD 模組的回應之前,中斷信號線必須保持有效電平( 低) ,在中斷過程結束後保持無效電平(高)。在多媒體卡/SD 模組服務了插斷要求後,通過一個I/O 寫操作,寫入適當的位到SD I/O卡的內部寄存器,即可清除中斷狀態位元。所有SD I/O卡的中斷輸出是低電平有效,多媒體卡/SD 模組在所有資料線(SDIO/D[3:0])上提供上拉電阻。多媒體卡/SD 模組在中斷階段對第8 腳(SDIO_D/IRQ)
採樣並進行中斷檢測,其它時間該信號線上的數值將被忽略。
  4.進入while迴圈,等待中斷的發生
      當USB主機檢測到USB設備插入後,進入中斷,先對設備進行重定,USB在復位後其位址為0,主機通過位址0和剛插入的設備通信,建立控制傳輸,將設備描述符發送給主機。重定的操作在MASS_Reset()函數中。
      主機對設備又一次重定,這時就進入了設置位址階段,這也是一個控制傳輸的過程,主機會分配一個唯一的位址給剛接入的設備。
      主機再次獲取設備描述符。
      主機獲取配置描述符,主機獲取字串描述符。
主機發送Get Max LUN請求,獲取最大邏輯單元。
主機通過批量端點發送CBW包,在CBW中定義了要操作的命令以及傳輸資料的方向和數量,返回該命令完成的狀態,即由設備返回CSW包,端點2用於輸出,端點1用於輸入資料至主機。CBW的標誌是0x55,0x53,0x42,0x43。
在USB主機和設備的通信過程中,資料會先被放到一個大小為512位元組的專用SRAM緩衝區裡面,然後再傳輸到主機或者USB設備。
      USB主機通過發送 SCSI Read10 Command和SCSI Write10 Command命令來對從機進行讀寫,分別調用的是SCSI_Read10_Cmd和SCSI_Write10_Cmd函數,最終調用MAL_Read和MAL_Write,在這兩個函數中判斷是對SD卡還是NANDFLASH進行讀寫。具體的過程可以參考代碼。
四、常式問題現象和解決方法
1.NAND FLASH盤可以被識別,格式化失敗。既然NAND FLASH盤可以被識別,那就表明USB的控制傳輸沒出現問題,USB批量傳輸出問題了,而且最可能的是NAND FLASH的驅動有問題,仔細看完代碼,發現在建立壞塊表,對Spare 區域進行讀操作中,對NAND FLASH 有一個NAND_CMD_AREA_TRUE1命令操作,查看代碼,
#define
NAND_CMD_AREA_TRUE1        ((u8)0x30)
 而30H這個命令在資料手冊中找不到,在程式中把這行注釋掉,編譯代碼,下載,還是無法格式化,最後研究了一下手冊,在注釋代碼的這一行加上5微秒的延遲,然後編譯,下載,NAND FLASH抽取式磁碟可以正常的讀寫了。
2.SD盤也能被識別,不能格式化。開始用的是2G的SD卡,後來嘗試1G和512M的,它們都能用,也就是說,這個SD卡驅動只識別1G和512M的SD卡。
在代碼中添加調試資訊,結果是寫入的塊長度出錯。通過對讀SD卡資訊瞭解到,2G的SD卡,塊大小是1024位元組,其它的卡都是512位元組。我在SDIO常式中設置塊大小為1024位元組,讀寫出錯,設置512位元組,居然是正確的。
USB MassStorage常式中是首先從SD卡中讀取SD卡塊大小的值,按照這個值來讀寫SD卡,而實際上2G的SD卡只能按照512位元組來讀寫。所以在讀取塊大小的值後,把塊的大小除以2,塊數目乘以2,卡的總容量不變,程式運行後,一切正常了。
USB Mass Storage常式下載位址:http://files.cnblogs.com/irwin0112/USB_MassStorage.zip
未經允許不得轉載:GoMCU » STM32 USB Mass Storage 例程調試筆記