前言
本項目是以SPI Flash(如W25Q128等)存儲元件作為存儲單元,MCU主控完成USB接口通信并根據(jù)SCSI協(xié)議實現(xiàn)U盤功能。其結構如下圖所示:
SPI Flash部分移植
SPI功能部分相對簡單,ACM32F403的接口引腳和STM32F103的相同,可直接對接,按照ACM32F403的說明對SPI接口進行初始化,并對底層讀寫函數(shù)進行更改即可。
USB部分移植
1. STM32F103代碼結構
在ST的芯片上,USB的數(shù)據(jù)是由兩個中斷,USB_LP_CAN1_RX0_IRQHandler和USB_HP_CAN1_TX_IRQHandler來進行,其中高優(yōu)先級中斷(USB_HP_CAN1_TX_IRQHandler)用于處理同步(Isochronous)模式傳輸或雙緩沖塊(Bulk)傳輸模式下的正確傳輸事件,而低優(yōu)先級中斷(USB_LP_CAN1_RX0_IRQHandler)用于處理其他傳輸時間。ST的USB數(shù)據(jù)處理如下圖所示:
由于USBFS協(xié)議的限制,一包數(shù)據(jù)中最多可攜帶64字節(jié)數(shù)據(jù),因此,當存在大量數(shù)據(jù)需要進行傳輸(IN或OUT包)時,需要分批次進行傳輸。在ST的代碼中,通過變量“Bot_State”來進行控制,以Read10指令為例,其讀數(shù)據(jù)流程可如下圖所示:
需要注意的是,Read10指令解析完成之后(即上圖左側流程圖)則進入數(shù)據(jù)傳輸階段,此階段是通過多次進入USB高優(yōu)先級中斷中,調用Read_Memory();來實現(xiàn)的。Read_Memory();函數(shù)內每次傳輸64字節(jié)數(shù)據(jù)。
2. ACM32F403代碼移植要點
本文基于上海航芯官方USB例程進行移植,移植后的程序結構如下圖所示:
ACM32F403的USB是采用一個中斷來進行數(shù)據(jù)處理。在官方例程中,USB的中斷函數(shù)內判定接收數(shù)據(jù)類型,包括suspend,resume,reset,EP0_pack以及其他端點的接收數(shù)據(jù)。判定結束后,會調用USB_Monitor();函數(shù)來處理suspend,resume,reset以及EP0_pack數(shù)據(jù)。而其他端點數(shù)據(jù)會在usb_transfer_monitor();函數(shù)中進行解析,該函數(shù)由客戶調用,一般在主函數(shù)的死循環(huán)中進行處理。在本文的移植中,主要需對USB的端點數(shù)據(jù)進行處理。
A. EP0_Pack
EP0接收的setup數(shù)據(jù)會被存放在SETIP_0_3_DATA和SETIP_4_7_DATA寄存器中 ,數(shù)據(jù)結構如下所示:
dev_req.bmRequestType=USBCTRL->SETIP_0_3_DATA &0xff;
dev_req.bRequest=(USBCTRL->SETIP_0_3_DATA>>8)&0xff;
dev_req.wValue=(USBCTRL->SETIP_0_3_DATA>>16)&0xffff;
dev_req.wIndex = USBCTRL->SETIP_4_7_DATA&0xffff;
dev_req.wLength=(USBCTRL->SETIP_4_7_DATA>>16)&0xffff;
該部分解析,可由用戶在函數(shù)void usb_control_transfer(void)中添加需要的處理函數(shù)。該函數(shù)由航芯官方例程里提供。在做U Disk程序移植時,需添加GetMaxLun和Storage_Reset處理函數(shù),如下圖所示:
B. EP1_Pack
在本文所述的代碼中,ACM32F403采用EP1完成數(shù)據(jù)的收發(fā)工作。主要是完成對SCSI協(xié)議的解析工作。移植過程中,需要文件mass_mal.c、memory.c、scsi_data.c、usb_scsi.c、usb_bot.c及其頭文件。本段主要就上述文件中代碼需要改動的地方進行說明,部分參數(shù)需要重新定義,讀者可自行解決。下表列出了ST和Aisino的USB收發(fā)功能函數(shù),該部分移植時需要修改的主要部分:
a. void Mass_Storage_In (void)
在ST的工程代碼中該部分主要用于處理SCSI的讀指令。由于全速USB一包數(shù)據(jù)最大支持64字節(jié),因此,當需要傳輸?shù)臄?shù)據(jù)個數(shù)大于該數(shù)值時,則需要分包傳輸。在使用ACM32F403時,可直接傳送需要的數(shù)據(jù)長度,內部會進行分包處理,因此,該函數(shù)可省略。
b. void Mass_Storage_Out (void)
該函數(shù)用于處理SCSI指令解析以及發(fā)送指令,需在usb_transfer_monitor()中調用,并將函數(shù)內部的接收數(shù)據(jù)部分更改為:
“Data_Len = HAL_FSUSB_Receive_Data(Bulk_Data_Buff, 64, out_ep_index, 1);”
c.void Transfer_Data_Request(uint8_t* Data_Pointer, uint16_t Data_Len)
將USB發(fā)送函數(shù)更改為ACM32F403對應的發(fā)送函數(shù)。在ST的工程中,該函數(shù)用于傳輸完數(shù)據(jù)后,進入BOT_DATA_IN_LAST狀態(tài),并在下一次的Mass_Storage_In()函數(shù)調用時,回復CSW指令。而本文的移植代碼中,省略了Mass_Storage_In()函數(shù),因此,可在該函數(shù)的尾部增加CSW發(fā)送指令:
Set_CSW (CSW_CMD_PASSED, SEND_CSW_ENABLE);
d.void Set_CSW (uint8_t CSW_Status, uint8_t Send_Permission)
將USB發(fā)送函數(shù)更改為ACM32F403對應的發(fā)送函數(shù)。
e.void Bot_Abort(uint8_t Direction)
該函數(shù)主要對收發(fā)端點的STALL狀態(tài)進行處理,在ACM32F403的收發(fā)庫函數(shù)中,對端點的STALL已做出相應控制,因此,該函數(shù)可省略。
f.void Read_Memory(uint8_t lun, uint32_t Memory_Offset, uint32_t Transfer_Length)
Read_Memory函數(shù)用于收到PC端的IN包請求后將存儲器中的數(shù)據(jù)讀取并發(fā)送至PC端。而ACM32F403的USB發(fā)送庫函數(shù)中,自行進行分包操作(一包最大數(shù)據(jù)為64字節(jié)),因此在數(shù)據(jù)緩沖區(qū)容量允許條件下,可直接發(fā)送完畢,該函數(shù)修改如下:
{
uint32_t Offset, Length;
Offset = Memory_Offset * Mass_Block_Size[lun];
Length = Transfer_Length * Mass_Block_Size[lun];
CSW.dDataResidue = CBW.dDataLength;
while(Transfer_Length --)
{
MAL_Read(lun ,
Offset ,
Data_Buffer,
Mass_Block_Size[lun]);
Length = min(Mass_Block_Size[lun], CSW.dDataResidue);
Offset += Mass_Block_Size[lun];
HAL_FSUSB_Send_Data((uint8_t *)(Data_Buffer), Length, in_ep_index);
CSW.dDataResidue -= Length;
}
Set_CSW (CSW_CMD_PASSED, SEND_CSW_ENABLE);
}
g.void Write_Memory (uint8_t lun, uint32_t Memory_Offset, uint32_t Transfer_Length)
寫數(shù)據(jù)指令完成后,將Bot_State 值更改為 BOT_IDLE。ST的工程代碼中,變量“Bot_State”收發(fā)狀態(tài)機的狀態(tài)值,其值如下表所示:
而基于ACM32F403的U Disk工程,IN包可由函數(shù)HAL_FSUSB_Send_Data()在其內部進行分包處理,不需要額外邏輯,因此,移植后Bot_State僅需要在BOT_IDLE、BOT_DATA_OUT、BOT_ERROR之間轉換,其他對Bot_State的控制可省略。
掃碼在線答疑
如需銷售咨詢,請聯(lián)系:sales@aisinochip.com