(補充說明:如果你問我:那以今日的角度,你還會不會像這一篇文章的做法?當然不會的
因為:第一現在要做這一種實驗,滿街一大堆學習板都可以做的,不用這麼辛苦的搞硬體。
第二、我當然也不會用這一種USB 控制IC 了。腳又多,又不好焊...神經病喔~除非真的有不可
承受的無奈,但我還是覺得應該機會很少。第三、我當然就用USB HID 方式做了,幹嘛還
在軟體搞這麼辛苦啊...最後,也是最重要的一點:搞這個做甚麼?吃飽太閒喔?以我USB 大師
的技術來說:搞這個能幹嘛?甚麼產品啊?要賣給誰啊?這問題遠遠都比眼前技術問題還重要
許多搞技術的工程師。總很喜歡追逐技術的新鮮感,但到最後往往都會被業務打槍,然後還會
怪業務不懂得欣賞你的才華?神經喔~你還真的以為"禮運大同篇"是存在這個世界上喔?
醒醒吧~這篇文章都已經有幾千年的歷史了,這也已經告訴你是不可能實現在人類史上的啦。
過了,就算了吧。現在年紀不小了。還是多珍惜一下剩下的人生歲月吧。哈~哈~)
>>>>>>>>>>>>>>>
我這樣子寫下去,會不會也像別人寫USB 書一樣?又要講基礎的東西?好了,別擔心,
I/O 的東西,我只要寫一個章節就好了,基本的東西會了,要做什麼?就讓各位自行發揮了,
直接進入重點吧。
------------------------------------------------------------------------------------------------------
首先,我們先體會一下使用的環境或特性,既然要作 I/O 就是說,我的程式就會經常
修修改改的,所以先看程式更新的機制吧。因為我的程式也是經由USB 下載到
USB Controller 的SRAM (您也可以說是Cache Memory ) 跑。
因為一般這種 IC的SRAM 都不大,所以,下載的路徑是不需要用到 Bulk Transfer 的,
只要Control pipe 就嚇嚇叫了。在此先參考一下USB DIY 講座(四) 裡的張架構圖。
所以,我就是利用那個外掛的 Boot EEPROM 來完成程式更新的機制,只要下載過一次,
我每個程式裡都含有更新Firmware 的機制,所以,我就可以反覆更新了:
看一下原始檔:
這種檔頭一看就知道是8051 的,不用我解釋吧。
再來看 USB 傳輸內容:
看到沒,我就是用 Control pipe 的 Vendor command (0x40)就可以一次傳 4K 的程式了。
當程式傳完後, USB controller 他自己會內部 Reset 8051。就可以完成程式更新的動作。
簡單容易,比ISP 還快。因為以USB的裝置來說,都是依附在HOST端(Such as PC)工作的,
而且,也都要經過 Enumeration 過程,所以,當完成Enumeration 這些工作後,再讓
應用程式進入 USB Controller 就可以了,當然,也有人直接把這些韌體直接寫在 Driver 裡面,
當Driver 安裝之時,韌體也可以完成下載的動作,這是看每個人的想法或作法了。。
當然,在PC端也要寫一個美美的程式,這樣子自己做起來也比較賞心悅目(看到左上角
那個小小的icon 嗎?沒錯,我是用VC++的MFC 寫的,而且我這個人就是『龜毛』,
明明像人家作的 CheckBox 的東西就好,我就是要偏偏作得像PCB版上的東西一樣。
或許很多人會覺得VB比較好用,就見仁見智了,只是C對我說:又可以寫PC端的程式
也可以用來寫韌體,年紀大了,實在是沒辦法轉來轉去的。):
就自己設計一個簡單的按鈕: 『Update FW』,來隨時更新韌體,我愛怎麼改就怎麼改,
還可以順便叫下載的程式把最新的 CheckSUM 回傳,以便確認更新無誤,當然,
也順便叫下載的韌體量一下USB 線上的電壓值:乖乖~竟然只有 4.74 Volts ,幸好還在
規格範圍內,這麼低的原因,是因為我是串接了 CATC 的USB 分析儀的關係,
USB Cable 就比較長囉。
就利用這張圖,說明一下我要DEMO 的東西 : 一般USB Controller 都會內建一棵 Controller
(Such as 8051),所以,複習一下USB 特性文章中的第九項:
特質九:USB Projects Require a Significant Code Development Effort
<<>> Device Side。
<<<>>> USB housekeeping firmware。這一部份我想您要寫得好,USB功力要一定的程度。
<<<>>> Application firmware。就是寫一般8051 ,要我講,就是污辱您了。
<<>> Host side。
<<<>>> Driver (maybe)。這一部份一般都用Microsoft DDK 寫。
<<<>>> Application software。這一部份我都是用Visual C++ 寫,因為跟底層好連接。
USB 裝置的程式分成PC端的程式及裝置端的程式,既然會有兩套程式,理所當然的,
我們也可以完成兩件事情:裝置端可以自己作的就叫裝置端內部的 Controller 作;
有些事情是可以請 PC 端的 Host 來控制。這是USB相對 RS232 或 Parallel 好的地方。
至於,USB Housekeeping 的程式我這裡沒有說明,因為一般USB Controller IC Provider
都已經幫您寫得很好了~不過~也很不幸的是~您都沒機會去體會到這一部份~
而瞭解這一部份卻是調整USB Performance 最重要的關鍵!這一部份當我們有用到時
我再說明。
而PC 端的程式我們就設計成 : 一個叫『SET Port』 由HOST 程式直接控制 USB Device 的I/O ,
所以他是按鈕;『USB Command』則是由PC 端的程式下命令叫 Device 端內部的程式自己跑
(我做的是叫 USB Controller他自己利用 8051 的 Timer 跑一個類似跑馬燈的東西),
然後我PC端的程式也可以同時去讀他 I/O 的狀態值,並把他 Show 螢幕上,所以,
他就是純顯示的燈號了囉。也就有所謂 RUN and STOP 兩個按鈕執行命令了。
先看 『SET Port』 部分:這部分就是最簡單的:把要設定的值,直接傳給 USB Device 端,
他直接把值Show I/O 上, PC 端程式:
int i;
for(i=0;i<8;i++) STICommandString[i]=0x00;
STICommandString[0]=0x7A; // SET USB Controller P1
STICommandString[1]=(UCHAR)(USB_P1&0x00FF); // SET USB Controller P1
USB_DEVICE_SEND_STI_COMMAND(STICommandString);
for(i=0;i<8;i++) STICommandString[i]=0x00;
STICommandString[0]=0x7A; // SET USB Controller P1
STICommandString[1]=(UCHAR)(USB_P1&0x00FF); // SET USB Controller P1
USB_DEVICE_SEND_STI_COMMAND(STICommandString);
到USB Bus 上:
看到沒? Command 0x7A + 0x02 的值。到這裡,您一定好奇問我,為什麼不用Setup Token
的 Vendor Command 把 0x7A 命令傳下去呢?因為反正您都得 OUT 一筆資料,
何必利用 Setup Token 那個資料空間呢(只有 8 Bytes 空間而已)?況且那個空間位置的
定義是被限制很多的,譬如 wLength 或wIndex 的;而在我們 OUT Token 中的資料是隨我
定義的。而且,您若將要執行的一些命令都寫在 Setup Token 中,除了備受限制外,
您的 USB Housekeeping 程式也很難寫,會囉哩囉唆,要模組化也很難。
(因為您會將 USB housekeeping 程式與Application 程式給混在一起!)
這樣子講不知您是否能體會得到?!因為在別的地方不用有人會教您這件事。
再看DEVICE 端的程式:
;---------------------------------------------------------------
USB3Cmd7A: ;; ;; Set P1 Port
cjne A,#7Ah,USB3Cmd7B
mov DPTR, #USBDsrr1
movx A, @DPTR //Read the P1 Request Value
mov P1, A
ret
USB3Cmd7A: ;; ;; Set P1 Port
cjne A,#7Ah,USB3Cmd7B
mov DPTR, #USBDsrr1
movx A, @DPTR //Read the P1 Request Value
mov P1, A
ret
要我解釋或叫我教您這段程式,就真的污辱您的智慧了。
--------------------------------------------------------------------------------------------------
喔~忘了介紹一下硬體,也是看圖:
上面那一塊是我隨便撿的一般版子,下面就是我的USB 控制版子,沒什麼IC,
就是我的USB Controller 大了一點。至於,上面的那兩條跳線,是我直接拉USB POWER
線分壓後,直接給USB Controller 量電壓用的。結合後成為無敵鐵金剛?(懶人施工法?!)
不要說我呼籠您,隨便寫個東西,就假設會動。我們還是眼見為憑:
上面那組就是『Set Port』,以紅燈表示。至於他的右邊的是 USB Controller 自己跑出來的
跑馬燈,以綠燈表示。您不要誤以為我一次點四個燈,那是我的數位相機爛~竟然拍到
視覺暫留現象,您看PCB 版上的燈就知道。不過,沒關係,您聽我說明即可:
因為一般 USB Controller 都會Built-in 一棵 MCU (Such as 8051) 所以,有些很緊急或要
real time 的東西,您就不要由HOST PC來控制,而直接請裡面那顆8051 自己來作就可以了。
而您只要下啟動命令即可,而此時,您HOST PC 就好像有點閒閒沒事幹,怎麼寫
『抓蝨母相咬』?我不會,只好請他一直去監控那組 GPIO 的狀態再把他SHOW在螢幕上了。
就成了這負德性。當然我一開始也是只寫個 Enable Timer Command~ 後來就把他變化一下~
把跑馬燈的速度參數也加進來,如此一來,還可以調整跑馬燈的速度:
先看PC程式:
int i;
for(i=0;i<8;i++) STICommandString[i]=0x00;
STICommandString[0]=0x7B; // Make 8051 Timer1 Enable
STICommandString[1]=0x08; // The Delay Counter of Flasher
USB_DEVICE_SEND_STI_COMMAND(STICommandString);
//---
for(i=0;i<8;i++) STICommandString[i]=0x00;
STICommandString[0]=0x7B; // Make 8051 Timer1 Enable
STICommandString[1]=0x08; // The Delay Counter of Flasher
USB_DEVICE_SEND_STI_COMMAND(STICommandString);
//---
而 Device 端呢:
;---------------------------------------------------------------
USB3Cmd7B: ;; ;; Set Enable Timer 1
cjne A,#7Bh,USB3Cmd8E
mov DPTR, #USBDsrr1
movx A, @DPTR
mov Tm1Base, A ;; Base of Timer1 Counter
mov Tm1Cnt, A ;; The Counter of Timer1
ret
USB3Cmd7B: ;; ;; Set Enable Timer 1
cjne A,#7Bh,USB3Cmd8E
mov DPTR, #USBDsrr1
movx A, @DPTR
mov Tm1Base, A ;; Base of Timer1 Counter
mov Tm1Cnt, A ;; The Counter of Timer1
ret
....
;---------------------------------------------------------------
STIstatus7B: ;; ;-- Enable Timer 1
cjne A,#7Bh,STIstatus7C ;; Chamber modify--1/19/2001
lcall Timer1Enable
ret
;----------------------------------------------------------
STIstatus7C: ;; ; inhibit Timer 1
cjne A,#7Ch,STIstatus7D ;; Chamber modify--1/19/2001
lcall Timer1Disable
clr A
ACC2IO GPIODR
ret
;---------------------------------------------------------------
STIstatus7B: ;; ;-- Enable Timer 1
cjne A,#7Bh,STIstatus7C ;; Chamber modify--1/19/2001
lcall Timer1Enable
ret
;----------------------------------------------------------
STIstatus7C: ;; ; inhibit Timer 1
cjne A,#7Ch,STIstatus7D ;; Chamber modify--1/19/2001
lcall Timer1Disable
clr A
ACC2IO GPIODR
ret
注意喔~這裡所謂的 0x7B 是分成兩部分喔~這裡有一個很重要的觀念在!原因沒他,
因為當USB 下命令及傳資料下來,您只能先收完命令或資料而已,您不能馬上作HOST PC
交辦的事情,否則您會耽誤USB Protocol 中的Handshake 的事。因為USB Protocol 的動作
還是需要您MCU去處理的。作USB 的最高指導原則:就是不能因為您應用程式的工作去
影響USB基本 Protocol 的東西 !所以大部分 USB Controller 裡的關於Protocol 的東西,
都是以中斷方式的原因。所以,雖然像去 Enable Timer1 這麼簡單的工作,我們還是等完成
回完 USB Status 之後才執行,養成這種好習慣,對您寫USB Controller 程式絕對是好事的,
而萬一您應用程式真的很忙,那您應該就把HOST PC 下來的命令先用 NAK 把他擋掉~
等到您事情告一段落之後再處理。或許您真的很難體會,但是這種技巧是作USB 不可或缺
的重要觀念!!
至於NAK 可以擋多久?哈~哈~您擋到明天或明年都不會有人說您錯了~
因為USB 規格就是這樣子規定的。有些寫軟體或驅動程式的人喜歡自作主張,
來個Time-Out ,那是不對的(更何況一旦USB 命令一下,一般上層應用程式只能等~
因為就算您想停,對不起,發 USB Protocol 也不是您的 Driver ,是微軟的作業系統!!
除非您拔 USB 線,否則沒解!!以前在 98SE 或所謂非 NT 作業系統還會來個 Blue Screen,
當場死給您看!!哈~哈~這些觀念一般書本都不會教您!!
好了,看我們的最後結果,不過真的很難表現跑馬燈的感覺,只好貼一張醜醜的照片~
大家就比照一下PC 螢幕上的綠色燈號與PCB 版子上的燈號吧~至少我是用數位相機連拍
功能的喔,跑馬燈是由兩邊到中間再向外延伸的霹靂燈方式!看圖自己想像一下吧!
不用管上面那三顆紅燈。~
---------------------------------------------------------------------------------------------------------------
好了,最後下一個簡單的結論,雖然我們只做了簡單的 I/O 控制,但我想這裡面已經涵蓋
許多USB 寫程式的技巧在裡面了,當然這些技巧都已經可以幫我們做出不錯的東西,
雖然一些書本都會附上類似的題目或範例程式,但您真的能夠從這些範例程式體會到
USB的精髓嗎?!像我上述的所謂的『先回USB Protocol 的東西,等回完 USB Status 的
命令再執行應用程式』,這種東西,沒有人教您該怎麼作~但是您會發現,
今天寫簡單的I/O 明明可以,為何我明天再作其他應用就不穩?!又沒有 CATC 看結果~
又要怪軟體的人,然後軟體的人又不知道您的應用程式在做什麼?!就每天在那邊吵?!
這樣子說,我相信是許多剛進入USB 領域的人都碰過!
好了~講完基礎的 I/O 控制後~如果要作像 LCD/LCM 或是什麼步進馬達的話,
就不用我多說了吧!再寫這些東西,我的網頁塞爆也寫不完。
而真正USB 的優勢也不侷限如此,他最厲害的是:大量資料的傳輸,
所以,接下來我們會直接以資料傳輸搭配一些 I/O 的動作,就可以幫我做出許許多多
的東西。說真的~技術的東西,不難,要作也只是花時間而已罷了~
重點應該還是您腦袋裡的東西,您想拿這個技術要做什麼?!才是最重要的。
~~(待續)~~
沒有留言:
張貼留言