Keil是一間位於德國的公司,1999年站長的第一份正式工作,有一部分就是利用Keil公司所開發的C51 8051 C語言編譯器開發嵌入式程式,用於早期陰極射線管(CRT)螢幕內部的控制。這套軟體還包含了Keil自己的整合式開發環境(IDE, Integrated Development Environment) μVision,比起目前Open Source界常用的Eclipse擴充性當然是差了很多,但是以僅供8051開發而言,是一套相當便利的程式。

事過境遷,Keil目前已歸於ARM旗下,不過8051系列直至今天仍然是嵌入式系統應用的常用選擇之一,儘管競爭對手如Arduno與Resberry Pi之流來勢洶洶,8051也歷經了好幾個世代的進化,絲毫不顯老態,最早期的8051僅有4K的ROM以及128 byte的SRAM,若是想開發較大的程式,必須外掛ROM以及SRAM,不過這樣就限制了可用的I/O PIN數量,因為Port 0和 Port 2必須做為64k選址用,隨著製程進步,許多廠商把flash memory和更多的RAM整合進IC,也有一些新的8051核心改進了原8051 12個clock cycle才能執行一個指令的缺點,時脈也越來越快,在站長記憶所及,曾經用過turbo-51 核心,四個clock cycle可以執行一個指令,而站長目前超級測管機專案使用的Silicon Labs EFM8LB Laser Bee系列,為pipeline 設計,多數指令僅需一個clock cycle即可完成,時脈更高達72MHz,比起8051問世時,效能快了70倍!

早期的8051程式開發,主要是以組合語言為主,在1980年代,會寫8051組語的人基本上都被當成神人,隨著8051的效能進化,越來越多人使用較為平易近人的C語言做為開發工具,Intel有提供了一套C compiler給8051使用,不過業界最常用的8051 C compiler,則非Keil C51 complier莫屬,Keil C compiler提供了許多最佳化選項,編譯出來的binary code與Intel C compiler相容,當年一套要價不斐。典型的C語言為單一執行緒(Task thread),搭配中斷系統勉強可以切換前景和背景作業,這樣的流程已經可以達成許多功能,Microsoft的DOS系統就是以這樣的前景/背景系統完成的。不過,若想要更進一步玩多執行緒(multi-tasking),Keil也提供了兩種RTOS (Real Time OS)選擇,一種是RTX51 Full,一種是精簡版的RTX51 Tiny,下圖為兩種RTOS的功能比較:

 

來源:ARM Keil官網

從上表可以看到,RTX51 Tiny提供基本的多程序(task)平行執行的能力,不過不支援premptive multi-tasking,只支援round-robin,一些多工系統常見的功能例如旗標(semaphore)與mailbox也只有在Full版才提供。不過呢,Full版在2014年已經被ARM下架停售了,有錢也買不到,下架理由未知,網路上有陰模論者懷疑這是ARM要讓更多的使用者切換到更高級的ˊ微處理器(例如ARM Cortex系列),至於RTX51 Tiny版本目前升級成2.0版,附在Keil PK51Professional Developer's kit販賣,一些生產8051系列控制器的晶片商大都有免費提供這套Keil PK51 Professional Developer's kit,例如Silicon Labs的Simplicity Studio。

為了讓開發程式更輕鬆,站長決定在目前開發的8051專案裡使用RTOS,目前在小型開源RTOS的選項裡,最受歡迎的是uCOS-II與FreeRTOS這兩種,這兩種都有人移植到8051上,不過ˊ都有ˊ執行效率過差的ˊ問題,這是因為8051問世時SRAM給ˋ得太少,因此Intel捨棄了一般用堆疊(stack)傳遞參數的作法,採用Call-tree分析以及overlay的方式以節省空間,若要把上述兩種RTOS移植到8051上,所有的函數都必須宣告成可重複進入(reentrant),還有一個問題就是Keil C51編譯器僅支援古老的C89標準,很多新的功能例如naked function是不支援的,這樣在移植FreeRTOS必須要改寫很多地方,我看到的方式是採用其他的C compiler例如SDCC來避開這個問題,不過經站長詢問,Silicon Labs沒有計畫在Simplicity Studio支援SDCC開發工具,因此比較可行的方法還是使用RTX51 Tiny,畢竟是自家的東西,Keil在C51 complier提供了很多秘技給RTX51做連結。

 RTXˋ51 Tiny基本上只建議用戶修改Conf_Tny.A51這個檔案,Conf_Tny.A51分成兩部分,最前端的部分有一些Comment告訴用戶哪些是可更改的,ˋ預設的選項是使用Register Bank 1做Tasking Switching,(所有的Task都必須使用register bank 0,這是不能選的),因此在預設的情況下用戶只剩register bank 2和register bank 3給中斷服務程式使用。若是輕量使用者,需要更動的地方大概就是INT_CLOCK和TIMESHARING這兩個參數,INT_CLOCK是指定每多少個系統時脈產生一次Timer 0中斷,RTX51 Tiny在Timer 0中斷服務程式裡決定是否切換執行緒,設定這個要配合不同的8051晶片,例如ˊ站長使用的這顆Silicon Labs EFM8LB12系統時脈SYSCLK為72MHz,但是它能提供給Timer 0最快的時脈數為SYSCLK/4,忽略這個數字的話會讓中斷速度差四倍。TIMESHARING這個參數是指定每多少次Timer 0中斷才做一次round-robin多工切換,預設值是5。

 這個檔案最精彩的部分,是接下來用組合語言寫成的程式,包含了Timer 0的中斷服務程式,切換執行緒的os_switch_task,以及系統起始函數main,如果要更清楚的了解RTX51 Tiny的運作方式,詳讀這段程式是必要的。Keil C51 compiler 提供了一個特別的keyword _task_,若要宣告一個函數為獨立執行緒僅須在宣告函數時加上這個 keyword即可。若是沒有特別處理,在linking stage linker看到_task_這個keyword會主動去連結RTX51.LIB,這是RTX51 FULL的l程式庫(ibrary),用戶必須在命令列指定RTX51TINY,這樣linker才會正確連結到RTX51TNY.LIB這個ˊ程式庫。

RTX51 Tiny的運作方式,跟所有的RTOS大同小異,它利用每次Timer 0的中斷,先把一些timeout counter 減一,檢查timeout 事件並設定狀態,然後看看有哪些執行緒的狀態是就緒(ready),以及round-robin的counter,再決定離開中斷程式後要切換到哪一個執行緒。每一個執行緒需要兩個byte的TaskState,一個byte的StackPointer,以及兩個byte的EntryPoint,相當精簡。

 在實際應用中,站長深深體會到RTX Tiny的限制,對我來說,沒有premptive 和最基本的semaphore是太致命了,本來打算換到FreeRTOS,研究過一陣發現工程實在浩大,效率又太差,轉頭動到hack RTX51 Tiny腦筋,如果能把這兩樣功能加到RTX51 Tiny,不就同時兼顧牛肉的鮮(效率)和瀨尿蝦的甜(功能)嗎?經過一番奮鬥,站長已經成功的把semaphore功能放入RTX51 Tiny上,目前的功能嚴格來說是MUTEX (Mutual Exclusive),是最簡單的Semaphore,有八個bit可以單獨鎖定八個不同資源,例如printf,這樣多個執行序都可以呼叫printf而不致出錯,至於Premptive功能看起來也不太難實作,等有需要的時候再把它加進去。