站長在過去的文章中
有大量提及串列通訊的概念
不論是在機械手臂的控制篇或是 Modbus 的介紹中
而在 STM32 環境裡只有寫法變成了函式庫的呼叫用法
其他的概念都一樣
只要瞭解函式庫所代表的動作含意
寫起來就跟以往的串列中斷沒甚麼兩樣
延續上一個程式
我們開啟 Hello.ioc 將 Connectivity -> USART3 中
Configuration -> NVIC Settings 的 Enable 勾選
重新按下右上角的 GENERATE CODE
此時串列中斷的功能就被開啟了
而且在 GENERATE CODE 之後
我們可以發現原來寫在 USER CODE 裡的程式碼都被保留下來
並不會被新產生的程式碼所覆蓋清除
如此我們可以放心地將板子上的功能一一慢慢驗證
當問題發生時也容易釐清是哪一個新加入的功能導致誤動作
儘量不要囫圇吞棗一下子把所有的功能都打開
尤其在面對不熟悉的介面或很多周邊整合應用時
常常會發生某個功能設定好就導致另一個功能異常的情況
特別是周邊功能有優先權衝突的時候
慢慢加入功能可以快速且有效地釐清問題點
建議大家可以用這樣的方式進行程式撰寫
==========================================================
/* USER CODE BEGIN PV */
uint8_t Enter[] = { 0x0d, 0x0a };
uint8_t Hello[] = { "Hello World !!!" };
uint8_t uart3_read_buffers[10];
uint8_t uart3_conv;
uint8_t uart3_read_cnt=0;
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
HAL_UART_Transmit (&huart3, Enter, sizeof(Enter), 0xffff);
HAL_UART_Transmit (&huart3, Hello, sizeof(Hello)-1, 0xffff);
HAL_UART_Transmit (&huart3, Enter, sizeof(Enter), 0xffff);
HAL_Delay(1000);
HAL_UART_Receive_IT (&huart3,&uart3_conv,1);
/* USER CODE END 2 */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
UNUSED(huart);
uart3_read_buffers[uart3_read_cnt]=uart3_conv;
uart3_read_cnt++;
HAL_UART_Receive_IT (&huart3,&uart3_conv,1);
if (uart3_read_cnt>=10 || uart3_conv==0x0a)
{
HAL_UART_Transmit_IT (&huart3,uart3_read_buffers,uart3_read_cnt);
uart3_read_cnt=0;
}
}
/* USER CODE END 4 */
上一篇文章利用 while 迴圈的輪詢
讓 UART3 不斷送出 Hello World !!! 的資料
這一次我們將 Hello World !!! 搬到 /* USER CODE BEGIN 2 */ 的位置
清空原來 while 迴圈裡的程式 (/* USER CODE BEGIN 3 */的位置)
並加入串列接收的中斷功能 HAL_UART_Receive_IT (&huart3,&uart3_conv,1)
這是啟動從 UART3_RX 收到一個 Byte 的資料後會發生中斷的功能
收到的資料會放在 uart3_conv 這個變數上
請注意這個函式所接受的資料參數是指標型態
因此呼叫的時候我寫成 &uart3_conv
就是把收到的資料放在 uart3_conv 的位址上
任何變數前面加上 & 的符號就是代表該變數在程式裡的位址 (指標的概念)
當中斷發生時,系統會自動執行
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 這個函式
我把函式放在 /* USER CODE BEGIN 4 */ 的位置
接下來的程式裡要傳達幾個概念:
第一個是 UNUSED(huart)
這是避免編譯器出現未使用警告
詳細的發生原因可以參考這篇文章的說明
第二個是接收資料並重啟中斷的寫法
uart3_conv這個變數會儲存最新一個 Byte 的串列資料
在下一個 Byte 進來之前
我們必須將裡面的資料搬移到資料陣列中 uart3_read_buffers[]
等待接收到滿足條件的資料後才進行資料分析
並且重新啟動串列接收中斷
才不會漏掉即將進來的下一個 Byte 資料
第三個是判斷資料與回傳資料的時機
這個範例是將儲存在資料陣列中的資料回傳
當作是滿足指令條件的回應動作
而指令就是收到換行符號
然而資料陣列的長度只有 10 Bytes
超過陣列長度的資料除非覆蓋掉原來的資料繼續儲存
不然就會產生誤動作 (更改到其他變數用記憶體的資料)
因此程式才會在滿足陣列長度時先回傳資料
並將計數歸零以重新接收新一輪的資料
直到有新的換行符號出現為止
而且回傳的函式也用了中斷寫法
當傳送完成後系統會先進到
HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) 這個函式中
如果你有需要判斷傳送是否完成
可以在 HAL_UART_TxCpltCallback 中加入判斷式及對應動作
==========================================================
使用中斷寫法的好處
就是系統不會一直等待串列動作完成
而把收發資料的動作交給串列周邊自己完成
等收發完成後系統再接手
而等待收發完成的這段時間
系統可以繼續做其他的事
比方說繼續量測電壓
繼續驅動馬達運轉......
諸如此類
不過
即便以中斷寫法來寫
進入中斷函式到離開還是需要一些時間
這段時間還是算占用中斷資源
如果在這段時間有其他中斷進來
就會排在後面或直接被捨棄掉
利用 AccessPort 做了一下實驗
將六組帶有換行符號的字串不間斷地連續送給 MCU
並不是每一次都可以六組完整地回傳給 AccessPort
最差的情況只回傳了三組
換句話說
串列還是要考慮進入中斷後重新啟動所花費的時間
收與發還是要有一定的時間間隔
才能確保資料的收發不會漏掉
==========================================================
為了證明收與發互搶中斷資源的問題
特別將資料陣列的長度改成 100
並且讓收與發的字串長度增加到了 70~80 Bytes
再用 AccessPort 連續發送六組資料
這次發現不管重複發送多少次
六組資料都完整地被回傳
當回傳的中斷執行間隔被拉長
收發的穩定度就提高了
因為發送資料的頻率降低
占用中斷的時間也相對變少
也不容易發生重複排程而被捨棄的問題
實驗後才發現
原來不是資料送得少才穩定
反而是送得多才穩定
送得多代表大部分的動作交給串列周邊處理
真正占用到 MCU 的時間變少了
系統才有充裕的時間處理更多的事
這也才是使用中斷的精華所在
希望這篇文章能對您有些幫助
有任何問題或是我有說錯的地方
也請不吝指教
接下來要引入 DMA 的用法
也請繼續期待!
沒有留言:
張貼留言