引言
妻子將只有兩個破洞的襪子以及略沾草跡的襯衫都扔掉了,這沒什么。但當(dāng)她將目標(biāo)轉(zhuǎn)向那臺舊式點(diǎn)陣打印機(jī)時,我表示了抗議。她不屑地說:“這東西很久沒用了,而且也不能連接到任何電腦上?!本拖窨茖W(xué)怪人一樣,我不能容忍任何丟棄舊計算機(jī)設(shè)備的念頭。目的非常明確:使打印機(jī)變廢為寶,或者干脆將其丟棄。我決定通過網(wǎng)絡(luò)使其重新煥發(fā)生機(jī),幸運(yùn)地是用一個TINI (微型網(wǎng)絡(luò)接口)可完成這項工作。連接網(wǎng)絡(luò)
TINI是Maxim提供的嵌入式網(wǎng)絡(luò)平臺,它是基于該公司的DS80C390、DS80C400、DS80C410和DS80C411微控制器而構(gòu)建的。這些器件都是增強(qiáng)型8051控制器,具有24位地址、硬件網(wǎng)絡(luò)控制器、多個數(shù)據(jù)指針、專用硬件堆棧和高速工作模式等特性。TINI平臺支持TCP/IP網(wǎng)絡(luò)棧(IPv4和IPv6)、存儲器管理、進(jìn)程調(diào)度以及諸如I2C、SPI和CAN等通信協(xié)議。通過常見的編程接口,可用8051匯編語言、C或Java對TINI進(jìn)行編程。C runtime可提供一個Berkeley套接字(socket interface),Java運(yùn)行環(huán)境支持Java 1.1.8 API內(nèi)核。
TINI具有豐富的IO、簡單的網(wǎng)絡(luò)接口以及多種編程方法,是一套功能強(qiáng)大的協(xié)議轉(zhuǎn)換器。這正是挽救那臺舊式打印機(jī)所需要的:TINI提供網(wǎng)絡(luò)接口,我來決定如何讓TINI與打印機(jī)通信。
硬件配置
系統(tǒng)的核心部分采用TINI評估(EV)板。該評估板基于DS80C400微控制器,包括1MB閃存、1MB RAM,以及用于RS-232和以太網(wǎng)通信的連接器。雖然其存儲器配置與TINI Java運(yùn)行環(huán)境兼容,但是仍可采用C和匯編語言進(jìn)行編程。這給打印機(jī)接口原型設(shè)計和應(yīng)用實現(xiàn)提供了多種選擇。所討論的打印機(jī)型號為Epson LX-800。從碳定年檢測和厚厚的灰塵來看,它是鄰近真空管和呼拉圈年代的產(chǎn)物。LX-800是9針打印機(jī),這里的9針是指打印頭的針數(shù),而不是驅(qū)動打印機(jī)的并行信號個數(shù)。實際上驅(qū)動打印機(jī)需要17個信號(不包括地)。PC打印機(jī)并行接口的常用信號及其在25芯打印機(jī)連接器中的排列如圖1所示。
圖1. 并行打印機(jī)接口的25芯信號定義
采用傳統(tǒng)的25芯Centronics打印機(jī)電纜連接TINI與打印機(jī),首先需要與圖1所示的連接方式相匹配。然而,還存在一個問題:TINI板的IO不適合用作較寬的并行總線。僅有8個或9個通用IO (GPIO)信號,約為所需信號數(shù)的一半。這些GPIO信號分布于多個不同的寄存器中。由于需要對多個寄存器進(jìn)行寫操作來設(shè)置8個輸出位,這種GPIO信號的限制使得對打印機(jī)的8位數(shù)據(jù)總線進(jìn)行簡單的寫操作變得不太方便。幸好,TINI插座板為添加CPLD (復(fù)雜可編程邏輯器件)預(yù)留了焊盤和走線,可大大增強(qiáng)TINI的IO性能。一旦安裝了CPLD后,只需要一個用于編程CPLD的插頭(通過JTAG接口)和一個能訪問某些TINI引腳的插頭。
CPLD配置
CPLD可被看作是可編程硬件。CPLD具有非常靈活的內(nèi)部架構(gòu),能實現(xiàn)許多邏輯功能和狀態(tài)機(jī)。TINI插座板為100引腳VQFP封裝的XILINX? CoolRunner?-II XC2C64 CPLD預(yù)留了空間;也支持封裝和引腳配置相同的其它CoolRunner-II。本項目采用具有相同引腳配置的大容量器件XC2C128。CPLD通過DS80C400微控制器的外部存儲器總線連接至TINI系統(tǒng)。微控制器僅需讀/寫特定地址就能訪問CPLD:
read_from_cpld: mov dptr, #0C00000h ; CPLD address movx a, @dptr ; read from the deviceCPLD的任務(wù)較復(fù)雜。一方面,CPLD需要正確響應(yīng)TINI的外部存儲器總線信號:當(dāng)TINI寫CPLD時,CPLD必須讀取總線數(shù)值;當(dāng)TINI讀CPLD時,CPLD必須提供總線數(shù)值。另一方面,對于打印機(jī),CPLD需要讀取指示打印機(jī)工作情況的狀態(tài)信號(如BSY),并提供輸出信號(如8位數(shù)據(jù)總線和寫選通STR)以控制打印機(jī)。圖2所示為CPLD需要實現(xiàn)的功能框圖。
圖2. CPLD自身IO引腳和微控制器地址總線之間必須具有一個接口。
CPLD內(nèi)有四個8位寄存器,對應(yīng)CPLD的32個I/O引腳。寄存器位設(shè)為0時,對應(yīng)輸出引腳被驅(qū)動為低電平;寄存器位設(shè)為1時,輸出引腳懸空。因此,CPLD通過I/O引腳讀數(shù)據(jù)時,應(yīng)將對應(yīng)寄存器位設(shè)為1,這樣就不會將該引腳驅(qū)動至任何數(shù)值。
圖2中的所有輸入信號都來自于TINI的外部存儲器總線。當(dāng)TINI寫CPLD時,數(shù)據(jù)總線上是TINI將要寫入的8位數(shù)值。當(dāng)TINI進(jìn)行讀操作時,CPLD根據(jù)檢測到的輸入值來驅(qū)動數(shù)據(jù)總線。然而,由于其它器件可能共享總線,因此,CPLD不能在每次TINI請求讀操作(即每次nPSEN信號有效)時,都去驅(qū)動數(shù)據(jù)總線。如果CPLD總是響應(yīng)讀操作,則TINI會讀取錯誤的指令或數(shù)據(jù),并產(chǎn)生無法預(yù)料的系統(tǒng)操作(即“產(chǎn)生錯誤”)。同樣,并非所有寫操作都是針對CPLD的,因此,CPLD也不能總是響應(yīng)TINI的每一次寫操作(即每次nWr信號有效)。因此,圖2中的讀、寫信號是否有效都是由地址線決定的。
只要地址線AH0和AH1 (TINI的第16、17條地址線)為0,并且所選的芯片使能有效(本例中使用芯片使能6),則CPLD被激活。由于TINI的每個芯片使能對應(yīng)2MB空間,因此訪問CPLD的基址為0xC00000 (2,097,152 × 6 = 12,582,912 = 0xC00000)。需要注意,本設(shè)計中TINI和CPLD接口時,TINI的大多數(shù)地址線并未指定。例如,TINI的地址線A2至A15為任意值時,仍可激活CPLD。然而為了方便起見,源代碼始終采用地址0xC00000至0xC00003訪問四個8位寄存器。
用HDL (硬件描述語言)編寫CPLD程序。這里采用Xilinx WebPACK?開發(fā),用Verilog?硬件描述語言編寫,模塊定義如圖2所示,包括獨(dú)立的8位寄存器模塊、多路復(fù)用器以及譯碼器。在頂層模塊中實現(xiàn)模塊和地址譯碼。
CPLD安裝并編程完畢后,在與打印機(jī)連接之前,我還想檢查確認(rèn)具體用到了哪些信號。于是搭建了一個小型電路板,將4組LED與4個邏輯寄存器(IO7:0、IO15:8、IO23:16和IO31:24)相連,如圖3所示。用C編寫了一個小程序,使4組LED的數(shù)值增加,中間插入暫停,以便通過視覺驗證程序的操作過程。確信CPLD編程及連接正確后,可接入打印機(jī)。圖3所示為TINI板與25芯打印機(jī)連接器之間的飛線連接。
圖3. TINIs400插座板與Epson打印機(jī)連接
與打印機(jī)通信
最終目標(biāo)是:無需任何專門驅(qū)動或客戶應(yīng)用程序,就能使打印機(jī)正常工作。簡單地說,就是嘗試用內(nèi)置打印驅(qū)動并通過Windows?打印對話框,使舊式打印機(jī)發(fā)揮作用。本應(yīng)用的最后測試步驟是打印MS Word文檔;如果出現(xiàn)問題,Word會透露一些信息。盡管打印出MS Word文檔是最終目標(biāo),仍希望用更簡潔的方案來實現(xiàn)這一目標(biāo)。第一步,先嘗試簡單的應(yīng)用,在不聯(lián)網(wǎng)的情況下向打印機(jī)發(fā)送一些ASCII字符。這種方法只能起到調(diào)試作用。從理論上講,只要發(fā)送的不是指令編碼和其它非ASCII字符,打印機(jī)會將每個字符直接打印出來。希望這個項目能像所有好的工程項目一樣,能夠流暢快速地打印,從而能盡快確定網(wǎng)絡(luò)接口。實際上我完全錯了。
利用Keil Software? μVision? 2工具套件,并采用C語言編寫了第一個打印機(jī)應(yīng)用程序。根據(jù)Indispensable PC Hardware Book的說明,似乎需要對打印機(jī)進(jìn)行初始化,并開始寫字符1。寫字符的算法比較簡單:
- 等待BSY變?yōu)闊o效。
- 設(shè)置數(shù)據(jù)總線上的輸出值。
- 設(shè)置寫選通STR為有效。
- 等待ACK信號,確認(rèn)已收到數(shù)據(jù)。
- 設(shè)置寫選通STR為無效。
- 可根據(jù)需要進(jìn)行清除或重復(fù)操作。
012345678901234567890123456789...
將程序下載至TINI上并運(yùn)行。我立刻知道打印機(jī)初始化程序已正常工作了,因為在關(guān)機(jī)并再次開機(jī)時,能聽到打印頭移動的聲音。然而,幾秒鐘后沒打印出任何東西。將程序復(fù)位,仔細(xì)檢查連接是否正確。遺憾的是,沒有留出時間監(jiān)視打印機(jī)的狀態(tài)信號。一切都很正常,于是重新到計算機(jī)硬件指南中尋找答案。經(jīng)過一番研讀,辨別出某些信號的極性出現(xiàn)了混淆。例如,就傳統(tǒng)的PC接口而言,當(dāng)BSY為0時該信號有效,然而從硬件方面來說它實際上為高電平有效。反復(fù)修改C程序,并改變不同信號的極性,但打印機(jī)初始化后仍不能工作。又檢查了一遍程序,使邏輯電平與我想象的打印機(jī)信號電平相匹配。修改程序并運(yùn)行后,打印機(jī)依然沒有反應(yīng)。
離開片刻,聽到點(diǎn)陣打印機(jī)打印完一行文本時發(fā)出的刺耳聲音。近前一看,已打印出一行漂亮的字符,但隨后打印機(jī)又不動了。一個原理突然浮現(xiàn)在腦海中:無論是否達(dá)到80個字符的極限或是否將行結(jié)束序列插入數(shù)據(jù)流,打印機(jī)都是直到行末才進(jìn)行緩沖。幾次打印操作的工作過程證明了這一原理?,F(xiàn)在可以打印幾行文本了。
實現(xiàn)LPD監(jiān)控程序
第二天,決定將網(wǎng)絡(luò)與打印機(jī)接口連接。簡單說,就是創(chuàng)建一個用戶應(yīng)用程序,直接從網(wǎng)絡(luò)上接收數(shù)據(jù),并輸出給打印機(jī)。這種方法存在的問題是:若要打印機(jī)與MS Word一起工作,還需要編寫一個Windows打印機(jī)驅(qū)動程序,這一步工作可以實現(xiàn),但這并不是我的樂趣所在。編寫驅(qū)動程序這一做法老早就有,用不著再多此一舉,我開始考慮其它選擇,希望不用了解打印機(jī)驅(qū)動的全部細(xì)節(jié)就可實現(xiàn)目標(biāo)。理想設(shè)計應(yīng)該是一個虛擬并行端口。也就是說,虛擬并行端口提供與標(biāo)準(zhǔn)并行端口一樣的接口,但就驅(qū)動而言,實際上是通過網(wǎng)絡(luò)向TINI發(fā)送和接收數(shù)據(jù)。類似于這樣的虛擬端口和協(xié)議轉(zhuǎn)換器是TINI的一般應(yīng)用,按照這一設(shè)計思想,我曾有過一些實現(xiàn)虛擬串行端口的經(jīng)驗。似乎很多應(yīng)用都是直接寫PC的并行寄存器,因此很難確定如何實現(xiàn)這種設(shè)計思想。下一步就是查看現(xiàn)有協(xié)議。根據(jù)同事建議,查閱了RFC 1179,行式打印機(jī)監(jiān)控程序LPD)協(xié)議2。
LPD正好可以滿足需要。UNIX通常都支持這種網(wǎng)絡(luò)協(xié)議,但經(jīng)過簡單的配置后也可在Windows中使用該協(xié)議。安裝驅(qū)動時,通知Windows安裝與計算機(jī)相連的新本地打印機(jī),而不是通知它使用哪一個現(xiàn)有端口,這樣就創(chuàng)建了一個新的LPR端口。這里可以定義運(yùn)行LPD服務(wù)器的機(jī)器名稱(本例中為TINI的IP地址),并提供打印隊列的名稱。(這里只有一個名為‘crusty’的打印隊列,我想這個名字非常適合我的打印機(jī)。) 最后,告訴Windows打印機(jī)型號(筆者的打印機(jī)為Epson LX-800)。
這一設(shè)置具有一個好處,Windows打印機(jī)驅(qū)動能自動處理所有數(shù)據(jù)的格式,以采用打印機(jī)的原始語句(Epson ESC/P)進(jìn)行打印。這樣就不必了解ESC/P語言,也不必解析PC傳送給TINI的任何數(shù)據(jù)。此外,非常容易實現(xiàn)基本的LPD服務(wù)器;調(diào)用少量Java本地方法實現(xiàn)對存儲器映射CPLD的訪問。最后的實際代碼約有400行,準(zhǔn)備用于TINI的Java類文件僅有4kB。需要注意的是,我并沒有實現(xiàn)整個LPD協(xié)議,只是實現(xiàn)了每次打印一個文件所需的部分協(xié)議。
實施打印
最后再做3件事即可宣告設(shè)計圓滿完成:打印Notepad文件,打印MS Word文件,如果意猶未盡,還可以打印小的GIF或JPG文件。在對LPD服務(wù)器進(jìn)行一些調(diào)試之后,順利地打印出了Notepad文件。比較奇怪的是,打印2行文本約向TINI發(fā)送了5,000個字節(jié)。其實這是因為Windows驅(qū)動將整個文件轉(zhuǎn)換為ESC/P指令,從而發(fā)送一個位圖圖像,而不是簡單地發(fā)送ASCII字符。打印完Notepad文檔后,轉(zhuǎn)而打印MS Word文檔。打印MS Word文檔時出現(xiàn)了一點(diǎn)小問題。為使測試文件變得更有趣,每隔兩、三個字就變換不同的字體,這樣便有機(jī)會看到一些以前從未見過的字體。打印時,出現(xiàn)了許多無用的字符圖形:打印機(jī)會跳過幾行,隨即打印出一些奇怪的ASCII圖形字符(0x80至0xFF范圍內(nèi)的一些字符),并不斷發(fā)出嘟嘟聲。最后,終于打印出一些正確的文字,但隨后又是一堆亂碼。經(jīng)過多輪調(diào)試,并跟蹤發(fā)送至TINI的ESC/P指令,決定試著為轉(zhuǎn)義字符增加延遲。這樣就能正確地運(yùn)行打印程序,但是非常慢。經(jīng)過多次試驗后,發(fā)現(xiàn)只需在打印時碰到行結(jié)束字符時增加延遲即可。這種情況很少出現(xiàn),因此實際打印時感覺不到延遲。
由于打印MS Word文檔時碰到了一定的困難,因此一度斷定打印GIF文件幾乎是不可能的,但事實并非如此。打印時僅碰到一個問題:GIF文件轉(zhuǎn)換為ESC/P指令后尺寸大于64k,而64k正是所用TINI版本支持的最大動態(tài)緩沖區(qū)大小。然而,TINI支持無此限制的文件系統(tǒng),因此可以將接收的數(shù)據(jù)存儲在文件中,而不是存儲在動態(tài)存儲器緩沖區(qū)中。一旦改用上述方法,就可以順利地打印圖形文件了,并且僅受TINI中RAM大小的限制。
評論
查看更多