Maxim USB庫是使用ARM7?處理器并基于MAX3421E/MAX3420E提供的軟件例程。本應用筆記對系統及其軟件運行進行了說明,在同一ARM? C語言代碼中同時實現了USB主機和外設功能。這種方法使得USB外圍設備和嵌入式主機的開發和研究,具有在USB電纜的另一端帶有參考設備的優勢,而所有的這些都包含在同一的C語言代碼中。
緒論
Maxim USB庫是兩塊電路板和C語言程序的組合。利用這個系統,可以:
- 研究、操作、測試和修改基于ARM7微控制器(μC)與MAX3420E USB外設控制器連接的USB功能 設備 。
- 研究、操作、測試和修改基于ARM7微控制器(μC)與MAX3421E USB主機控制器連接的USB功能 主機 。圖1顯示了主機從USB存儲棒中的數據檢索。
- 使用USB電纜將主機連接至外設,并同時運行主機和外設。開發USB主機或USB外設代碼時,USB電纜的另一端有一個參考設計是非常有用的。這樣,USB電纜的兩端,主機和外設因為都是執行相同的C語言代碼,從而具有可控性和定制性。
圖1. MAX3421主機/ARM從任意的USB外設重新得到枚舉數據,并通過串口連接至運行有終端仿真程序的PC來報告結果。
硬件
本應用C語言代碼運行在兩板裝置上:
- MAX3421評估(EV)板,第一塊電路板
- Keil MCB2130板,第二塊電路板
圖2. Maxim評估板插進Keil MCB2130板。
圖2展示了板級設備。藍色電路板是Keil MCB2130,其中包含一塊ARM7芯片,Philips? LPC2138。這個微控制器包含2個SPI?硬件單元,連接至兩塊Maxim USB控制芯片。
豎立的電路板是MAX3421EEVKIT-1。一個MAX3420E外設控制器連接至ARM的一個SPI端口,并布線連接至USB的“B”連接器(J5),在圖2中用“3420 P”標記(P代表外設)。MAX3421E主機/外設控制器連接至ARM的另外一個SPI端口,并布線連接至“3421P” (J2)和“3421H” (J1)連接器(H代表主機)。應用中運行的代碼將MAX3421E作為主機使用,因此評估套間電路板中部的USB連接器(J2)沒有使用。
主機軟件使用兩個串口中的一個(MCB2130電路板上的P1)將USB說明信息發送到運行終端仿真程序的PC。終端程序,如Tera終端程序,可以仿真終端(VT100),識別由程序發送的專用“轉移碼”序列以實現清屏和指針復位。終端程序的串口設置為38400、N、8、1,無流量控制。
圖2中的米色方盒和帶狀電纜是Keil ULINK JTAG裝載-調試器。Keil μVision?3開發環境支持該調試單元。MCB2130附帶一個評估版μVision3,具有完整功能版本的Keil工具集。但評估版在代碼容量上有16kB的限制。
本應用筆記的附件包含整個Keil工程的源文件,另還有.hex格式的裝載模塊。如有ULINK JTAG單元,可編譯該代碼,并可通過JTAG端口進行加載和調試。這對于快速開發USB、修改工作主機和/或外設代碼使其適合開發目的是一條極好的途徑。如果沒有ULINK,可使用稱為Flash Magic的免費程序加載、運行hex文件,這個程序可從網站www.esacademy.com獲得。參考Maxim應用筆記3937可獲得如何配置和使用該程序的信息。
圖3. MAX3421EEVKIT-1電路板框圖。帶陰影的橢圓形是主要軟件模塊。
程序功能
圖3顯示了MAX3421EEVKIT-1電路框圖和本應用筆記的軟件模塊。MAX3420E和MAX3421E都使用SPI總線訪問其寄存器組。LPC2138含有2個硬件SPI單元:
- 一個專用SPI端口(SPI)
- 一個通用串行接口(SSP,同步串行端口)
可對SSP編程以實現第二個SPI端口。這些端口的編程不同。例如,SSP有8個發送、接收FIFO,而SPI只有1個讀緩存器。為與MAX3420E和MAX3421E兼容,兩個SPI接口都編程工作在8位數據模式下。軟件模塊SPI_FNs.C包含兩個USB控制器都使用的寄存器訪問函數。
MAX3420E屬于SPI單元,所以對這塊芯片來說函數有一個“P” (例如Prreg和Preg)前綴,表明外設操作。MAX3421E屬于SSP單元,所以其訪問函數有一個“H”前綴(例如Hwreg),表明為主機操作。
外設程序
C模塊3420_HIDKB.C實現一個通過使用評估電路的連接器J5連接至PC機的USB外設。MAX3420E的INT輸出引腳連接至ARM7的EINT0 (外部中斷0)引腳。這個中斷可在由MAX3420E實現的USB外設需要服務時隨時觸發。
本應用列舉和實現了標準的USD HID (人性化接口設備)。符合標準Windows?平臺的一大優勢是設備驅動內置在Windows中,從而不需要安裝專用的驅動。Windows將由本應用實現的設備識別為一個標準鍵盤。按任意的連接至MAX3420E GP-IN引腳的四個按鈕,將使“鍵盤”在任意打開的接收文本窗中輸入文本字符串,如記事本和寫字板。
注意 :本應用可在任何可接收文本應用中輸入文本,如電子郵件、匯編編輯器、或Word文檔。在按鍵之前請確認有一個安全的應用,如記事本被打開,并處于有效狀態。作者根據經驗可以確定:在一個打開的C源文件中輸入無效文本將不能進行完全編譯。---
主機代碼
3241_Host_EVK.C文件包含主機代碼,其使MAX3421E完成一定的枚舉步驟,該枚舉步驟與有USB設備插入連接器J4時PC所執行的枚舉步驟相似。這個模塊中的 main() 函數就像HID鍵盤代碼的中斷服務程序。EINT0中斷服務程序簡單如下:
// EINT0 Interrupt handler--MAX3420E INT pin
void INT3420 (void) __irq
{
service_irqs(); // Do the USB thing in 3420_HIDKB_INT_EVK.C module
EXTINT = 1; // Clear EINT0 interrupt flag (b0)
VICVectAddr = 0; // Dummy write to indicate end of interrupt service
}
基礎程序 main( ) 包含以下無限循環子程序:
while(1)
{
detect_device();
waitframes(200); // Some devices require this
enumerate_device();
wait_for_disconnect();
}
Enumerate_device() 函數完成絕大部分的工作,向連接設備發送USB請求,并通過串口報告結果。任意的USB設備插入評估板的連接器J1,這個函數將向設備發送枚舉請求,并通過串口報告結果(圖1)。
同步主機/外設操作
因為MAX3421EEVKIT-1電路板包含一個USB外設和一個USB主機,且它們使用ARM7上單獨的SPI接口,所以軟件將可以很容易的編寫,使主機和外設應用同時的運行。主機應用運行 main() 在后臺與MAX3421E對話,而HID外設代碼使用MAX3420E中斷激活 service_irqs() 函數。
如果用USB電纜連接評估板的連接器J5和J1,ARM處理器可作為USB主機與外設對話,而這個外設就是其自身。圖4顯示了3421_Host.C的主機代碼詢問由3420_HIDKB.C實現外設的報告。
圖4. 通過USB詢問自身的代碼演示
主機代碼開發
通讀 enumerate_device() 函數有助于理解USB主機是如何工作的,也有助于理解如何命令MAX3421E完成枚舉USB設備時的各種主機操作。而開發主機代碼,如果對外設插入主機的響應過程不清楚,有時將很難診斷問題。如果在全部的控制中(3420_HIDKB.C模塊)擁有參考外設,將使問題很容易發現,并可改變設備的響應以測試主機紉件。
外設代碼開發
主機是用于開發USB外設代碼的優秀詢問工具。MAX3421E和3421_Host.C模塊的結合,產生一個簡單但功能強大的USB信息包生成器和分析器。通過編寫C語言主機代碼,可以控制外設狀態;MAX3421E顯示外設的響應過程。
實例
假設編寫外設代碼以處理USB字符串。在hidkb_enum_tables.h文件中按如下方式編寫生產商字符串XYZ Widget Company:
// STRING descriptor 1--Manufacturer ID
{
18, // bLength
0x03, // bDescriptorType = string
'X','Y','Z',' ','W','i','d','g','e','t',' ','C','o','m','p','a','n','y'
},
編譯該文件,將其裝入ARM的flash存儲器,并運行。主機程序將報告如下生產商字符串:
圖5. 不良結果,例程沒有按照要求顯示生產商名字,代碼有問題。
出現了什么問題? 原來,USB規定文本字符串用Unicode表示,即每個字符用兩個字節表示。回顧外設的源代碼,更改如下:
// STRING descriptor 1--Manufacturer ID
{
35 // bLength
0x03, // bDescriptorType = string
'X',0,'Y',0,'Z',0,' ',0,'W',0,'i',0,'d',0,'g',0,'e',0,'t',0,
' ',0,'C',0,'o',0,'m',0,'p',0,'a',0,'n',0,'y' // Unicode!
},
編譯、運行,應該可以看到:
圖6. 正常的代碼例程,使用Unicode格式,可看到結果。
該結果有所改善,但并不完美。Company中的“y”到哪去了? 仔細計數該字符串有35個字符,并將其作為第一個字節輸入以顯示字符串長度。設計沒有注意的是:長度字節也是字符串,必須包含前兩個字節。代碼最終的修改如下:
// STRING descriptor 1--Manufacturer ID
{
37 // bLength
0x03, // bDescriptorType = string
'X',0,'Y',0,'Z',0,' ',0,'W',0,'i',0,'d',0,'g',0,'e',0,'t',0,
' ',0,'C',0,'o',0,'m',0,'p',0,'a',0,'n',0,'y' // Unicode!
},
這是結果應為如下所示:
圖7. 最佳的代碼實例。代碼現在可實現設計想要實現的功能。
通過測試外設代碼和使用主機控制器及編碼運行/分析,由于報告的結果是由MAX3420E發送至MAX3421E的實際USB報文,從而可以看到與PC機響應完全相同的結果。最好的是,這些都可在一個可控的基礎上實現,因而不必擔心PC機的如何響應而使代碼開發出現錯誤。
代碼說明
圖8. Keil工程的結構
圖8顯示了三個主要模塊,并展開顯示了它們的文件依靠。沒有展開的模塊是Keli開發環境所需要的內部處理文件。
SPI_FNs.C
這個模塊包含由其它兩個模塊調用的公用函數。
void init_PLL(void)
此函數設定LPC2138的PLL和時鐘除法器。附著于LPC2138 (F OSC )的晶體為12.000MHz。根據公式CCLK = FOSC × M,設定CPU頻率為48MHz,其中M = 4。LPC2138使用內部CCO (電流控制振蕩器)倍頻FOSC以獲得更高的內部頻率。FCCO = CCLK × 2 × P。FCCO必須處于156MHz和320MHz之間。對于P = 2,FCCO = 48MHz × 2 × 2 = 192MHz。
注意 :Keil文件startup.s也包含設置PLL參數的啟動代碼。 init_PLL() 函數將有效的取代這些設置。---
重要警告 :修改這些設定會有一定的風險。錯誤的設定能迫使LPC2138超出其規定的限度,從而使Keil調試器停止作用。如果這種情況發生,補救方法是使用Flash Magic公用軟件擦除LPC2138的flash存儲器(使用第二個串口,連接至Keil電路板的P2),修改代碼中的PLL設定,然后再試驗。---
init_IO
這個函數初始化LPC2138的I/O引腳和SPI單元(SPI和SSP):
- SPI由CCLK/8時鐘控制,允許最大值。SCLK為48MHz/8 = 6MHz。
- SSP由CCLK/(2 × VBDIV)時鐘控制。VBDIV設置為1,使MAX3421E的SCK信號為48MHz/2 = 24MHz。MAX3421E的SCK輸入最高可運行至26MHz。
關于SPI接口
SPI接口使用如下信號:* MOSI 主機輸出數據,從機輸入數據
- MISO 主機輸入數據,從機輸出數據
- SCLK 串口時鐘,由LPC2138提供
- SS# 從機選擇,由LPC2138驅動
LPC2138的SPI硬件控制前三個信號,但SS#使用一個GP-OUT引腳(P0.7)直接設置和清除。
關于SSP接口
雖然SSP有一個硬件SS#引腳,但使用GP-OUT引腳作SS# (P0.20)更簡單。
**Hwreg
Hwritebytes**
這些函數使用LPC2138的SSP硬件向MAX3421E寫入寄存器和FIFO數據。H前綴表示主機操作。Hwreg寫入一個信號寄存器值;Hwritebites向MAX3421E的FIFO寫入多個字節。
**Hrreg
Hreadbytes**
這些函數使用LPC2138的SSP硬件從MAX3421E中讀出寄存器和FIFO數據。
SSP接口
SSP硬件需要譯碼,從而在發送和接收通道上需要數據FIFO。在一個SPI操作中,每8位數據移出總是伴隨著8位數據移 入 。LPC2138使用如下步驟訪問MAX3421E的SPI接口:
- 觸發SS# (低)
- 發送由登記號和方向位構成的命令字節
- 發送/接收一個或多個數據字節
- 釋放SS# (高)
SPI寫操作簡單明了—每個數據都會移出,輸入FIFO內的數據可以被忽略。但讀操作更加復雜,因為讀FIFO至少包含一個壞字節,即步驟2中自動由時鐘控制記入的。由于以前的多字節寫操作也可以使輸入數據失時效。輸入FIFO不能由硬件禁止或清除。因此,讀SPI數據的代碼首先要手動清除FIFO,即從SSPDR中讀出字節,直至表示“讀FIFO不為空”的標志位被釋放。
**Pwreg
PwregAS
Pwritebytes**
這些函數使用LPC2138的SPI硬件向MAX3420E寫入寄存器和FIFO數據。第二個函數與第一個函數一樣寫入寄存器值,但其還設置SPI命令字節中的ACKSTAT位。這是停止USB控制轉移的捷徑。細節請參考應用筆記3598,"MAX3420E編程指南"。
**Prreg
PrregAS
Preadbytes**
這些函數使用LPC2138的SPI硬件從MAX3420E中讀出寄存器和FIFO數據。第二個函數與第一個函數一樣讀出寄存器值,但還設置SPI命令中的ACKSTAT位。
readout
readout這個函數更新連接至MAX3421E通用輸出引腳GPO[6:0]的7段信息顯示,函數保留GPO[7]的設置,GPO[7]控制USB “A”連接器(評估板的J1)的VBUS開關。
3421_Host.C
這個模塊實現MAX3421E USB主機,該主機訪問USB設備并通過MCB22310的串口報告枚舉數據。作為參考,附錄A給出了主機枚舉由3420_HIDKB.C實現的內置設備的LeCroy (CATC)總線過程。
這個模塊中有三種函數類型:
- 初始化函數
- 公用函數
- main() 中調用的高級函數
下面的說明將按此順序。
Reset_Host
MAX3421E包含上電復位,因此這個操作并不是必須的。盡管如此,程序開發階段使用該字段,不需要重復上電即可初始化調試過程,是復位功能的很好方式。啟動該函數使器件清零,不會帶有以前的調試信息。
復位MAX3421E使片上振蕩器停止工作。解除復位后,這個函數在返回之前將等待OSCOKIRQ位(振蕩器正常中斷請求)變為有效。
initialize_3420
這個函數在3420_HIDKB.C模塊中。
initialize_ARM_Interrupts
這個函數建立ARM向量中斷如下:
- EINT0連接至MAX3420E的INT引腳,使用優先級0 (最高)。
- Timer0用于功能發光體閃爍,使用優先級1。
- EINT2連接至MAX3421E的INT引腳,指定優先級為2,在這個程序中沒有使用到該中斷。當然,這個中斷是為了方便才提供的。
- Timer1用于檢測發送/停止按鈕(在3420_HIDKB.C中使用),使用優先級3。
service_irqs
該函數在3420_HIDKB.C模塊中。它進行HID鍵盤仿真。初始化ARM中斷的代碼僅需將該地址置為一個中斷向量。
waitframes
用于USB主機測量時間的一個簡單方法是計算1ms幀標志的個數。當微控制器設定寄存器位SOFKANAB = 1時,MAX3421E自動產生這些由全速模式的SOF數據包或低速模式的“保持激活”脈沖組成的幀標志,隨后FRAMIRQ位每毫秒觸發一次。
USB 規定要求確定的長延遲時間,例如復位一個設備后給它一個“復位恢復時間”。調用waitframes函數將是實現相對長的延遲時間的一個簡便方法。
detect_device
這個函數在檢測到一個USB設備插入USB-A連接器J1后返回。
wait_for_disconnect
該函數在檢測到一個插入USB-A連接器的設備斷開后返回。
Send_Packet
所有發送USB數據包的函數都調用這個函數。圖9顯示了IN事務處理的總線過程和實現它的C程序。
圖9. 啟動USB總線動作,由MAX3421E調用C程序函數
- 微控制器在FNADDR寄存器中設定函數地址。因為在微控制器重新載入FNADDR寄存器之前,裝載值一直保持不變,所以每個數據包不需重復這個步驟。
- 微控制器通過寫入HXFR寄存器開始傳輸數據,指明USB令牌和終端。然后等待HXFRDNIRQ (主機傳輸完成)觸發IRQ,發送信號完成。
- 微控制器讀HRSL (主機計算結果)寄存器來決定事務處理的結果。16個計算結果代碼表示下列結果:
A.成功(ACK)
B.設備占用(NAK)
C.非正常握手(STALL)
D.設備故障,如超時、串音或總線錯誤
- 微控制器讀計數寄存器字節,然后卸載RCVFIFO并將數據存入一個字節數組中。
這個函數處理NAK重試,連續重新啟動數據傳輸直到:
- 收到ACK握手,或
- 超過常數NAK_LIMIT設定的預設限制
如果設備無反應,函數重試傳輸,直至受到RETRY_LIMIT限制。
CTL_Write_ND
這個函數以無數據模式執行控制-寫操作( _ND = no data )。這個函數僅在主機Set_Address請求時調用。它可以實現Sent_Packet的兩個調用:第一個用于SETUP數據包,第二用于IN-Handshake數據包。
IN_Transfer
這個函數盡可能多地調用Send_Packet (指定的IN),以重新獲得USB的數據記錄。MAX3420E和MAX3421E終端FIFOS為64字節長,如此長的字符描述,舉例來說,需要多個IN請求數據包組成一個大數組(XfrData[2000])。
CTL_Read
這個函數處理3態CONTROL-Read USB事務,調用三個函數:
- 調用Send_Packet發送SETUP數據包
- 調用IN_Transfer使數據進入XfrDaTa[]中
- 調用Send_Packet發送OUT-Handshake數據包
enumerate_device
這個函數執行附錄A所示的10個數據傳輸。因為它調用以前的函數來實現低級USB操作,所以通過研究這個函數可很容易理解枚舉步驟。
print_error
這個函數檢查4位HRSL值(圖9中的第3項)以打印輸出主機的傳輸結果。如果通過數據為0,不打印輸出。這個函數返回HRSL值,因此調用這個函數可以很容易地檢測錯誤,例如:
if(print_error(HR)) return;
3420_HIDKB.C
這個模塊使用MAX3420E實現一個USB外設,符合標準HID類,可以象標準鍵盤輸入一樣自動輸入文本。入口點是service_irqs函數,該函數在響應LPC2138的EINT1引腳時作為中斷服務程序調用。ENT1引腳連接至MAX3420E的INT輸出引腳。
Reset_Peripheral
這個函數以與Reset_Host相同的方式復位MAX3420E。
initialize_3420
這個函數完成三件事:
- 配置MAX3420E SPI接口以實現全雙工操作(分離MOSI和MISO引腳)
- 配置MAX3420E INT引腳為上升沿有效
- 通過應用程序使能MAX3420E中斷
service_irqs
這個函數直接處理總線復位和設備斷開。這個函數也調用do_SETUPC處理枚舉和do_IN3處理鍵盤輸入。
注意 :為精簡代碼,本應用不處理USB的掛起-恢復任務。實現該功能的例子可參考Maxim應用筆記3690,"MAX3420E的USB杖舉程序(及其他)"。AN3690中的枚舉代碼作為3420_HIDKB.C的基礎。---
do_SETUP
這個函數通過檢查8字節SETUP數據包中的各個字節處理設備枚舉,然后調用函數以處理特定的請求。代碼僅處理USB標準請求類,因為本應用程序中沒有實現USB分類或客戶請求。
do_IN3
這個函數根據HIDKB_enum_tables.h中的Message[]字符數組發出按鍵信息。每字符三個字節的格式定義在與HID Report Descriptor相同的文件中,如圖10所示。
圖10. Report Descriptor詳述了一個鍵盤按鍵的3字節數據格式
最終考慮和免責聲明
插入J1的USB設備可能是十億個左右可用設備的任意一個,因此不必說,將有各種各樣的響應滿足主機的枚舉請求。任意一個通過兼容性測試的設備(有USB標識顯示證明)都將在枚舉時顯示無誤。本程序中的故障檢測由每次主機事務處理后對HRSL值的檢查完成。因為許多故障條件難以模擬,所以故障檢查沒有進行廣泛的測試。
-
處理器
+關注
關注
68文章
19291瀏覽量
229906 -
usb
+關注
關注
60文章
7947瀏覽量
264742 -
C語言
+關注
關注
180文章
7605瀏覽量
136887
發布評論請先 登錄
相關推薦
評論