?
USB固件編程可以用以下語句來精練地進(jìn)行描述:
Device的固件編程,要搞定的是那幾個端點。端點多少和配置情況受所用的Device芯片決定,具體可以看芯片資料。芯片一般提供一個中斷信號,與單片機(jī)接口時,只要端點接受到數(shù)據(jù),或發(fā)送數(shù)據(jù)成功后,便后產(chǎn)生中斷,在固件里面,只要對些中斷進(jìn)行響應(yīng)即可。當(dāng)Device接收到數(shù)據(jù)時,對這些數(shù)據(jù)進(jìn)行分析處理(端點0遵守標(biāo)準(zhǔn)的數(shù)據(jù)格式,其他端點受端點類型的不同,有不同的數(shù)據(jù)格式),一般來說,這些數(shù)據(jù)是主機(jī)對設(shè)備發(fā)出的請求,設(shè)備只要響應(yīng)主機(jī)的這些請求即可。Device芯片發(fā)送完數(shù)據(jù)后也會產(chǎn)生中斷,這個中斷信號告訴與之接口的單片機(jī),可以繼續(xù)發(fā)送后續(xù)的數(shù)據(jù)。USB固件,好比一個有“妻管炎”的男人,而主機(jī),則好是一個女管家。一般來說,主機(jī)讓干啥就干啥,所以,USB固件程序的結(jié)構(gòu)一般是基于中斷處理的。平時,主程序做完必要的初始化工作后,就什么事也不做了,等待USB中斷的產(chǎn)生,中斷產(chǎn)生后,根據(jù)中斷狀態(tài)對相應(yīng)的端點讀取數(shù)據(jù),或是向相應(yīng)的端點發(fā)送數(shù)據(jù)。這一點是USB通訊協(xié)議的主-從模式先天決定的。但讓人不可思議的是,這還有點象是母系氏族時期,因為,一個USB總線上,只能有一個主機(jī),可以有多個設(shè)備,整個USB總線上通訊的協(xié)議和處理,發(fā)起與中止,基本上是主機(jī)控制的。因此,固件編程中,只要滿足了主機(jī)的要求,就萬事大吉了,可以確保自己的氏族中的應(yīng)有地位
U盤固件編程之二:固件編程的幾個主要部分
在整個U盤固件中,程序從功能模塊上分成兩個部分:USB協(xié)議的處理和對Flash的讀寫.USB協(xié)議的處理又分成兩個方面.
一是端點0的配置過程.所有USB設(shè)備在插入USB端口時,主機(jī)都通過地址0與設(shè)備的端口0進(jìn)行通訊。在這個過程中,主機(jī)發(fā)出一系列得到描述符的請求,通過這些請求,主機(jī)得到所有感興趣的設(shè)備的描述符,從而知道設(shè)備的情況,然后通過Set Address為設(shè)備設(shè)置一個唯一的地址,配置過程完成以后,主機(jī)就通過為設(shè)備所設(shè)定的地址與設(shè)備通訊,而不再是使用默認(rèn)地址0.配置地址后,可能還要獲取一次描述符,然后設(shè)置配置(Set Configuration),之后便完成了對新插入USB總線的設(shè)備的配置過程。
二是其他端點的數(shù)據(jù)通訊過程。在配置階段中,主機(jī)已經(jīng)知道了設(shè)備的端點的使用情況,以后,便可以通過這些端點來進(jìn)行特定傳輸方式的通訊了。對于U盤來說,有兩種傳輸方式,BULK ONLY和CBI方式,一般使用Bulk Only較多。這種傳輸方式要使用特定的Bulk端點,然后還要為其選擇一種命令集。比如UFI或SCSI,因為Bulk端點的數(shù)據(jù)沒有特定的數(shù)據(jù)格式,因此,需要使用某種命令集,來約定所傳數(shù)據(jù)的格式。對于U盤固件編程來講,就是要處理BULK端點的各種數(shù)據(jù)通訊。除了對各個相關(guān)的端點的USB協(xié)議的處理,剩下的就是FLASH的讀寫問題。這里存在兩個層面的問題。
一是解決Flash讀寫的問題,就是說你使用的Flash,先要實現(xiàn)成功的讀寫和擦除,這部分內(nèi)容,是比較成熟的,一般都使用三星公司出的K9FXX08系列的,有16M(2808),32M(5608),64M(1208),138M(1G08)。它們的封裝一致,只需要軟件編程中稍做修改,便可以進(jìn)行適應(yīng)于另一種容量的存儲器。
第二個層面的問題,就是在U盤通訊過程中的問題了。NAND型的Flash有個特點,不可隨機(jī)存取,擦除操作一次對16K的內(nèi)容進(jìn)行。所以,在U盤響應(yīng)過程中,不可避免要對數(shù)據(jù)進(jìn)行緩存。如果你的U盤方案中有較寬裕的RAM(超過16K),這個問題變得簡單,只需要開一個16K的數(shù)組,把數(shù)據(jù)存到這16K中,最后再寫入Flash即可。否則,在緩沖上面是要花一些功夫的。最基本的思路是用Flash的另外一個Block做緩沖空間。但這種方式會引發(fā)下列問題:1、速度;2、Flash的那個用來做緩存的塊將比別的塊使用頻度大幅上升,磨損最嚴(yán)重,最先壞,這影響整個Flash的壽命。在實際處理中,引入了一系列的折中方案,比如,對Write命令所寫的Block號進(jìn)行判斷,如果是整個的數(shù)據(jù)Block,則直接擦除原有內(nèi)容,將數(shù)據(jù)寫入。再如,對于非整個Block的數(shù)據(jù)進(jìn)行緩沖,而對于整個Block的讀寫,不緩沖直接寫入。再就是對于前面文件分配表、目錄項所在的Block進(jìn)行緩沖,等等。經(jīng)過這些處理,可以盡可能地提高Write的速度。
U盤固件編程之三:合理的USB通訊調(diào)試方法和思路是成功的關(guān)鍵
在介紹更多細(xì)節(jié)內(nèi)容之前,我不得不談?wù)勎覍SB調(diào)試方法的理解。USB通訊過程是一個動態(tài)的過程,是不太好使用硬件仿真器來設(shè)斷點調(diào)試的,因為每一次USB的傳輸過程,都有時效要求,等待時間過長,通訊過程也就中止了。但也不排除可以巧妙地使用斷點仿真的方法進(jìn)行調(diào)試。但個人認(rèn)為,使用串口輔助編程過程,卻是一種經(jīng)濟(jì)有效的方法。所謂用串口輔助調(diào)試過程,也就是在固件代碼中加入類似于Printf的語句,向串口輸出一些信息。這些信息可以是幾個字符(如A、B、C),或是某個變量或寄存器的值。程序運(yùn)行到此處時,便會輸出這些信息,借此,便可以知道:1、程序是否運(yùn)行到此處;2、運(yùn)行到此處時相應(yīng)變量或寄存器值。這不就是硬件仿真調(diào)試的功能么?如果想使用這種方式來調(diào)試,在硬件上必須增加一個RS232串口電平轉(zhuǎn)換芯片,而且所使用的MCU得要有串口,并且,一般要自己編寫Printf函數(shù)的實現(xiàn)方式。這個翻翻串口控制方面的書籍,很容易就可以搞定。
串口調(diào)試的方法,還可以推廣到其他的單片機(jī)應(yīng)用中,在簡單系統(tǒng)中,它基本可以替代掉硬件仿真器,降低開發(fā)者的門檻和投資。在USB通訊過程中,有兩個階段,一是通過端點0完成對設(shè)備的配置,在此階段,把從USB端點得到的數(shù)據(jù)輸出到串口,就對通訊所處的階段一目了然了。一旦完成配置階段,Bus Hound便可以粉墨登場了,因為此時,Bus Hound中已經(jīng)可以看到設(shè)備了,看到設(shè)備后,便可以選擇設(shè)備,對主機(jī)與此設(shè)備間的通訊數(shù)據(jù)進(jìn)行分析和監(jiān)視。串口調(diào)試和Bus Hound這兩種手段配合使用,可以使USB通訊過程的調(diào)試更加容易。比如,剛開始時,端點0的數(shù)據(jù)量本來就少,因此,使用串口調(diào)試比較方便。而對于Bulk端點的數(shù)據(jù)傳送過程,再使用串口就不太方便了,因為數(shù)據(jù)量大,串口輸出的數(shù)據(jù)太多,延時會比較嚴(yán)重,影響USB通訊過程,所以改用BUS HOUND來監(jiān)視USB總線上的數(shù)據(jù)。這個時候很有趣的一件事情是,Bus Hound在PC機(jī)上,而串口實際上在單片機(jī)端。所以,利用這兩種手段,里應(yīng)外合,有助于我們確定一方發(fā)時另一方收的數(shù)據(jù)是否正確。比如,單片機(jī)上發(fā)出的一組數(shù),將其輸出到串口,然后看看Bus Hound上是否收到的是這些數(shù),如果正確,則說明硬件通訊過程沒有問題,如果不正確,則說明通訊的某一方有問題,進(jìn)一步可以定位此問題,排除之。正確的調(diào)試思路,將使調(diào)試過程事半功倍。比如,在調(diào)試端點0 的配置過程時,可以先用Switch...Case語句建立對于端點0的數(shù)據(jù)的分支響應(yīng),對照標(biāo)準(zhǔn)請求的數(shù)據(jù)格式,可以得到什么情況下是Get Device Descriptor,什么時候是Get Configuration Descriptor,每個分支處理時對應(yīng)一個函數(shù),在這個函數(shù)里向串口標(biāo)志信息。這個工作完成以后,便一個一個地來處理請求,處理完一個后,主機(jī)會自動進(jìn)入下一個階段,這時,通過串口可以看到相應(yīng)的狀態(tài),按步就班地一個一個處理余下的請求,即可完成端點0的設(shè)備配置過程的固件程序的編寫?!τ贐ulk端點也是一樣,先建立程序框架,然后再一個一個地處理請求。這種自上而下,逐步求精的思路并不稀奇,在USB調(diào)試過程中十分受用。最忌一上來就處理請求,不講求結(jié)構(gòu),不講求代碼的條理性,最后可能弄得自己都是一頭霧水
U盤固件編程之四:玩轉(zhuǎn)你的端點
接上面hewx,我來談?wù)劧它c的問題。前面提到過,端點是由USB設(shè)備端的接口芯片決定的。你選擇了什么樣的芯片,那么端點的配置情況屬性就已經(jīng)決定了,你只能使用將就這些特定的情況。這些端點的配置,具體要參考你所使用的接口芯片的芯片資料,比如說,端點0當(dāng)然都為控制端點,其MaxPacketSize可能為8,16,32,64;端點1可能是Bulk-In端點,2是Bulk-Out端點,其字長也有可能是8,16,32,64,但一般是64。其他端點可能有同步端點,或者同一端點既可被配置成同步傳輸方式,也可以工作在Bulk傳輸方式下,等等,不一而足。USB協(xié)議精妙之處就在于枚舉過程。主機(jī)最初發(fā)過來的包,一定是8個字符長的。所以,你的端點的MaxPacketSize至少必須是8,能滿足與主機(jī)之間最基本的通訊過程。對于主機(jī)的第一個請求Get Device Descriptor,你也只用回復(fù)8個字符就OK了,因為主機(jī)在第一次只對這8個字符感興趣,在后面逐漸的獲取描述符的過程中,主機(jī)逐漸得到設(shè)備使用那些端點,每個端點的最大字長(這些內(nèi)容在Endpoint Descriptor中,通過Configuration Descriptor提供)是多少,等等,總之,通過枚舉,主機(jī)便知道你的端點的情況了,以后就會用這些端點來與設(shè)備進(jìn)行通訊。對于Hewx的問題,我想是你在Endpoint Descriptor中沒有正確進(jìn)行端點的設(shè)置,因為,如果進(jìn)行了正確的端點配置,主機(jī)是會自動通過Bulk端點來發(fā)Inquiry命令的,而不會從你說的Endpoint1(16B)來發(fā)送這一信息。而且,主機(jī)會自動對要發(fā)送的信息進(jìn)行分割,每次以不高于相應(yīng)端點的MaxPacketSize長度來發(fā)送。除了描述符中要給出正確的端點描述符的描述,有些時候在芯片中也需要設(shè)備相應(yīng)的控制位,在決定你要使用哪些及如何使用這些端點,這個也得根據(jù)具體的芯片資料來設(shè)置。
U盤固件編程之四:玩轉(zhuǎn)你的端點(增補(bǔ))
對于某種設(shè)備來說,需要使用到的端點是固定的。比如說,Mass Storage設(shè)備吧,就只需要用到一個Bulk-In端點和一個Bulk-Out端點。而不需要幾個此類端點。至于到底需要幾個端點,完全需要根據(jù)有關(guān)協(xié)議中的說明進(jìn)行,描述符也據(jù)此進(jìn)行提供,而不是沒有根據(jù)地在描述符中提供許多端點。至于哪些端點可以做何種方式來使用,這也要看接口芯片的資料,比如,很可能,有的端點只能用作同步方式,那你就不要勉強(qiáng)將其用作批量方式,控制端點就只能用作控制傳輸方式,就不能為同步方式......搞清楚這個概念,在固件編程中才好正確地提供描述符。
評論