轉載自http://chamberplus.myweb.hinet.net/misc_1.htm 寫MP3 系統程式,因為牽涉許多DOS File system
的東西,用C寫的確是比較快一點,但卻也發現用高階C寫韌體的許多盲點。後來,看到公司內有一些部門在招一些寫韌體工程師時,竟也以資訊系為優先考量,結
果整個部門幾乎都是寫軟體出身的韌體發展部門?結果是什麼呢?開給IC設計部門的規格,所需求的記憶體空間就大到很難想像?8051 要寫到
1MBytes ?! 真是很誇張,對工程師來說:寫不出精緻的韌體,剩下的便是作苦工的寫程式。不知您是否可以體會到這點?!
什麼是精緻的韌體?我想您寫韌體在上機前,做了多少前置準備動作?還是,接到一個案子就批哩叭啦的從第一行往下寫?其實,寫韌體最難能可貴就是它是一份系統整合工作。除了程式語言以外,就是您對系統他所展現的專業知識。這話怎麼說?譬如說:當您寫完引擎控制程式後,您不只知道引擎控制韌體怎麼寫而已,還深深的體會到引擎機械系統的瞭解。我常常說:您作一行就是要入一行。所謂入一行,不是會寫寫基本程式而已,您還能體會到那一行的種種問題。這樣子,您才能出奇制勝,當然啊。當您要換工作時,您就會明明白白的知道您為什麼會離開原來的那一行? 好~我就舉一兩個例子來說明這件事情: 首先,您還記得我在單晶片與引擎控制 (二)--- 數學篇
中舉的例子嗎? 針對引擎的轉速,您是用一個integer 來宣告 呢?還是用
八位元的255 來表示呢?若您是寫軟體出身的,鐵定一定是用 Integer
來宣告,那因為在引擎控制系統中會用到許多這個變數,包括許多查表功能,那您查表公式是不是也是一路用integer 來運算呢?!結果當然對CPU執行效率或變數memory
需求一定不得了。我上述的那個問題就發生了。我列一張表格您可以看看:
剛接觸寫韌體的工程師,往往可能是以前是寫個人電腦的應用軟體的機會很多,或許,現在寫韌體的工程師越來越少呢?還是現在寫單晶片的系統工具比較好呢?所
以,許多寫韌體工程師所受的韌體程式訓練或機會不多,一上機寫韌體,就當作寫個人電腦的程式一般的寫法,一路往下寫。其實,我沒有說這樣子不行,但是,當
您看完我這篇文章之後,或許,您會有不同的看法?
我以前看或寫引擎控制程式都是組合語言,後來寫掃描器的韌體或是USB也都是組語。當然,以這些工作機會,是用不到 8051 的C組譯器(Keil C)。但那時,KEIL
C 也剛推出整合性高階語言的開發環境,我就自學把KEIL C學起來。後來很巧,我接了MP3 系統開發計畫時,就剛好派上用場,這件事讓我體會到一點:技能總是在工作中磨練,但若是未來您可能會接觸到的,您可能要提早自己自學,這樣子,等機會一來,您就比別人多一份機會了。就像投資您自己一樣,像這種屬於基本技能的東西,還是得好好的投資一下自己。
Engine Speed RPM |
Reference Period |
RPMX |
0 | 0 | |
200 | Tm0 | |
400 | Tm1 | 16 |
600 | Tm2 | |
800 | Tm3 | 32 |
1000 | Tm4 | |
1200 | Tm5 | 48 |
1400 | Tm6 | |
1600 | Tm7 | 64 |
1800 | Tm8 | |
2000 | Tm9 | 80 |
2400 | Tm10 | 96 |
2800 | Tm11 | 112 |
3200 | Tm12 | 128 |
3600 | Tm13 | 144 |
4000 | Tm14 | 160 |
4400 | Tm15 | 176 |
4800 | Tm16 | 192 |
5200 | Tm17 | 208 |
5600 | Tm18 | 224 |
6000 | Tm19 | 240 |
6400 | Tm20 | 255 |
以一具四缸四行程引擎來說:要跑到6400 轉,已經是不得了的事了,何況若超過
6400轉之後,其實已不需要查表作精準的控制了。那您從右邊的變數,您看到了什麼?就是對應到引擎轉速的八位元數值。而每一個 Byte 對應就是 25轉/byte
。對查表或控制來說,已經綽綽有餘了,但是對您寫韌體來說多方便啊。(另外,對於 RPM 的讀取信號,當然就是用單晶片的 timer
,這不用講大家都知道,但您把他轉成怎樣的數據呢?)再看一張圖表:
Load Units (N) |
MAP_LD |
TPS(%) |
0 | 20 | 0 |
8 | ||
16 | 25 | 6.25 |
24 | ||
32 | 30 | 12.50 |
40 | ||
48 | 35 | 18.75 |
56 | ||
64 | 40 | 25.00 |
72 | ||
80 | 45 | 31.25 |
88 | ||
96 | 50 | 37.50 |
112 | 55 | 43.75 |
128 | 60 | 50.00 |
144 | 65 | 56.25 |
160 | 70 | 62.50 |
176 | 75 | 68.75 |
192 | 80 | 75.00 |
208 | 85 | 81.25 |
224 | 90 | 87.50 |
240 | 95 | 93.75 |
256 | 100 | 100.00 |
引擎控制中,查表就是利用引擎轉速與引擎負載(一般有時會用油門位置(TPS)來表示)。TPS =
100%就是表示油門全開,此時對應的就是外面的大氣壓了。左邊就是我們程式要運算用的變數,您有沒有發現一件事:
引擎轉速的變數是粗量化,而油門負載變數是細量化;但是都是以 255為主要全量變數。
再來討論一個變數:點火進角。再來考考您,您怎麼宣告?!因為在引擎控制中,點火進角只會發生在上死點前後約九十度以內而已。但又需要達到小數點的精準度,您該不會用一個
Float 變數宣告吧!答案就看一個公式:
N = (Deg+11) *256/90 ; 這是一個全量變數的轉換,將您要的 90 範圍轉換成 255
的解析度。如此一來,您整個系統程式的數學系統,就完全統一在八位元的環境裡了。無論是查表或作內差公式(副程式),就變得很簡單了。更不用說您整個查表的表格就精簡多了:以下就是一個點火進角的查表表格:
400 PRM |
||
DB | 97 | (23 DEG) 20 KPA-MAP |
DB | 97 | (23 DEG) 30 |
DB | 97 | (23 DEG) 40 |
DB | 91 | (21 DEG) 50 |
DB | 85 | (19 DEG) 60 |
DB | 77 | (16 DEG) 70 |
DB | 65 | (12 DEG) 80 |
DB | 60 | (10 DEG) 90 |
DB | 57 | ( 9 DEG) 100 |
800RPM | ||
DB | 100 | (24 DEG) 20 KPA-MAP |
DB | 97 | (23 DEG) 30 |
DB | 94 | (22 DEG) 40 |
DB | 91 | (21 DEG) 50 |
DB | 85 | (19 DEG) 60 |
DB | 80 | (18 DEG) 70 |
DB | 74 | (15 DEG) 80 |
DB | 71 | (14 DEG) 90 |
DB | 71 | (14 DEG) 100 |
1200RPM | ||
DB | 108 | (27 DEG) 20 KPA-MAP |
DB | 105 | (26 DEG) 30 |
DB | 102 | (25 DEG) 40 |
DB | 100 | (24 DEG) 50 |
DB | 97 | (23 DEG) 60 |
DB | 94 | (22 DEG) 70 |
DB | 91 | (21 DEG) 80 |
DB | 85 | (19 DEG) 90 |
DB | 80 | (18 DEG) 100 |
1600RPM | ||
... | ... | |
... | ... |
經過這樣子的數學系統的轉換後,程式就精簡多了,而且當您的引擎上限增加時,您還是可以演用這樣子的數學系統。如此這樣子,您會跟我說:組合語言很難維護?我想是您的數學系統沒有模組化吧。其實我講的這些東西,是不是跟您以前在念工程數學時,那個無因次化的東西很像啊?!因為您的程式裡是看不到RPM
= 400 或油門開度 100% 這些物理數值,取代是的標準的八位元的數學系統,除非您是將 八位元換成
十六位元,否則,您整個系統程式,無論應用到哪一款車款或引擎都可以移植的。還有誰會跟我說組語是很難移植的?!
或許,您會跟我說:這是引擎控制特殊應用。那您就錯了。我後來利用這個觀念寫了一支掃描器的韌體。 這是我另一個例子:以彩色的RGB三條線來說:以 600DPI
來說需要 6ms * 3 = 18ms
!而以步進馬達控制來說:不同的DPI的掃瞄速度來說:需要不同定點的馬達控制換相,以達到掃瞄速度的調整。所以,我就將最大的掃瞄解析度 600 DPI 的 18
msec 訂為最大變量 : 255。不同的掃瞄解析度有著不同的馬達換相時間點。所以就可以得到這樣一個表格: _ColorPixelMotorControlTGEventTBL: ;;--- 8
Bit/Color/Pixel 中間的那個 0~255
數值就是對應我馬達控制換相發生的作用值。結果,這個計畫,我花了一個月作紙上作業的數學分析,再花兩個月寫程式,程式一
release無論是馬達加減速或掃瞄速度控制都沒問題。重點是:整個程式含USB
控制程式外加步進馬達加減速再加掃瞄色彩控制等我只用不到 8KBytes 程式寫完。還有,就是因應不同的客戶或 CCD/CIS 感應器,只要抽換
Table就可以了,程式本體邏輯控制都不需要作更改。您說組語很難維護?!我還得再質疑一次這種說法。PS :
這個計畫是一家國際大廠的一張給台灣OEM的單,工廠在大陸,對不起喔~我一次也沒跑大陸就把案子給結了。 結論:經由以上的例子,我想表達的是:當您接到一個案子,是寫系統的韌體時,您自己想一想,您花了多少時間在紙上作推演演算過,還是就直接上機一邊寫一邊想,您的前置分析工作做了多少?其實,這些工作都是學校教給您的,只是您畢了業就還給老師了。我也知道當上級長官把任務交付給您了。您也想很快的做出成果,但您有沒有想過,每次寫程式或韌體時,最累倒不是寫程式或韌體本身,而是調整整個系統穩定的問題,若您能多花一點時間先作一些數學或系統分析。我想事後的Debug
得時間會少很多。更何況 您用八位元的單晶片寫程式時,您覺得在八位元裡寫程式比較容易出錯呢?還是CALL 了一大堆副程式後,容易出錯呢?!
最後,我來舉一個很好笑的例子,看您自己會不會跟這例子的人很像?!我們知道 Keil C
會將您的高階語言組譯成組語,您會利用這個組譯的組語,把他再移植到您的組語環境來寫,以期望增加程式性能。甚至一些數學運算,您就直接把整段組譯程式移植到組語中。在我 單晶片與引擎控制 (二)--- 數學篇所舉的以3.14
圓周率的計算 :您是不是有點捨本逐末呢?!還在CALL 那整段除法副程式呢?(就算您整段移回來,用高階寫或組語都是一樣的哩)還是在紙上先利用數學分析
將*201/64 = *201+右移六次。求出常數再計算呢?! 您是寫韌體呢?還是在寫軟體呢?!
CP600: db #01000001B, 119, 118, 28, 30, 24, 2, 30, 90
;; 600 DPI
CP300: db #01000010B, 79, 78, 38, 40, 12, 4, 0, 20, 40, 60
;; 300 DPI
CP150: db #10000100B, 59, 58, 58, 60, 6, 4, 10, 25, 40, 55
;; 150 DPI
CP75: db #10000010B, 79, 78, 38, 40, 3, 8, 0, 10, 20, 30, 40, 50,
60, 70 ;;75 DPI
CP600K: db #01000001B, 119, 118, 28, 30, 24, 4, 10, 40, 70, 100 ;;9
;;---
_ColorLineMotorControlTGEventTBL: ;;--- 8 Bit/Color/Line
CL600:; db #01000001B, 119, 28, 28, 30, 4, 2, 30, 90
;; 600 DPI
CL300:; db #01000010B, 119, 38, 38, 40, 4, 4, 15, 45, 75, 105
;; 300 DPI
CL150:; db #00000010B, 119, 38, 38, 40, 3, 4, 15, 45, 75, 105
;; 150 DPI
CL75: ; db #00000010B, 119, 38, 38, 40, 3, 8, 0, 15, 30, 45, 60, 75, 90, 105 ;;
75 DPI
CL600K:; db #01000001B, 119, 28, 28, 30, 4, 4, 5, 35, 65, 95
;;---
_GrayMotorControlTGEventTBL: ;;--- 8 Bit/Gray/Line
GL600: db #01000100B, 59, 58, 58, 60, 8, 2, 15, 45
;; 600 DPI
GL300: db #11000010B, 39, 38, 38, 40, 20, 4, 5, 15, 25, 35
;; 300 DPI
GL150: db #10000010B, 39, 38, 38, 40, 8, 4, 5, 15, 25, 35
;; 150 DPI
GL75: db #10000010B, 79, 78, 38, 40, 8, 8, 0, 10, 20, 30, 40, 50, 60, 70 ;; 75
DPI
GL600K: db #01000100B, 59, 58, 58, 60, 8, 4, 5, 20, 35, 50
留言列表