2019年10月25日 星期五

Hinet 網頁系列 --- USB DIY 系列(八)---USB DIY 講座 (六)

(補充說明:如果你問我:那以今日的角度,你還會不會像這一篇文章的做法?當然不會的

因為:第一現在要做這一種實驗,滿街一大堆學習板都可以做的,不用這麼辛苦的搞硬體。

第二、我當然也不會用這一種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);

到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


要我解釋或叫我教您這段程式,就真的污辱您的智慧了。
--------------------------------------------------------------------------------------------------

    喔~忘了介紹一下硬體,也是看圖:


上面那一塊是我隨便撿的一般版子,下面就是我的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);
            //---

而 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
....
;---------------------------------------------------------------
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 的動作,就可以幫我做出許許多多

的東西。說真的~技術的東西,不難,要作也只是花時間而已罷了~

重點應該還是您腦袋裡的東西,您想拿這個技術要做什麼?!才是最重要的
~~(待續)~~

沒有留言:

張貼留言