2019年9月1日 星期日

STM32CubeMX之串列中斷--STM32F205RBT6在MDK5的應用範例

站長在過去的文章中

有大量提及串列通訊的概念

不論是在機械手臂的控制篇或是 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 的用法

也請繼續期待!

沒有留言:

張貼留言