作者:Ryan Hu
延續上篇【嵌入式學習心得#2】雷射調光的PWM功能實踐,這篇算是簡易雷達調光的最終章,不過事實上, UART 和 ADC 並沒有應用在這次的專案上,因此本篇文章主要針對這顆埋控,做功能上的測試和介紹。
關於UART
剛開始玩 Arduino 的時候,只知道 Serial.println() 可以在 console 中印出某些字串、變數的值等等,沒有去探究這是如何實現的,但玩了一陣子後,瞭解到 USB 轉 TTL 的事實、TXD,RXD 背後的UART protocol 是怎麼運作的。然而,即便了解了背後的原因,但因為Arduino 把整個東西做掉。因此,當時我沒機會了解得非常深入,應該說沒機會真的去親身實作背後原理。
因為這次專案的機會,雖然說的確不會用到 UART 的部分 (松翰的 SN-LINK-V3 處理了燒錄的部分),不過前輩希望我把所有功能都玩一遍,因此就有了頭一次自己寫 UART 的機會。看了 datasheet,松翰這份文件內 sample code 的部分寫得比較不完整,而且有些地方是有問題的,還有一些細節的部分沒有交代的得很清楚。因此,我必須自己做實驗來驗證。
不知道是不是自己功力還不夠,我在測試 SN8F5701 這顆埋控有關於 UART 部分的時候,發現這顆竟然不是全雙工 (Full Duplex),但提供的文件上卻清楚的表示 UART 是全雙工!接下來就來分享我做實驗的過程。透過像是 coolterm 這樣的串口軟體工具 (Arduino serial console 的概念),我希望在鍵盤上輸入文字或是數字 (RXD),SN8F5701 能夠吐回來到 console 中顯示 (TXD)。
開始進行測試
UART 不論哪顆埋控一定都會有一個或多個 buffer 來接目標物 (不論是要送出去抑或丟進來),SN8F5701 當然也不例外。S0BUF 就是那個 buffer,要用來接我鍵盤輸入的文字或數字,但也必須要使用它來接要丟出去的東西,但這樣豈不是會打架嗎?此時,我心想既然文件都肯定是全雙工了,或許有什麼東西是沒寫出來,但是能夠順利 work 的。
首先,我先做了個基本的輸送實驗,以下是部分的程式碼 (省略數字的部分):
稍微解釋一下上方的程式碼,uart_init() 作為 uart 功能的初始設定,tx, rx 分別寫成兩個一般的 function,chstring[] 這個字元陣列是在開機 (reset) 後,會先顯示在 coolterm console 上的字串。
我認為,這裡最值得分享的還是 while (TI0==0) {},這個寫法比較不直覺,不過卻很必要。TI0 (和 RI0))是個 flag,如果 S0BUF 滿了,也就是說東西可以丟出去 (或接進來),TI0 (或是 RI0))會變成 1,必須手動清除 flag 將其變回 0 才能再收 (或丟)下一批東西。之所以要這樣寫,是因為 MCU 的速度太快,還來不及清除 flag 就已經跑過 TI0=0 這個指令了。
以上方的程式碼舉例,如果沒有 while (TI0==0) {} 這行,因為 MCU (fcpu)太快,可能再 S0BUF = da 後,在 TI0 還沒變成 1 之前,就已經先跑完了 TI0=0 這行程式碼,導致 TI0 清不掉,無法繼續有效運作。雖然,這個寫法是對,但卻很不合理。一方面,我花了所有 main 的資源,只把我的輸入印在 console 上。在做實驗上是可以這樣做,但實際與其他功能配合運用時就是個災難;另一方面,大家應該很疑惑在 rxreceivedata() 裡面,WDTR = 0x5A 的用意。
事實上,若不加這行而在有開 watch dog 的情形下,只要使用者沒有在 watch dog 所設定的時間內,真的輸入東西 (相信我,一般人是不可能跟上的),系統就會直接判定當機而進行 reset,而為了避免這種窘況,WDTR = 0x5A 就是用來清 watch dog 的指令,好讓 rxreceivedata() 可以順利等下去,不過說真的沒有人會這樣寫,沒東西來就卡在那個 while 裡面一直等,什麼都不用做了。
另外,我用了個 flag (enableWrite)讓 txsenddata() 在沒有東西進來的情況不會送東西出去。
既然,上述兩個 function 都放在 main 的情形這麼不優雅,是時候派 ISR 上場了!UART 當然也有 interrupt 的機制,據 Winson 說:「UART 通常會把 rx 放在 ISR,tx 放在 main」我覺得很合理,試想如果「把 rx 放在 main,tx 放在 ISR」,那上述的問題就沒獲得解法。寫法變更如下 (程式碼片段):
rx 在 ISR,文件明確表示啟動的機制是 RI0 變 1 時便 trigger ISR,意思是說我不用用 while 再繼續等,進來的時候把 RI0 flag 清掉就好。因為有著全雙工,所以不用太在意收丟交錯的情形,有東西進來把 enableWrite flag 打開就行。
小結
我在 ISR 中多加了「 if statement」,這是因為我發現,如果不加, tx 傳到螢幕上的字元陣列會漏字,於是我懷疑UART不是全雙工,寫了那個 if 便是把它當作「半雙工」的意思 (收的時候不能丟,丟的時候不能收),果然這樣寫後,鍵盤輸入什麼,螢幕就會顯示什麼了!
*UART 的功能測試就告一個段落,下一篇將接著講 ADC。
- 【NB-IoT】菜鳥Maker輕鬆上手DSI2598開發板 - 2019/12/13
- 【Maker電子學】Modbus over TCP 實作(上) - 2019/11/28
- 【Maker電子學】Modbus RTU的傳輸資料格式 - 2019/09/18
訂閱MakerPRO知識充電報
與40000位開發者一同掌握科技創新的技術資訊!