Nodebot – Johnny Five servo clock (part 1)

繼上篇文章所述,雖然 STM32F4 已經到我手邊 (好想快點開始玩,把 Linux 弄上去架個 server 之類的練練功,不然該忘的也差不多都忘了…),不過答應自己要做的東西得真的做出來才行。這篇文章就是要分享利用上次提到 Johnny-Five node package 做的一個小時鐘。

這其實是我在國外網站看到的一個小專案,在沒有看到相關 C/C++ repo 或是 source code download link 的情況下,決定自己手幹一個。另外,由於之前太懶,做的東西都沒放上 github,現在也懶得整理過去的專案 (因為真的太多了…),不過從現在開始的專案都會丟上去 (誰叫用學校帳號辦這麼爽),所以有興趣的同好朋友們可以自己抓下來玩玩。

在此附上 github repo 連結:https://github.com/4D616B6572/Johnny-Five-clock

進入主題,就先從硬體開始吧。

硬體周邊需求

  1. Arduino UNO x 1
  2. 麵包板 x 1 (非必須,也可以很霸氣的直接焊起來)
  3. SG90 伺福馬達 x 7 (因為我想做成 7 段顯示器的樣子)
  4. 跳線 -> ∞
  5. 各種紙板,泡綿膠等美工用具

如標題所示,這只是本專案的 part 1,上方所列的硬體需求只是單一個數字的實作,既然要變成時鐘,那少說也得準備四個數字,不過硬體方面就得再思考要怎麼準備,畢竟 UNO 腳位有限,我也不可能拿四塊板子,除非我準備用來和 UNO 溝通的主機 (eg. STM32F4) 有這麼多 USB 埠口,屆時想到會在下篇文章分享,若有想法的同好朋友也歡迎提出討論。

軟體執行操作

事實上,整份 code 不到 100 行,而且也不難,只是要一直嘗試一些原先在 Arduino 上很直覺的事情但在 nodebot 上卻不是如此,畢竟 nodebot 在操作上本身就跟一般單晶片專案的思維不太一樣,講得有點籠統,不過沒關係下方會一併說明。

在整個過程中,不知道是我 google 能力還有待加強還是真的確實如此,我在網路上並沒有找到太多有關 Johnny-Five 實作實際專案的文章,多數都是如何使用、起頭、點燈之類的初階分享。

因此我會針對和 Arduino 真的很不一樣的點做說明,其他有寫過 nodejs 的同好一定看得懂程式碼本身在做的事情。

Johnny-Five 最重要的兩件事,第一,new 一個 five.Board() 的物件稱之為 board,再來 board.on (“ready", [callback]); callback function 在 “ready” 後被執行;第二,REPL (read-eval-print-loop),是重點中的重點,是 Johnny-Five 能讓 nodebot 活得像一般單晶片一樣的主要媒介

相信有看過介紹 Johnny-Five 套件相關文章的朋友,一定都對第一個重點不陌生,不然也不可能點起 UNO 版上的 LED。至於第二個,應該相對不熟悉,而這也我主要想分享的部分。

board.on (“ready", [callback]); 如同 Arduino 裡的 void setup(),兩者都是做些初始化以及 “一次性的操作 (eg. call function)”,那 REPL 是不是就相對應應該會是 Arduino 裡的 void loop()?目前我覺得不是,之所以說 “目前” 是因為我覺得以我現在對 Johnny-Five 的理解,我對於在此套件中要做到像一般單晶片 eg. Arduino void loop() 不斷執行定義在其內的所有 function 的可行性 (包括 ISR 的註冊) 打了一個大問號。

因為在 REPL 內,雖說稱作 loop,它也只不過是去聽某個 function 是否有被 call,雖然也可以在此註冊類似 ISR 的功能,但它本身並不會主動去執行某個程式,除非你呼叫它也就是說,若在進入 repl 後,不打出某個 function 按下 enter,nodebot 是不會理你的,如同我放在 github 上的 source code,在 this.repl.inject() 中有定義一個 function time,當在 repl 中打下 time() 才會回傳現在時間的秒數

舉個簡單的例子來說明,在一般的 Arduino 環境內如果想要讓 LED 不斷閃爍,從最基礎的 Blink 範例程式可以知道,只要 HIGH 和 LOW 加個 delay 交替放在 void loop() 中執行即可,但在 nodebot 我目前並不曉得要怎麼做到這件事,這次的專案是因為剛好我要抓的是時間,所以我可以註冊一個 setInterval,讓他每秒回傳一個數字回來,但如果不是固定時距,而是一個特別的 pattern 那豈不是糟了?

另外上方提到 nodebot 可以註冊類似 ISR 的功能,這件事對於有寫 js 的同好朋友一定也不意外,畢竟 js 就是 event driven 的語言,js 的 callback function 就是在處理 “某件事發生後”,去執行一個相對應的行為,而在 firmware 就像是 button.on(“down", [callback]),指 button 被按下時執行 callback,非常直覺且合理。

想要解決 nodebot 不能真正做到 loop 的行為,講到這不知道有沒有人跟我一樣有一個暴力的想法:那就寫一個 function 裡面有個 while(1) 不就得了?這樣我只要在進入 repl 後,執行某個 function 一次,他就如同 Arduino 的 loop() 一樣,能夠跑到天荒地老?

照理來說好像可以,但我直覺告訴我應該不行,感覺 repl 會直接被綁架,不能動作,待我實驗,下回揭曉!

最後就用 nodebot 時鐘單一數字成果的 GIF 作為本篇結尾吧!

本文同步刊登於 MakerPRO

廣告

JavaScript 統一世界 – node.js x Arduino

最近一次比較成型的專案距離現在已經有一段時間,由於在自己的心理狀態以及學校課業的拿捏 (花多少時間在課業) 上,一直沒有給自己一個好的交代,無法說服自己。幸好,這些困擾許久的煩悶,最近因為自己的調適加上下定決心嘗試,而有了個明確的方向。

包括上篇文末所提的全新專案 (剛開始不久…),以及我曾講過很想做的專案我都記在心中,雖然不知道多久能生出來不過我想我會盡力找到時間,畢竟興趣才是我的動力,不為什麼只是單純開心。

雖然專案還沒有個底,原本以為不知道可以幫自己紀錄點什麼,不過認真想想能寫的還是不少。

最近幾次和 Bird 聯絡剛好提到我打算用 STM32F4 來開發我的專案,加上聊到 RTOS,想要在 rpi 玩玩 Embedded Linux (我也想搞在 STM32F4 上看看),Bird 就說要幫我淘個板子讓我玩玩 (鳥爸無敵,太開心拉),不過板子還沒來,既然還沒來也沒東西可以分享,不如就來說說近期的發現吧 – 利用 node.js 進入硬體的世界。

JavaScript 統一世界

事實上也不算是近期的發現,隱隱約約早就知道 python, javascript 能用來寫 Arduino,不過從來沒試過,也不知道這個東西的能耐到底在哪。

因為過去就有在接觸 node.js,加上這學期的課程剛好修了網路服務程式設計,外加接案,寫 js 的時間已經超過了 C/C++,因此突然有個想用 node 去會一會 Arduino 的想法,而這一玩不得了,有種發現新大陸的感覺,也激發了我的新想法。

我算是 3/4 個 js 的愛好者,跨足 web programming,接著後起的 web app,加上較少人知道的 firmware 領域,js 基本上可以說是無所不能 (不論其他例如效能、穩定性的比較)。網路上討論 javascript x IOT 的文章也不在少數,開源的 library 數目更是多到讓我有點驚訝 (如下連結)。

文章參考:10 Javascript IoT Libraries To Use In Your Next Project

而本篇會針對 Johnny – Five 這個目前最受歡迎 javascript robotics framework 做分享,不會是以教學文的方式呈現,因為網路上可以找到一卡車。

Johnny – Five

螢幕快照 2019-04-21 上午2.38.07.png圖片出處:https://github.com/rwaldron/johnny-five

其實早在 2010 年網路上就開始有人提出 NodeBots 的概念 (我真的是孤陋寡聞…),從 node-serial 開始往下長,長成現在巨大的模樣。Johnny – Five 只是其中一個成員。

目前 johnny – Five 不論是硬體相容性,API 完整度,技術討論社群都是眾多 framework 中表現最亮眼的,若有興趣的同好朋友可以到其官網看看。

講到這裡,若是先前只有在 Arduino IDE 上寫過 C/C++ code 的朋友想必現在會一頭霧水,心中疑問:“蛤,那要怎麼讓電腦認得板子?”

事實上這個懷疑是對的,板子並不是 USB 插上電腦就可以動,要使用 johnny – Five 之前,必須先做些前置動作。如果用 Arduino UNO 作為例子 (實際上 Arduino 系列被支援的開發版都可以這樣操作),必須先打開 Arduino IDE 找到範例裡的 StandardFirmataPlus 並燒錄 (upload) 進去,這樣 Johnny – Five 才可以被使用。

之所以需要此步驟的原因是因為,要讓電腦上 (主機 host computer) 的軟體能夠 “認得” 並 “操控” 開發版 (MCU),這之間是透過一個叫做 Firmata 的 communicating protocol 做溝通,也就是 StandardFirmataPlus 這份 code 做的事情。

主機上軟體的訊息 (程式碼),包括使用哪個 USB port,baud rate 等等就可以由 node-serial 傳給 Arduino UNO。

如果說軟體的第一步是 print “Hello World!",那 MCU (SoC) 第一步就是讓 LED 燈亮!

燒錄完後接著,直接先讓 LED 亮起來再說!

螢幕快照 2019-04-21 下午11.55.36
此圖即為將 Johnny – Five github 上的範例 glone 下來在我的電腦上執行的結果。

程式碼的部分應該不是什麼大問題,重點有趣的是下方 terminal 顯示的訊息,執行 node strobe.js 後有個 REPL (Read-Eval-Print Loop),顧名思義是個 “讀取 – 求值 – 輸出的循環”,是不是有一種似曾相似的感覺?我個人覺得很像 Arduino 的 firmware code 燒錄後,利用 serial console 下 AT_command 的感覺,只是 node 弄得比較直觀好懂一點 (註 1)。

看完軟體的部分,再看看 UNO 版。可以看到 LED 13 的確每 1000 milliseconds 閃一次外,也可以看到 LED 13 旁的 RX LED 也以一個固定的頻率在閃爍,表示電腦一直有在送訊息給 Arduino UNO,如下圖:

57382416_1355530074600454_3777324978353995776_n.jpg

講到這裡,我自己其實一直存在著一個疑惑,既然 REPL 得透過 node-serial 才能和板子溝通,那一但 USB 拔了不就什麼都沒了嗎?確實,不像 Arduino 一旦燒錄進 flash,只要通電,都還是執行當初被燒進去那份程式碼的行為,這也是我覺得 nodeBot 非常大的一個缺點,即便同份程式碼,每次上電就得重新 setup 一次,而且得隨時和電腦溝通,所以他是離不開電腦的…。

若說要做 rpi 相關的應用倒是無妨 (畢竟 node.js 也可在 Linux 上運行),但像前一個專案的應用 (A8106 無線調光) 就不可能使用 nodeBot 執行了…。

以上是目前我個人對 nodeBot 和 Johnny – Five 理解,在 STM32F4 和 rpi 到手前我想我會用 Johnny-Five 搭配 express (node.js web framework) 做出點小東西來過過乾癮,屆時在寫篇文章紀錄吧!

註 1:有興趣但不想自己 setup 一些東西的朋友,又或是沒有想法想先玩玩看別人寫好的範例程式碼可以參考這個網站:node-ardx (比 johnny – Five 自己 github page 所提供的 git repo 還要豐富些) 所提供的 package,除了在 REPL 中有自定義的變數,讓使用者可以動態的和所定義的變數互動 (eg. LED,執行 led.on() 等等),裡面也有其他詳細的應用案例介紹,一樣,git clone 下來就可以直接執行了。

文章參考:NodeBots – The Rise of JS RoboticsJohnny – Five github repo

本文同步刊登於 MakerPRO

A8106 (8051 2.4G RF 無線調光) – RF 通訊實作 receiver & sleep mode (下)

繼上篇 RF 通訊實作 trasmitter 的部分大致分享完後,接著就是 receiver。如果單就 “接收" 這件事來說,實作並沒有太困難,因為和發射 (transmit) 的部分非常類似,所以若搞懂了發射的機制,那接收不會是問題。

不過這次的專案在接收端 (receiver) 不只是要做接收,還有一個非常重要的功能 – 學習模式。據我得到的 spec 指示,一個控燈的 receiver 要能 (也最多只能) 記住三隻發射器 (也就是 transmitter),透過 receiver 上的三個 buttons 操作特定的流程 (eg. 長按 button 1),進入學習模式;進入學習模式後,再依特定的流程記錄某一隻特定的發射器 (eg. 進入學習模式後,某發射器發射訊號,receiver 收到後若 button 2 有被 clicked 兩下,那就拉出 ID 資訊記在 flash 裡),最後再操作離開學習模式的流程。

學習模式的大方向如上所述,實際上要注意的事項還真不少,嚴格來說要完成 receiver 的開發相較於 transmitter 是難上許多的。另外,transmitter 上篇也有個大重點尚未介紹 – 睡眠模式 (sleep mode)。睡眠模式 (sleep mode) 之所以重要是因為 RF 發射是相當耗電的,比 normal mode 時的耗電還要高上好幾倍,如果不想一直換電池的話這是必須的功能,下方介紹時再一併說明。

以當時 Winson 根據客戶驗收需求給我的指示,開發的先後順序是:基本 RF 收發功能並且能控模擬的 LED 輸出 -> 睡眠模式 (sleep mode) -> 學習模式 (很可惜,由於當時案子似乎沒談清楚,以及介於我快要離職之際,學習模式並沒有碰到)。

那就照順序從 receiver 的接收功能繼續下去吧!

接收端 (receiver)

如果大家對於上篇文章有印象,要切換到接收端的 code,首先 P3_5 要先設為 0 (或也可用硬體直接控制),而接收部分的程式碼如下:

提個外話,相較於前篇把 transmit 的 code 寫在 main,這裡的做法我個人比較喜歡,再寫一個 function 讓 main 看起來比較乾淨 (我習慣分檔案)。

言歸正傳,由於 RF 的發射是廣播 (broadcast) 的方式傳送資料,也就是說當按下發射器並且觸發 RF 發射時,所有通電的 receiver 都會收到這個訊號 (這裡不論強弱),至於該不該收 (也就是該不該接收某發射器傳來的 data 並做相對應的動作) 是根據開發者用程式碼所定義的,就是所謂 ID 的功用。

還記得 transmitter 將 8 個 bytes 的 data 透過 Packet_Tx[8] 傳出去,裡頭包含了自定義的資料,即每個 byte 所代表的意義都可以自訂,裡頭就包含了 ID (eg. 第 7 個 byte 代表 ID) 以及一些其他重要的資訊。而 light_condition_receiver 這個 function 最主要就是在 “聽空氣中 RF 的訊號”,確定收到後再執行 RxPacket 這個 function 去分析收到的 packet 的內容。

把兩個主要的 function 分別細看,先講 light_condition_receiver() 的部分,如同 transmitter 的 code,這裡也是利用 sample code 寫好的 function – RFLIB_StrobeCmd 做處理,只要把 TX 改成 RX (接收端收資料),RX 改成 TX (接收端發出 ack),其實就大功告成,就不浪費篇幅了。

至於 RxPacket(),上圖的 code 不包含 error checking 因此相對簡單許多 (比如說錯幾個 bit 以上要做什麼相對應的處理),先利用全域的 array – tmpbuf[128],去存從 packet 讀出來的東西,再利用 tmpbuf 裡的資料回 light_condition_receiver 做下一步的執行 (即註解的 light control 區段)。

大致上單純接收並解析 RF packet 的實作介紹至此,一樣我相信還有許多功能我沒玩到,不過若有問題或發現,歡迎各位同好朋友可以提出來一同討論,指教交流!

基本上專案進行到這裡,已經可以順利控燈了。不過如同文章開頭所說,若不想要一直更換發射器的電池,那編寫發射端的睡眠模式勢在必行 (接收端倒是沒差,因為控燈的裝置是可以持續插著電的)。

睡眠模式 (Sleep mode)

睡眠模式很複雜,至少比我一開始想像的還要困難許多。在分享我開發睡眠模式的過程前,先講有睡眠模式和沒有睡眠模式耗電量的差別,這樣看下去可能會比較有感。根據 A8106 的 datasheet,在一般板子通電預設的狀態下,MCU 會在 normal mode,RF 則會在 sleep mode,在此情形下的耗電量為 5.5 mA (我測的時候的確在 5. 多 mA)。

而 RF 在 TX mode (MCU 仍在 normal mode) 時更是飆高至 17-24 mA 不等。照發射器所使用的電池,如果我沒記錯是 CR2032,容量是 225 mAh,姑且不論電池本身的特性,例如內阻損耗我直接忽略不計 (我甚至記得這種鈕扣電池最大放電電流應該是沒辦法到 17 mA 這麼高…),在不發射 RF 的情況 (RF in sleep mode),225 / 5.5 ~= 41 h (小時),意思是說不到兩天的時間什麼都不做就沒電了,未免太過荒謬…。

為了解決這個棘手的問題,A8106 設計了三個 RF in sleep mode (此時 MCU 是 stop mode) 的狀態,分別為 PM1,PM2 和 PM3 (先不算 PM3 without sleep timer 的部分,此三種 mode 皆是有 sleep timer 的情形),就拿我使用的 PM2 做例子 (各個 mode 有些差距在此先不提),耗電量只有 3 uA,整整比原先少了 1700 多倍…,很明顯的高下立判。

而在實作方面,睡眠模式要顧的不外乎兩件事:1. 進入睡眠模式,2. 從睡眠模式醒來。而在我實作的應用中,醒來的唯一目的就是發射 RF,亦即沒要發射就睡,以此方式省電。

然而進入睡眠模式簡單,醒來做事反而難。A8106 有個很有趣的特色 – key interrupt,其三個 port P0, P1, P3 的任何一隻 GPIO 都可以作為外部中斷的媒介,此稱為 key input,可以透過 key interrupt (即外部中斷) 喚醒 A8106 (stop mode -> normal mode) 接著執行這個外部中斷想要執行的程式碼。

若套用到我的實作應用上,上述的 key input 即為使用者按了發射器上的任一按鍵,喚醒 A8106 並且 RF 發射那個按鍵所代表的功能訊息。又若將整個睡眠模式放進原先的程式碼,流程就會變成以下:

通電 -> 進入 RF sleep mode, MCU stop mode -> key input -> key interrupt (RF sleep mode, MCU normal mode) -> 發射 RF -> 進入 RF sleep mode, MCU stop mode。

流程看起來簡明扼要,但每一步所要注意的事情可多了…,包括 key input 的設定 (WUN – wake up enable),RF mode 和 MCU mode 設定的先後順序,有沒有足夠的時間給 mode 轉換等等都是問題。由於一開始我一直沒有搞清楚整個睡眠模式的流程,導致我到離職那天都尚未破關,現在也沒東西讓我繼續試,因此我最多只能分享到這,實在有點可惜,若後續的專案有這類的需求 (應該很常會有),屆時我會再分享我的程式碼,並深入解釋其中奧秘 (據說睡眠模式真的很麻煩,各家埋控場奇異的程度比一般的功能都還要高出許多)。

安可爾系列也就到此告一的段落,接下來會是全新也是我正在進行的個人專案,下次見!

本文同步刊登於 MakerPRO

A8106 (8051 2.4G RF 無線調光) – RF 通訊實作 transmitter (中)

由於篇幅關係,最終決定將 transmitter 和 receiver 各自一篇,不然怎麼切割都好像怪怪的…。廢話不多說,直接開始吧!

發射端 (transmitter)

發射端有兩個有趣的重點值得提出來分享。第一,button 按下去多久以及怎麽樣算是一個 “有效” 的 “按下”;第二,RF 傳輸的邏輯。

偵測 button 被按這件事我以前是有經驗的,不過還是停留在我知道因為空氣中有很多雜訊,可能導致 input pull-up 的 button state 變成 LOW,明明沒人按,但卻執行被按之後的行為,以前是用 “秒數” 去決定是否 “成功” 被按,比如說 state 在 LOW 時維持了 1 秒才算有效。

不過業界不是這樣操作的,直接利用部分程式碼解釋,如下:

上方的 code 是 timer0 的 ISR,透過 t0hrel 的設定可以將其設為 1 millisec 進來一次,重點是下方的 button1_time_cnt 變數,unsigned int 在此是 2 個 bytes,每 1 millisec 讓這個變數向左平移一個 bit,並且和 Button1_SW1 做 OR 的邏輯運算,如果翻譯成白話就是:

根據 Button1_SW1 (P3_2) 的 state 為 input pull-up 為 HIGH,因此 button1_time_cnt 為 16 個 1,而因為 timer ISR 的設計,每 1 millisec 就會去檢查一次 Button1_SW1 的 state 並且將其值 assign 給 button1_time_cnt 的 LSB,下次進來 ISR 時又會向左平移,依序執行下去,也就是說,在 main 裡面可以利用 if statement 判斷,button1_time_cnt == 0x0F 時,表示 button1 真的被按了。

這是一個極具彈性的寫法,利用 ISR 有效且精準的掌控 button 被按多久才算是有效,在此為遇到 0x0F 波形時成立 (利用示波器真的可以看到)。不過這個寫法是用在短按或是一次性按下的功能,比如說單純 on/off,也就是遙控器最上面那個 button 的功能。

至於長按的部分,目前的寫法是用一開始提到的方式,利用 flag 隨者每 1 millisec ISR 檢查 (eg. 判斷 Button2_SW2 是否為 state LOW) 而增加,如果 flag 的值比某個值大 (自己設定怎樣才算有效的一次長按,在此我是設 1 sec,意即 flag 的值如果 >= 1000 表示長按一次成功) 就成立。

講到長按 button 的功能就可以順勢帶到第二個重頭戲 – RF 傳輸邏輯。

當長按一次成功時,transmitter 就會執行發射一次 RF 的動作。首先,發射前總得要知道要挾帶的 data 是什麼,因此得先用個 array 將 data 放進去,以 datasheet 內提到 FIFO mode 的傳輸 packet format 的 playload 最大是 256 bytes,其他像是 preamable,ID code,CRC 在官方的定義的 protocol 都有說明,只不過我要實作的應用不需要用到這麼多功能,我只需要 8 個 bytes 就可以處理完 playload 以及 ID 的需求,以下用部分程式碼來解釋:

從 TXFIFO 這個 register 開始往下數 8 bytes 就是我會先放我要傳的 data 的地方 (從 0x900 ~ 0x93F 都是可以放 data 的位置,共 64 bytes,實際要如何操作到上述最大 playload 256 bytes 我並沒有研究),一旦 RFLIB_StrobeCmd(CMD_TX) 執行,東西就會被打出去 (RFLIB_StrobeCmd 這個 function 是原先 sample code 就有寫好,也是去設定某 SFR 讓 RF 動作),接著是我覺得最有趣的部分。

由於並不知道 RFLIB_StrobeCmd 這個 function 裡面到底怎麼寫,因此 while ((RFLIB_ReadReg(MODEC1_REG) & 0x80) == 0x80); 這行會顯得有些莫名其妙。從 datasheet 只知道 MODEC1 這個 register 如果值為 0x80 時為 sleep mode,那表面上看來在我設定完 RFLIB_StrobeCmd(CMD_TX) 後,MODEC1 的值應該已經要是 0xC0 的 TX mode 了,這個 while 怎麼樣也不會成立。我後來給自己的解釋是時間差,即真的要將 MODEC1 的值設定成 0xC0 這件事是需要時間的,也就是 transmit 的時間,真的變成 0xC0 才算傳輸完成。

而在 A8106 的 RF protocol 中,transmitter 傳 data 出去後,會把自己設定為 RX mode,去接收是否有來自 receiver 的 ACK bit,如果 Timeout 到了還沒收到就重新在 transmit 一次。

以上是 RF transmit 介紹的精簡版,只挑出了幾個比較大的重點出來,事實上有很多小的眉角需要注意,而且我相信我做到的部分只是 A8016 的冰山一角,這顆埋控能做到的事情絕不如此,有礙於篇幅先停在這裡,下篇會繼續將接收端一併解釋清楚,分享出來,幫自己紀錄。

本文同步刊登於 MakerPRO

A8106 (8051 2.4G RF 無線調光) – RF 通訊實作 (上)

在台大電機的第一個學期終於結束,也撐過了地獄的兩個月,過年期間我會將過去一年的種種在 facebook 網誌上好好整理一番,在此就讓我直接開始那個從 12 月初就積欠到現在的專案文章吧!

前些時間,基於學校課業考量,又加上外務真的太多,因此在 11 月底時我向公司提了離職,結束了我在安可爾科技短暫的四個月旅程。學到的東西很多,不過時間很趕有些東西來不及紀錄,文章內容若有問題,歡迎交流指教。

上篇文章以埋控開發必備的 debugger 作為開頭,事實上跟 A8106 沒有絕對的關係,只是做了幾種 8051 單晶片的開發後,對於 debugger 之於開發的重要性有感而發,因而放置在此系列的第一個部分。 如同上篇文末所言,此篇就會介紹 A8106 是如何進行 RF 通訊以及實作方式。

要談 RF 實作,必須要先瞭解 A8106 的特性,那就得從 datasheet 下手,網路上能找到的是瑋忠 A8106 RF module 規格書,這裡面只有記載一些比較基本的資訊,像是 electrical specification,哪些 GPIO 在這個版本有被拉出來 (interface descriptions),一些簡略的 features 介紹等等。

實際上如果要真的使用 A8106 實作 RF 通訊,還是要回歸到最原始 Amiccom A8106 datasheet 清楚瞭解各個 SFR (special function register) 和其他像是 timer,port 等等的功用,才有辦法成功實現。不過網路上並沒有相關文件 (應該是沒有釋出),所以我只能將我用到的部分分享給大家參考。

上篇文章也已經提到,此專案會利用遙控器 (transmitter) 發出 2.4G 訊號來控制燈具 (receiver),並且利用硬體 PWM 的方式進行燈光控制。若在功能上定義得更清楚些,從接收端講起,由於接收器燈具的部分客戶會提供,我只負責將控制的部分寫好,收 RF 訊號,控制 8 顆燈 (8 段亮度,每顆燈分別不做 PWM 只有 on/off 行為) 的亮滅,實現硬體 PWM 的感覺。

另外,發射端遙控器的部分,總共有三個按鍵,上下鍵各一,再外加一個可以記憶此時 PWM 狀態,做出關和亮 (亮起來時的狀態是上次全關前的亮度) 的功能,遙控器如下圖。

螢幕快照 2019-01-27 上午10.28.37.png上圖即為三鍵式遙控器

其中 “+" 按鈕預計短按控制單一顆燈的 on,長按做亮度遞增的效果,"-" 按鈕即為 “+" 的反向。而最上方的按鈕的功能就如同前述的一樣,可以記錄上一次關燈時的狀態。

而不論發射端或是接收端,公司還是得搭配 A8106 設計我們自己的電路板,為了成本以及開發的便利性,基本上 transmitter 和 receiver 的板子是一模一樣的 (至少在測試的時候是,不過抱歉電路圖是公司資產我無法放上來),要模擬真實情況的應用,因此板上有三個 button (模擬發射器的三個鍵),以及由 A8106 拉出來的 8 隻 GPIO 以及一隻 GND 作為 output 給燈使用,如下圖:

圖片 1.png此圖中的遙控器已是實際樣品的版本 (新版本的 PCB 設計),不為前述給測試之用。

功能確定那接著就來看要如何從 datasheet 提供的資訊,利用 code 將 RF 做出來。首先得先搞定前置作業再說。

和之前的狀況一樣,為了讓開發者更容易上手他們家的單晶片,廠商都會釋出他們的 sample code 給下游做參考,以利下游公司的開發速度,這次也不例外,基本上所有基礎功能的 function 都己經寫好,像是 timer,interrupt,uart 等等 (不過看不看得懂又是另外一回事了…像這次的 code 真的很髒…,要不就是沒 comment,test code 糞 code 沒標註,要不就是變數名稱取的很難理解…)。

根據 sample code 的指示,我自己整理出了主要的功能脈絡,將 sample code 改寫成我要的東西。在 main 裡面,很乾淨的利用 define P3_5 (*註 1),1 時為 transmitter,0 時為 receiver,不過有些東西是不論 transmitter 或是 receiver 都要設定好的初始值 – port 的設定。

A8106 在 GPIO 方面,總共有三個 port,任一 port 皆由 8 bits 組成,每個 bit 代表一隻腳位 (所以共 24 隻 I/O),像上述提到的 P3_5 即為一例。在設定方面,A8106 有一般的 logic high/low (*註 2)(eg. P0 = 0x00 -> port 0 所有 pin 腳皆是 LOW state),OE (output enable, eg. P0OE = 0x00 -> port 0 所有 pin 腳皆為 input mode),PUN (pull-up, eg. P0PUN = ~P0OE -> input pin 才需 pull-up),WUN (wake-up enable, 這個部分牽涉到 sleep mode,因此會留到介紹 sleep mode 時一併說明)。

根據需求以及上述說明來設定 register,除此之外,在 A8106 中,有三隻特殊給 RF 專用的腳位,分別為 GPIO1 (P1_2),GPIO2(P1_3),CKO (P0_7),原先看 datasheet 以為這是 RF 運作時會用到的三隻腳位,不能讓我將其使用於一般 GPIO,不過後來經查證發現事實不然,RF 早就被 module 本身做掉了,何必要在使用三隻額外的 GPIO 做傳輸呢?果然,此三隻腳位如果在 IOSEL 這個 register 為 xxxx 1111 時,可以用來對 RF 做一些設定或者說控制,並不是 RF 在傳輸時會利用到這三隻腳位,因此對我來說,此三腳位完全可以當作 GPIO 做使用 (且瑋忠甚至沒有把 P1_2 拉出來)。

接著,timer0,RF 等的初始化,一些重要 flag (eg. TR0, EA) 的設定,RF mode 的選擇等等做完後終於可以進入 transmitter 的部分。

*註 1:後來發現,瑋忠科技的版本因為有把 P3_5 這隻 GPIO 拉出來,既不是控制 8 個燈的其中任何一個 output pin,也不是 button input pin (因為公司設計的板子根本沒拉出 P3_5),所以實際上可以利用硬體的一個切換紐去決定現在板子要是發射端或是接收端,不需要每次 upload 時都手動去更改 code 裡面 define P3_5 所代表的值。

*註 2:稍微提醒一下,我發現不少人會在實作的過程中將 logic HIGH / LOW 和 INPUT / OUTPUT 混在一起,且本人自己也犯過類似的錯,很神奇,但就是有可能會錯在這種地方…。

本文同步刊登於 MakerPRO

A8106 (8051 2.4G RF 無線調光) – debugger (JTAG or SWD)

一如既往,先解釋一下這次來到手邊的專案要做什麼。簡單來說,要利用 Amiccom A8106 這顆 8051 based 的 RF 模組,做一對遙控器和接收器,發射端廣播 2.4G 的訊號,接收端收到 packet 並解析後做出相對應調光的動作,這次的調光是硬體調光,並不是軟體的 PWM。

2(A8106 瑋忠科技版本)

不僅如此,客戶最終希望能夠多支遙控器都能控制同一個接收端,所以勢必需要實作學習模式,將遙控器的資訊 (eg. id) 記在接收端的 flash 裡面,讓接收端認得這個空中的 broadcast 是誰發出來的。

這是我在安可爾的第二個案子,我得說,真的滿難的,以前在 Arduino 上玩的 WiFi,BLE (都是 2.4G) 都是第三方 library 寫好,直接 call function 來用就可以,但這次,我沒有 function 可以 call 只有下游廠商莫名又難懂的 sample code (很多真的有寫跟沒寫一樣…) 讓我去研究 2.4G RF 的傳輸方式。

經過幾個星期的努力,終於破關 (只破了最主要的功能,週邊還沒研究清楚,繼續努力),整理實作的過程後產出了新的一個系列文章,接下來將會一一分享出來。

debugger 的秘密

做韌體的同好朋友一定都知道,無論要開發的專案是什麼,燒錄這件事絕對是最基本也最重要的事。

上個專案是我第一次使用 keil C51 做開發,當時有個叫做 SN-LINK 的硬體說是 SN8F5701 這個模組開發的 debugger,我不疑有他,接上去後照著步驟設定的確就可以執行燒錄的動作。

當下的我沒有多想,因為 Arduino 並不需要這樣東西,不需要在模組和電腦之間還要連一個 debugger 才可以燒錄,覺得是特例,直到這次的專案,debugger (這次叫做 ICE,Amiccom 自己家的 debugger) 又出現了,完全激起了我的好奇心。

為什麼這個 debugger 需要存在?他不就是燒錄器嗎?先講結論,他主要的功能不是燒錄器,如果你不想執行 debug mode 那他其實可以不用存在。

故事的源頭,要從 bootloader 開始講起…

bootloader 的意義

我們都知道燒錄絕對和 bootloader 脫離不了關係。

Bird 曾經跟我說過:everything can be called bootloader before user code.

一個模組 (開發版) 中,bootloader 可以有很多段,例如,若是確定為燒錄模式,就會執行接下來的 bootloader 去等 UART 的資料,接著寫到 program flash (中間什麼 erase block 等等),動作過後,燒錄完成。

在模組通電的瞬間,模組內會先執行 MCU 廠商已經寫死在裡面的 bootloader,這是用來控制硬體的,像是這次的 A8106,datasheet 裡面有提到,P04 腳位如果是 LOW 時會進入燒錄模式,若為 HIGH 則為 normal mode,也就是說,通電後,bootloader 利用判斷式去偵測 P04 腳位的情況,根據判斷結果再塞對應的值進相關的暫存器,達成燒錄或是正常的模式。

相信以上,有在玩專案的 maker 同好或多或少都知道這件事,不過知道這些跟 debugger 有什麼關連?

一般的埋控開發,在與電腦連結前中間都會出現一個 middleware 我們稱之為 debugger,這個連接 debugger 的介面就得好好來介紹一下,因為 P04 若為 LOW,與 debugger 連接的其中兩條線 (此為 P14 和 P15) 將被設成 debug IO (可以進行燒錄或 debug),反之,P04 為 HIGH (normal mode),P14 和 P15 就會是普通的 GPIO。

此介面稱為 JTAG (or SWD),不過一般玩 Arduino 的 maker 不太會接觸到這個叫做 JTAG 的介面,而講到 JTAG 當然 SWD 也得一起說明才完整,不過其實簡單來說 SWD 就是簡化版的 JTAG。

JTAG 是非常常用,在一般的模組、開發版上必定會看到的 debug 介面,由四隻腳組成 (這裡講的是主要,市面上還有很多奇形怪狀的形式),分別是:

  1. TDI (data in)
  2. TDO (data out)
  3. CLK (clock)
  4. TMS (mode 切換)

*每家可能名字會有些許差異,但功能都一樣

它可以操縱 IC 內暫存器的讀取或寫入,聽起來沒有很厲害,依上次專案的經驗,我的 user code 也可以直接對 MCU 的暫存器進行設值的動作。

但 JTAG 厲害的地方在於,以這次專案使用的 A8106 作為例子 (雖然 ICE 是走 SWD,不過解釋上用 JTAG),A8106 是 MCU,只是一顆 IC,原廠的下游廠商會根據這顆 IC 做不同的功能釋放,做出市面上我們看到的模組 (開發版),由此可知不同廠商很可能都利用 A8106 這個 MCU 兜出不一樣功能取向的模組,意即若某廠商覺得某些腳位並不重要,他可能就不將此腳位拉出來,作為模組的 GPIO pin,或是其他功能的 pin 腳位。

螢幕快照 2018-12-11 上午11.54.08
(Amiccom ICE 的 SWD 示意圖)

也就是說 user code 可以控制的部分在於 “廠商有拉出來的腳位",而 JTAG 可以做到的是,整個 MCU (甚至周邊的 IC) 他都可以操控!之所以說周邊 IC 也可以,是因為到底行不行,都是根據事先被定義好的 JTAG mapping,去做讀取或寫入的動作,偵測那些腳位是否有出問題,這些是軟體碰不到的部分。

相信眼尖的同好朋友有發現,JTAG 也才四個腳位,要怎麼樣才能去讀取或寫入這麼多的腳位?其實,JTAG 用了 shift register 的概念,serial to prallel,用 buffer 吃值的方式,將序列的 data 轉成並列的形式 (CLK 腳位就是在控制這個把 buffer 值倒出來的速度有多快)。

介紹完 JTAG 的強大,但還是沒解釋這跟燒錄有什麼關係?

就像我剛開始提到的結論一樣,如果不用 debug mode 那真的沒什麼用,因為要不要進燒入模式的確是 bootloader 決定的,不過如果要 debug,在此也只有 JTAG (or SWD) 做得到。

相信有在開發 web 的朋友一定知道,在 debug 的時候,可以 “一步一步” run program 看到底是在 code 的哪個環節出了問題,而 Keil C51 debug mode 也可以做到完全一樣的事情。而這整件事歸功於這個 “debugger" 的存在 (事實上就是 JTAG),拜 JTAG 可以操控暫存器所賜,可以直接控制 MCU 裡面的 clock,而我們知道 MCU 可以動起來就是因為 clock 在震,也就是說 JTAG 可以控制 MCU,動一步,clock 停住,達成我們所看到一步步偵錯的效果。

因此他叫 debugger 不是沒有原因,因為它主要的功能並不是燒錄而是執行 debug。

不過!不知道有沒有朋友發現,剛剛上方提到 bootloader 也只是將判斷的結果塞進某些特定的暫存器。那讀取或寫入暫存器不就是 JTAG 的本能嗎!也就是說 JTAG 可以直接設值讓模組進入燒錄模式!也難怪常常聽到前輩說 JTAG 是整個模組的最後一道防線 (一般常說的 “ MCU 死了” 通常就是指 bootloader 當掉)。

談完以上的種種,以及 debugger 實際的定位後,那反觀 Arduino 為什麼沒有 debugger?

經過師父 Bird 解釋後才了解到,Arduino compile 後並不是可執行的 machine code,燒錄前會再經過中間一層 Arduino 自家的東西,才會成功變 machine code 燒錄進板子,至於中間那層到底是什麼,我目前沒有特別去研究。

搞懂了 debugger 以及燒錄的種種後,下一篇文章將會開始談 A8106 的 RF 到底怎麼傳資料,拭目以待。

本文同步刊登於 MakerPRO

SN8F5701 – 8051 ADC 功能介紹

上次由於篇幅的緣故,和大家分享 8051 的 UART 之後沒能繼續把 ADC 講完 (每次都說最後一篇,然後每次都有最最後一篇哈哈…),所以今天這篇真的真的要終結這顆埋控了,文末也會稍微提到目前正在進行的專案,與 2.4G RF 有關。

ADC (analog to digital converter)

我們都知道現實世界是 “類比” 的,不過類比實在太複雜,聰明的人類因此創造出 “數位” 來簡化問題,如果不夠清楚,就想像類比是 “連續數值的變化” 而數位就只有 0 或 1,而這之間的轉換牽涉到很多數學的複雜運算,比如說傅立葉轉換 (fourier tranform),將 time domain 的 data 轉成 frequency domain,原本看似沒意義的 function curve 突然之間有了生命。

本篇就用最簡單的例子 – 電壓大小變化,來實作這顆 SN8F5701 埋控的 ADC 功能。

這顆埋控 ADC 的部分是用 SAR 架構 (聽說還有很多種,但我都還沒遇到過或是我不知道我遇過了…),6 隻可以設為 analog input 的 pin 腳位,解析度有到 4096 steps,在腦中想像一下長條圖 y 軸的刻度,就是 4096 階,也就是說如果 4096 階這個值不變,在 reference high voltage 和 VSS (通常都是接地 0V) 相差的越小  (下方會說明),解析度相對就會提高,但有利有弊,除非進來的 analog 訊號數值和變化真的不大,否則這個情況很難發生。

上述提到的 reference high voltage (參考高電位) 還有 clock rate 是 ADC 中相當重要的一環,這顆埋控有四個 clock rate 可以選擇,以 datasheet 裡面提到的 converting rate,我目前的理解是每次每個數值轉換 (analog to digital) 的速率,所以說如果類比訊號進來的很快,但我 MCU ADC 的 converting rate 設太低,用膝蓋想都知道讀出來的數值可能會出問題。

剛剛之所以說以我目前的理解是因為我在別顆埋控的 datasheet 裡面有看過 ADC sampling rate,至於這和 converting rate 是否相同,經過詢問 Bird 後得知,在大部分的狀況下是一樣的,但有些進階的 ADC 上會不一樣 (就我所知 sampling rate 越高表示越能模擬出真正 analog 訊號的全貌)。

再來談到參考高電位, SN8F5701 提供 4 個 sources 給開發者選擇,分別是 VDD, 4V, 3V, 2V,拿 3V 舉例,因為埋控規定 VSS (0V) <= sampled input voltage 一定要 <= 參考高電位,意思是說如果 reference high voltage 我設定為 3V 我只能去測量 0-3V 之間的數值,而 4096 這個解析度,就是將 0-3V 分割成 4096 階,去定義進來的 analog 訊號該對應到什麼二進位的數值,用下圖搭配著看會更清楚:

螢幕快照 2018-10-20 下午10.32.32.png
圖片出處:類比數位資料轉換器 ADC 介紹

接著在講 initiate ADC 之間,先談談整個轉換流程在這顆埋控中是怎麼進行的。首先要讓 ADC enable (ADENB=1),接下來要讓 ADC 開始可以轉換數值 (ADS=1),當轉換結束後硬體自行會將 ADS 變為 0 表示轉換結束,硬體也會把其他兩個 flag – EOC 和 ADCF 拉 HIGH 為 1,另外轉換好的數值就存在 ADB[11:4] 和 ADR[3:0] 兩個 registers。

而上方提到的 ADCF 其實是和 ADC 的 interrput 做搭配,ADCF 為 1 時,如果開發者有 enable adc interrput (EADC=1),這時 ISR 會被觸發,觸發 “前” ADCF flag 就會被硬體清掉。但我並沒有用 ADC interrupt 的功能,原因下方我會說明。

以下程式碼是 ADC 功能的初始設定:

要作為 ADC pin 腳位,首先要將 pin 設定為 input mode,再用 POCON 設為 pure analog input pin,重點是一次同時間只能有一隻腳位為 analog pin,因為這顆埋控 analog 和 digital I/O 腳位是共用,所以非 analog pin 的腳位一律要設定好是 digital 不然會有問題。

根據四種不同的 clock rate (ADR 的 ADCKS 設定) 會對應出不同的 converting time (converting time 的時間是指從開始轉換 <ADS=1> 到結束 <EOC=1> 這段時間) 我是直接開到最大,用 fosc / 1 的 clock rate 在跑,至於 reference high voltage 我是用內部 VDD (5V) 作為參考高電位,因此可以利用電源供應器給 P01 這隻 pin 腳 0-5V 的類比訊號做為測試。

以下是部分程式碼:

上方程式碼片段總共有三個重點,第一,要讀出來總要看得到是多少,所以搭配 UART 顯示在我的 console 上 (所以也是為什麼我上次覺得 UART 和 ADC 要寫在同一篇的緣故)。第二,我之所以沒有開 ADC 的 interrupt 就是因為我不需要這麼常取值 (但還是很看應用所以也不一定),我反而是用 timer0 去計時,在 main 裡面每隔 1 秒去讀一次 ADB 和 ADR 裡的值。第三,因為轉換結束後不僅 EOC 會被系統拉 HIGH,還有 ADS 也會拉 LOW,所以我為了要啟動下一次的轉換,我勢必得清掉 EOC 並且拉 HIGH ADS。

以上搞懂且成功運行後,就可以在 PC 的 console 上面看到,隨著電源供應器的旋鈕被轉動,console 上顯示的數值就會跟著做改變,而 ADC 的功能測試也到這告一個段落,SN8F5701 大致玩完,雖然沒有到很深,但該摸的基本上都有摸到,繼續前進下一顆埋控!

文章最後提一下我目前手邊正在進行的專案,是利用 Amiccom A8106 2.4G RF module 實作發射與接收器,RF 比我想像中還要複雜許多,不僅僅是廠商自定義的 protocol,還要 based on 這個 protocol 去做不同的 use case (比如說學習模式,接收器可以認得三隻發射器等等),屆時再來分享瞜!

*本文同步刊登 MakerPRO