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