# 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`