繼上篇【Amiccom A8106 RF 無線調光】 RF 通訊實作(中) 的部分針對發射器(transmitter)大致分享完後,接著就是接受器(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 時的耗電還要高上好幾倍,如果不想一直換電池的話這是必須的功能,下方介紹時再一併說明。
以前輩當時根據客戶驗收需求給我的指示,開發的先後順序是:基本 RF 收發功能並且能控模擬的 LED 輸出 -> 睡眠模式 (sleep mode) -> 學習模式 (很可惜,由於當時案子似乎沒談清楚,以及介於我快要離職之際,學習模式並沒有碰到)。
那就照順序從 receiver 的接收功能繼續下去吧!
接收端 (receiver)
如果大家對於上篇文章有印象,要切換到接收端的 code,首先 P3_5 要先設為 0 (或也可用硬體直接控制),而接收部分的程式碼如下:
unsigned char tmpbuf;
void light_condition_receiver(void);
void RxPacket(void);
void main(void)
{
//hardware initialization...
if(P3_5)
{
while(1)
{
// transmitter code...
}
}else
{
while(1)
{
light_condition_receiver();
}
}
}
void light_condition_receiver()
{
// the variable counts for error bit...
RFLIB_StrobeCmd(CMD_RX);
Delay10us(1);
while((RFLIB_ReadReg(MODEC1_REG) & 0x80)==0x80); //wait receive completed
RxPacket();
// do the light control here...
// status = tmpbuf...;
// state = tmpbuf...;
// if...else if...bunch of code...
WriteFIFO(); // write data to tx fifo
RFLIB_StrobeCmd(CMD_TX); // entry tx
Delay10us(1);
while((RFLIB_ReadReg(MODEC1_REG) & 0x80) == 0x80); //wait receive completed
}
void RxPacket()
{
unsigned char i,recv;
for(i=0; i<8; i++) // transmit only 8 bits in my case.
{
recv = RFLIB_ReadReg(RXFIFO_REG + i);
tmpbuf=recv;
//error checking (bit, byte)...
}
}
提個外話,相較於前篇把 transmit 的 code 寫在 main,這裡的做法我個人比較喜歡,再寫一個 function 讓 main 看起來比較乾淨 (我習慣分檔案)。
言歸正傳,由於 RF 的發射是廣播 (broadcast) 的方式傳送資料,也就是說當按下發射器並且觸發 RF 發射時,所有通電的 receiver 都會收到這個訊號 (這裡不論強弱),至於該不該收 (也就是該不該接收某發射器傳來的 data 並做相對應的動作) 是根據開發者用程式碼所定義的,就是所謂 ID 的功用。
還記得 transmitter 將 8 個 bytes 的 data 透過 Packet_Tx 傳出去,裡頭包含了自定義的資料,即每個 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,去存從 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 轉換等等都是問題。由於一開始我一直沒有搞清楚整個睡眠模式的流程,導致我到離職那天都尚未破關,現在也沒東西讓我繼續試,因此我最多只能分享到這,實在有點可惜,若後續的專案有這類的需求 (應該很常會有),屆時我會再分享我的程式碼,並深入解釋其中奧秘 (據說睡眠模式真的很麻煩,各家埋控場奇異的程度比一般的功能都還要高出許多)。
安可爾系列也就到此告一的段落,接下來會是全新也是我正在進行的個人專案,下次見!
(本文同步發表於自造者萊恩;責任編輯:葉于甄)
只需不到短短一分鐘...
輸入您的信箱與ID註冊即可享有一切福利!
會員福利
免費電子報
會員搶先看
主題訂閱
好文收藏