前言:
今天我們學習STM32CubeMX串口的操作,以及HAL庫串口的配置,我們會詳細的講解各個模塊的使用和具體功能,並且基於HAL庫實現Printf函數功能重定向,UART中斷接收,本系列教程將HAL庫與STM32CubeMX結合在一起講解,使您可以更快速的學會各個模塊的使用
所用工具:
1、芯片: STM32F407ZET6
2、STM32CubeMx軟件
3、IDE: MDK-Keil軟件
4、STM32F1xx/STM32F4xxHAL庫
5、串口: 使用USART1 PA9,PA10
知識概括:
通過本篇博客您將學到:
STM32CubeMX創建串口例程
HAL庫UATR函數庫
重定義printf函數
HAL庫,UART中斷接收
HAL庫UATR接收與發送例程
工程創建
1設置RCC
- 設置高速外部時鐘HSE 選擇外部時鐘源
2設置串口
- 1點擊USATR1
- 2設置MODE爲異步通信(Asynchronous)
- 3基礎參數:波特率爲115200 Bits/s。傳輸數據長度爲8 Bit。奇偶檢驗無,停止位1 接收和發送都使能
- 4GPIO引腳設置 USART1_RX/USART_TX
- 5 NVIC Settings 一欄使能接收中斷
3設置時鐘
我的是 外部晶振爲8MHz
- 1選擇外部時鐘HSE 8MHz
- 2PLL鎖相環倍頻72倍
- 3系統時鐘來源選擇爲PLL
- 4設置APB1分頻器爲 /2
32的時鐘樹框圖 如果不懂的話請看《【STM32】系統時鐘RCC詳解(超詳細,超全面)》
4項目文件設置
- 1 設置項目名稱
- 2 設置存儲路徑
- 3 選擇所用IDE
5創建工程文件
然後點擊GENERATE CODE 創建工程
配置下載工具
新建的工程所有配置都是默認的 我們需要自行選擇下載模式,勾選上下載後復位運行
HAL庫UART函數庫介紹
UART結構體定義
UART_HandleTypeDef huart1;
UART的名稱定義,這個結構體中存放了UART所有用到的功能,後面的別名就是我們所用的uart串口的別名,默認爲huart1
可以自行修改
1、串口發送/接收函數
- HAL_UART_Transmit();串口發送數據,使用超時管理機制
- HAL_UART_Receive();串口接收數據,使用超時管理機制
- HAL_UART_Transmit_IT();串口中斷模式發送
- HAL_UART_Receive_IT();串口中斷模式接收
- HAL_UART_Transmit_DMA();串口DMA模式發送
- HAL_UART_Transmit_DMA();串口DMA模式接收
這幾個函數的參數基本都是一樣的,我們挑兩個講解一下
串口發送數據:
HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
功能:串口發送指定長度的數據。如果超時沒發送完成,則不再發送,返回超時標誌(HAL_TIMEOUT)。
參數:
- UART_HandleTypeDef *huart UATR的別名 如 : UART_HandleTypeDef huart1; 別名就是huart1
- *pData 需要發送的數據
- Size 發送的字節數
- Timeout 最大發送時間,發送數據超過該時間退出發送
舉例: HAL_UART_Transmit(&huart1, (uint8_t *)ZZX, 3, 0xffff); //串口發送三個字節數據,最大傳輸時間0xffff
中斷接收數據:
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
功能:串口中斷接收,以中斷方式接收指定長度數據。
大致過程是,設置數據存放位置,接收數據長度,然後使能串口接收中斷。接收到數據時,會觸發串口中斷。
再然後,串口中斷函數處理,直到接收到指定長度數據,而後關閉中斷,進入中斷接收回調函數,不再觸發接收中斷。(只觸發一次中斷)
參數:
- UART_HandleTypeDef *huart UATR的別名 如 : UART_HandleTypeDef huart1; 別名就是huart1
- *pData 接收到的數據存放地址
- Size 接收的字節數
舉例: HAL_UART_Receive_IT(&huart1,(uint8_t *)&value,1); //中斷接收一個字符,存儲到value中
2、串口中斷函數
- HAL_UART_IRQHandler(UART_HandleTypeDef *huart); //串口中斷處理函數
- HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); //串口發送中斷回調函數
- HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart); //串口發送一半中斷回調函數(用的較少)
- HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); //串口接收中斷回調函數
- HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//串口接收一半回調函數(用的較少)
- HAL_UART_ErrorCallback();串口接收錯誤函數
串口接收中斷回調函數:
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
功能:HAL庫的中斷進行完之後,並不會直接退出,而是會進入中斷回調函數中,用戶可以在其中設置代碼,
串口中斷接收完成之後,會進入該函數,該函數爲空函數,用戶需自行修改,
參數:
- UART_HandleTypeDef *huart UATR的別名 如 : UART_HandleTypeDef huart1; 別名就是huart1
舉例: HAL_UART_RxCpltCallback(&huart1){ //用戶設定的代碼 }
串口中斷處理函數
HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
功能:對接收到的數據進行判斷和處理 判斷是發送中斷還是接收中斷,然後進行數據的發送和接收,在中斷服務函數中使用
如果接收數據,則會進行接收中斷處理函數
/* UART in mode Receiver ---------------------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
UART_Receive_IT(huart);
}
如果發送數據,則會進行發送中斷處理函數
/* UART in mode Transmitter ------------------------------------------------*/
if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
{
UART_Transmit_IT(huart);
return;
}
3串口查詢函數
HAL_UART_GetState(); 判斷UART的接收是否結束,或者發送數據是否忙碌
舉例:
while(HAL_UART_GetState(&huart4) == HAL_UART_STATE_BUSY_TX) //檢測UART發送結束
USATR接收與發送
重新定義printf函數
- 在 stm32f4xx_hal.h 中包含#include <stdio.h>
#include "stm32f4xx_hal.h"
#include <stdio.h>
- 在 stm32f4xx_hal.c 中重寫fget和fput函數
-
/** * 函數功能: 重定向c庫函數printf到DEBUG_USARTx * 輸入參數: 無 * 返 回 值: 無 * 說 明:無 */ int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff); return ch; } /** * 函數功能: 重定向c庫函數getchar,scanf到DEBUG_USARTx * 輸入參數: 無 * 返 回 值: 無 * 說 明:無 */ int fgetc(FILE *f) { uint8_t ch = 0; HAL_UART_Receive(&huart1, &ch, 1, 0xffff); return ch; }
#define RXBUFFERSIZE 256
char RxBuffer[RXBUFFERSIZE];
while (1)
{
/* USER CODE END WHILE */
printf("Z小旋測試\n");
HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
之後便可以使用Printf函數
UART接收中斷
因爲中斷接收函數只能觸發一次接收中斷,所以我們需要在中斷回調函數中再調用一次中斷接收函數
具體流程:
3、進入接收中斷,接收完數據 進入中斷回調函數
4、修改HAL_UART_RxCpltCallback中斷回調函數,處理接收的數據,
5 回調函數中要調用一次HAL_UART_Receive_IT函數,使得程序可以重新觸發接收中斷
函數流程圖:
HAL_UART_Receive_IT(中斷接收函數) -> USART2_IRQHandler(void)(中斷服務函數) -> HAL_UART_IRQHandler(UART_HandleTypeDef *huart)(中斷處理函數) -> UART_Receive_IT(UART_HandleTypeDef *huart) (接收函數) -> HAL_UART_RxCpltCallback(huart);(中斷回調函數)
HAL_UART_RxCpltCallback函數就是用戶要重寫在main.c裏的回調函數。
代碼實現:
並在main.c中添加下列定義:
#include <string.h>
#define RXBUFFERSIZE 256 //最大接收字節數
char RxBuffer[RXBUFFERSIZE]; //接收數據
uint8_t aRxBuffer; //接收中斷緩衝
uint8_t Uart1_Rx_Cnt = 0; //接收緩衝計數
在main()主函數中,調用一次接收中斷函數
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);
/* USER CODE END 2 */
在while循環中添加:
while (1)
{
/* USER CODE END WHILE */
HAL_Delay(1000);
HAL_UART_RxCpltCallback(&huart1);
/* USER CODE BEGIN 3 */
}
在main.c下方添加中斷回調函數
/* USER CODE BEGIN 4 */
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
*/
if(Uart1_Rx_Cnt >= 255) //溢出判斷
{
Uart1_Rx_Cnt = 0;
memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff));
HAL_UART_Transmit(&huart1, (uint8_t *)"數據溢出", Uart1_Rx_Cnt,0xFFFF);
}
else
{
Uart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer; //接收數據轉存
if((Uart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)&&(Uart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D)) //判斷結束位
{
HAL_UART_Transmit(&huart1, (uint8_t *)&Uart1_RxBuff, Uart1_Rx_Cnt,0xFFFF); //將收到的信息發送出去
while(HAL_UART_GetState(&huart4) == HAL_UART_STATE_BUSY_TX) //檢測UART發送結束
Uart1_Rx_Cnt = 0;
memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff)); //清空數組
}
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1); //再開啓接收中斷
}
/* USER CODE END 4 */
發送數據被正常返回