# STM32F429 Discovery kit ## 3. UART (Universal Asynchronous Receiver/Transmitter) [TOC] :::success 用一條**USB**線就可以讓你的stm32板子跟電腦對話了嘎~ ::: **本範例使用環境:** * OS : Windows 10 * IDE: eclipse(luna版本) * 燒錄軟體: 內建ST-Link * Terminal: hercules_3-2-8.exe * 開發板:stm32f429 Discovery kit ## 1. 基本Transmit / Receive ### (1) Receive from PC Serial Input 1. 加入 `HAL_UART_out.c`, `HAL_UART_out.h`兩個檔案(複製加入到`src`,`include`資料夾內) 2. 在`main.c`內加入: ``` C= #include "HAL_UART_out.h" ``` 3. 在`main.c`內加入: ``` C= STM32f4_UART_Init(&UartHandle) ``` 4. 可使用兩種方法: (1) 一般receive法 ``` C= if(HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)!=HAL_OK){ Error_Handle(); } ``` Error_Handle();內有個while(1){}無窮迴圈,若跑進來則永遠困在這。 (可寫個LED toggle/每0.5秒來debug 要注意的是:根據`uint16_t Size`決定**輸入資料長度**,若長度**小於**Size,則視為失敗,很可能會return `HAL_Timeout` (2) **interrupt** Receive法 :::info **Pros: CPU不用卡在Receive等,可以去做其他事情。** ::: * Steps: 1. 在`HAL_UART_out.c`的`STM32f4_UART_Init()`內補上: **(1)Priority** **(2)Enable** ``` C= HAL_NVIC_SetPriority(USART1_IRQn, 6, 2); HAL_NVIC_EnableIRQ(USART1_IRQn); ``` 2. `main.c`內改成 HAL_UART_Receive_**IT** ``` C= if(HAL_UART_Receive_IT(&UartHandle, (uint8_t *)aRxBuffer, RXBUFFERSIZE) != HAL_OK){} ``` 3. 在`HAL_UART_Receive_IT`內部根據不同的Flag,有不同的`__HAL_UART_ENABLE_IT`: 一般傳輸的話是進入`RXNE`這個Flag ``` C= /* Enable the UART Parity Error Interrupt */ __HAL_UART_ENABLE_IT(huart, UART_IT_PE); /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */ __HAL_UART_ENABLE_IT(huart, UART_IT_ERR); /* Process Unlocked */ __HAL_UNLOCK(huart); /* Enable the UART Data Register not empty Interrupt */ __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE); ``` 接著`IRQHandler`會啟動這個function: ```c= void USART1_IRQHandler(void) { HAL_UART_IRQHandler(&UartHandle); } ``` 4.接著`HAL_UART_IRQHandler()`內會再call`UART_Receive_IT(huart)`: ```c= /* UART in mode Receiver ---------------------------------------------------*/ if((tmp1 != RESET) && (tmp2 != RESET)) { UART_Receive_IT(huart); } ``` 5.最後讀完後,進入`HAL_UART_RxCpltCallback(huart);` ```c= if(--huart->RxXferCount == 0U) { __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE); /* Disable the UART Parity Error Interrupt */ __HAL_UART_DISABLE_IT(huart, UART_IT_PE); /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */ __HAL_UART_DISABLE_IT(huart, UART_IT_ERR); /* Rx process is completed, restore huart->RxState to Ready */ huart->RxState = HAL_UART_STATE_READY; HAL_UART_RxCpltCallback(huart); return HAL_OK; } ``` Note: `stm32f4xx_hal_uart.c`本來就有`HAL_UART_RxCpltCallback`,但是前面Library寫 **__weak** 的部分:表示高度猜測**大部分Programmer會自己再寫一個Rx的Callback function**,所以為了**避免編譯衝突**,加上__weak使得Compiler會自動選擇正確的。 ```C= __weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { /* Prevent unused argument(s) compilation warning */ UNUSED(huart); /* NOTE: This function Should not be modified, when the callback is needed, the HAL_UART_TxCpltCallback could be implemented in the user file */ } ``` :::danger **注意:** ``` C= /* Process Locked */ __HAL_LOCK(huart); ``` 這個可能發生在系統認為RX有衝突行為,會直接Return回去,**不是HAL_OK**,所以可能會卡在{...}內 ::: :::success 有關SetPriority的部分: 1.若現在**正執行一個interrupt service routine(ISR)**,又有人要中斷進來,若他的**優先權較高**,則CPU被抓去**先做優先權高者** 2.若他的**優先權比你低**,CPU繼續做完現正的ISR 3.若有兩個ISR的優先權一樣(比你低)在你後面排隊,他們的先後順序看他們的**SubPriority** 4.若3.的兩個ISR的第二優先權也一樣:照**時間上先後順序**排程 ::: ### (2) Transmit from STM32f429 board to PC 傳輸部分,使用已經寫好的API: 以下列出的都非常好用 (先不要用DMA相關的,會比較複雜) ```C= void print_u32d(unsigned int int32){ uint8_t print_cnt=0; sprintf(UsartTxBuffer_0, "%u\r\n", int32); while(UsartTxBuffer_0[print_cnt]!='\0'){print_cnt++;} HAL_UART_Transmit(&UartHandle, (uint8_t*)UsartTxBuffer_0, print_cnt, 5000); } void print_u8x(uint8_t int8){ uint8_t print_cnt=0; sprintf(UsartTxBuffer_0, "0x%02X\r\n", int8); while(UsartTxBuffer_0[print_cnt]!='\0'){print_cnt++;} HAL_UART_Transmit(&UartHandle, (uint8_t*)UsartTxBuffer_0, print_cnt, 5000); } void print_u32x(unsigned int int32){ uint8_t print_cnt=0; sprintf(UsartTxBuffer_0, "0x%08X\r\n", int32); while(UsartTxBuffer_0[print_cnt]!='\0'){print_cnt++;} HAL_UART_Transmit(&UartHandle, (uint8_t*)UsartTxBuffer_0, print_cnt, 5000); } void print_32d(int int32){ uint8_t print_cnt=0; sprintf(UsartTxBuffer_0, "%d\r\n", int32); while(UsartTxBuffer_0[print_cnt]!='\0'){print_cnt++;} HAL_UART_Transmit(&UartHandle, (uint8_t*)UsartTxBuffer_0, print_cnt, 5000); } void print_string(char str[]){ uint8_t print_cnt=0; sprintf(UsartTxBuffer_0, "%s", str); while(UsartTxBuffer_0[print_cnt]!='\0'){print_cnt++;} HAL_UART_Transmit(&UartHandle, (uint8_t*)UsartTxBuffer_0, print_cnt, 5000); } ``` ## 2. 範例程式 :::warning **提醒**:在使用模擬的Terminal console時,要記得: 1.baurate (此範例為9600) 2.STOP bit(此範例為1) 3.Parity bit(此範例為None) 等設定要跟你`UART_Init()`內的設定一樣才能有效傳輸喔! ::: ...待補上。 1. RXBufferSize : HAL_UART_out.h的define ```C= /* Buffer used for transmission and reception*/ uint8_t UsartTxBuffer_0[MaxPrintByte], UsartRxBuffer_0[MaxReceiveByte]; ``` 2.不要在底層打High-level的api (e.x. `print_string()`等 ```C= sprintf(TestChar, "AB%c", 'C'); HAL_UART_Transmit(huart, (char*)TestChar, 5, 5000); // 把'ABC' 印出到Terminal上 ``` 使用Rx Interrupt 流程: 1.HAL_UART_Receive_IT (in stm32f4xx_hal_uart.c) 2.UART_Receive_IT(in stm32f4xx_hal_uart.c) 內的 ```c= // Store huart->Instance->DR data (byte-by-byte) into pRxBufferPtr! *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FFU); ... // 重點!!after Executin Rx!!! Callback function HAL_UART_RxCpltCallback(huart); ``` 3. 這個沒用到!! ```c= __weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) ``` 4. 在助教自己打的 HAL_UART_OUT.c內 進入`HAL_UART_RxCpltCallback (in HAL_UART_OUT.c)` ```c= /** * @brief Rx Transfer completed callback * @param UartHandle: UART handle * @note This example shows a simple way to report end of DMA Rx transfer, and * you can add your own implementation. * @retval None */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { /* Set transmission flag: transfer complete*/ //UsartReady_0 = SET; UsartReady_Rx = SET; /* Turn LED4 on: Transfer in reception process is correct */ BSP_LED_On(LED4); } ``` ###### tags: `STM32f429 Discovery kit` `UART` `USART` `Micro controller unit` `MCU` `firmware`