一、USART簡介
通用同步異步收發(fā)器(Universal Synchronous Asynchronous Receiver and Transmitter)是一個串行通信設(shè)備,可以靈活地與外部設(shè)備進(jìn)行全雙工數(shù)據(jù)交換。有別于 USART 還有一個 UART(Universal Asynchronous Receiver and Transmitter)烟很,它是在 USART 基礎(chǔ)上裁剪掉了同步通信功能,只有異步通信。簡單區(qū)分同步和異步就是看通信時需不需要對外提供時鐘輸出灵嫌,我們平時用的串口通信基本都是 UART。
串行通信一般是以幀格式傳輸數(shù)據(jù)葛作,即是一幀一幀的傳輸寿羞,每幀包含有起始信號、數(shù)據(jù)信息赂蠢、停止信息绪穆,可能還有校驗信息。USART 就是對這些傳輸參數(shù)有具體規(guī)定,當(dāng)然也不是只有唯一一個參數(shù)值玖院,很多參數(shù)值都可以自定義設(shè)置菠红,只是增強它的兼容性。
USART 滿足外部設(shè)備對工業(yè)標(biāo)準(zhǔn) NRZ 異步串行數(shù)據(jù)格式的要求难菌,并且使用了小數(shù)波特率發(fā)生器试溯,可以提供多種波特率,使得它的應(yīng)用更加廣泛郊酒。USART 支持同步單向通信和半雙工單線通信遇绞;還支持局域互連網(wǎng)絡(luò) LIN、智能卡(SmartCard)協(xié)議與 lrDA(紅外線數(shù)據(jù)協(xié)會) SIR ENDEC 規(guī)范燎窘。
USART 在 STM32 應(yīng)用最多莫過于“打印”程序信息摹闽,一般在硬件設(shè)計時都會預(yù)留一個 USART 通信接口連接電腦,用于在調(diào)試程序是可以把一些調(diào)試信息“打印”在電腦端的串口調(diào)試助手工具上褐健,從而了解程序運行是否正確付鹿、如果出錯哪具體哪里出錯等等。
二铝量、引腳分布
STM32F103VET6 系統(tǒng)控制器有三個 USART 和兩個 UART倘屹,其中 USART1 和時鐘來源于 APB2 總線時鐘,其最大頻率為 72MHz慢叨,其他四個的時鐘來源于 APB1 總線時鐘纽匙,其最大頻率為 36MHz。UART 只是異步傳輸功能拍谐,所以沒有 SCLK烛缔、nCTS 和 nRTS 功能引腳。
三轩拨、中斷控制
四践瓷、新建工程
1. 打開 STM32CubeMX 軟件,點擊“新建工程”
2. 選擇 MCU 和封裝
3. 配置時鐘
RCC 設(shè)置亡蓉,選擇 HSE(外部高速時鐘) 為 Crystal/Ceramic Resonator(晶振/陶瓷諧振器)
選擇 Clock Configuration晕翠,配置系統(tǒng)時鐘 SYSCLK 為 72MHz
修改 HCLK 的值為 72 后,輸入回車砍濒,軟件會自動修改所有配置
4. 配置調(diào)試模式
非常重要的一步淋肾,否則會造成第一次燒錄程序后續(xù)無法識別調(diào)試器
SYS 設(shè)置,選擇 Debug 為 Serial Wire
五爸邢、USART1
5.1 參數(shù)配置
在 Connectivity
中選擇 USART1
設(shè)置樊卓,并選擇 Asynchronous
異步通信
波特率為
115200 Bits/s
。傳輸數(shù)據(jù)長度為 8 Bit
杠河。奇偶檢驗 None
碌尔,停止位 1
浇辜,接收和發(fā)送都使能
。5.2 配置NVIC
使能串口接收中斷
5.3 生成代碼
輸入項目名和項目路徑
選擇應(yīng)用的 IDE 開發(fā)環(huán)境 MDK-ARM V5
每個外設(shè)生成獨立的
’.c/.h’
文件不勾:所有初始化代碼都生成在 main.c
勾選:初始化代碼生成在對應(yīng)的外設(shè)文件唾戚。 如 GPIO 初始化代碼生成在 gpio.c 中柳洋。
點擊 GENERATE CODE 生成代碼
5.4 printf重定向
microlib
進(jìn)行了高度優(yōu)化以使代碼變得很小。它的功能比缺省 C 庫少颈走,并且根本不具備某些 ISO C 特性膳灶。 某些庫函數(shù)的運行速度也比較慢,如果要使用printf()立由,必須開啟
。
在 main.c 中添加如下頭文件和函數(shù)
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
/* USER CODE BEGIN 4 */
/**
* @brief 重定向c庫函數(shù)printf到USARTx
* @retval None
*/
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
/**
* @brief 重定向c庫函數(shù)getchar,scanf到USARTx
* @retval None
*/
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
}
/* USER CODE END 4 */
5.5 修改中斷回調(diào)函數(shù)
打開 stm32f1xx_it.c
中斷服務(wù)函數(shù)文件序厉,找到 USART1 中斷的服務(wù)函數(shù) USART1_IRQHandler()
中斷服務(wù)函數(shù)里面就調(diào)用了串口中斷處理函數(shù) HAL_UART_IRQHandler()
打開 stm32f1xx_hal_uart.c
文件锐膜,找到定時器中斷處理函數(shù)原型 HAL_TIM_IRQHandler()
,其主要作用就是判斷是哪個串口產(chǎn)生中斷弛房,清除中斷標(biāo)識位道盏,然后調(diào)用中斷回調(diào)函數(shù) HAL_UART_RxCpltCallback()
。
/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_GPIO_EXTI_Callback could be implemented in the user file
*/
這個函數(shù)不應(yīng)該被改變文捶,如果需要使用回調(diào)函數(shù)荷逞,請重新在用戶文件中實現(xiàn)該函數(shù)。
HAL_UART_RxCpltCallback()
按照官方提示我們應(yīng)該再次定義該函數(shù)粹排,__weak
是一個弱化標(biāo)識种远,帶有這個的函數(shù)就是一個弱化函數(shù),就是你可以在其他地方寫一個名稱和參數(shù)都一模一樣的函數(shù)顽耳,編譯器就會忽略這一個函數(shù)坠敷,而去執(zhí)行你寫的那個函數(shù);而 UNUSED(huart)
射富,這就是一個防報錯的定義膝迎,當(dāng)傳進(jìn)來的串口號沒有做任何處理的時候,編譯器也不會報出警告胰耗。其實我們在開發(fā)的時候已經(jīng)不需要去理會中斷服務(wù)函數(shù)了限次,只需要找到這個中斷回調(diào)函數(shù)并將其重寫即可而這個回調(diào)函數(shù)還有一點非常便利的地方這里沒有體現(xiàn)出來,就是當(dāng)同時有多個中斷使能的時候柴灯,STM32CubeMX會自動地將幾個中斷的服務(wù)函數(shù)規(guī)整到一起并調(diào)用一個回調(diào)函數(shù)卖漫,也就是無論幾個中斷,我們只需要重寫一個回調(diào)函并判斷傳進(jìn)來的定時器號即可弛槐。
接下來我們就在 stm32f1xx_it.c
這個文件的最下面添加 HAL_UART_RxCpltCallback()
/* USER CODE BEGIN 1 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
HAL_UART_Transmit(&huart1, (uint8_t *)Buffer, 1, 0xffff);
HAL_UART_Receive_IT(&huart1, (uint8_t *)Buffer, 1);
}
}
/* USER CODE END 1 */
5.6 添加全局變量
在 main.c 頭部添加全局變量 Buffer
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
/* USER CODE BEGIN PV */
uint8_t Buffer[1];
/* USER CODE END PV */
在 stm32f1xx_it.c 頭部聲明全局變量 Buffer
/* External variables --------------------------------------------------------*/
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN EV */
extern uint8_t Buffer[1];
/* USER CODE END EV */
5.7 添加串口接收中斷啟動函數(shù)
在 main.c 中懊亡,while 循環(huán)前,串口初始化后乎串,添加接收中斷開啟函數(shù)店枣,這樣在第一次接收到數(shù)據(jù)的時候才會觸發(fā)中斷速警。
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1, (uint8_t *)Buffer, 1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
5.8 HAL庫與標(biāo)準(zhǔn)庫代碼比較
STM32CubeMX 使用 HAL 庫生成的代碼:
uint8_t Buffer[1];
/**
* @brief USART1 Initialization Function
* @param None
* @retval None
*/
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
/**
* @brief This function handles USART1 global interrupt.
*/
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
HAL_UART_Transmit(&huart1, (uint8_t *)Buffer, 1, 0xffff);
HAL_UART_Receive_IT(&huart1, (uint8_t *)Buffer, 1);
}
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)Buffer, 1);
使用 STM32 標(biāo)準(zhǔn)庫的代碼:
/**
* @brief USART GPIO 配置,工作參數(shù)配置
* @param 無
* @retval 無
*/
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打開串口GPIO的時鐘
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
// 打開串口外設(shè)的時鐘
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
// 將USART Tx的GPIO配置為推挽復(fù)用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 將USART Rx的GPIO配置為浮空輸入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 配置串口的工作參數(shù)
// 配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
// 配置 針數(shù)據(jù)字長
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
// 配置校驗位
USART_InitStructure.USART_Parity = USART_Parity_No ;
// 配置硬件流控制
USART_InitStructure.USART_HardwareFlowControl =
USART_HardwareFlowControl_None;
// 配置工作模式,收發(fā)一起
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 完成串口的初始化配置
USART_Init(DEBUG_USARTx, &USART_InitStructure);
// 串口中斷優(yōu)先級配置
NVIC_Configuration();
// 使能串口接收中斷
USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
// 使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
}
/**
* @brief 配置嵌套向量中斷控制器NVIC
* @param 無
* @retval 無
*/
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中斷控制器組選擇 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置USART為中斷源 */
NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
/* 搶斷優(yōu)先級*/
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 子優(yōu)先級 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中斷 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置NVIC */
NVIC_Init(&NVIC_InitStructure);
}
// 串口中斷服務(wù)函數(shù)
void DEBUG_USART_IRQHandler(void)
{
uint8_t ucTemp;
if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
{
ucTemp = USART_ReceiveData(DEBUG_USARTx);
USART_SendData(DEBUG_USARTx,ucTemp);
}
}
MX_USART1_UART_Init();
對應(yīng) USART_Config();NVIC_Configuration();
HAL_UART_Init(&huart1)
對應(yīng) USART_Init(DEBUG_USARTx, &USART_InitStructure)
HAL_UART_Receive_IT(&huart1, (uint8_t *)Buffer, 1);
對應(yīng) USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
六鸯两、注意事項
用戶代碼要加在 USER CODE BEGIN N
和 USER CODE END N
之間闷旧,否則下次使用 STM32CubeMX 重新生成代碼后,會被刪除钧唐。
? 由 Leung 寫于 2021 年 1 月 15 日
? 參考:STM32CubeMX系列教程5:串行通信(USART)
STM32CubeMX實戰(zhàn)教程(六)——串口通信(為啥你的中文會亂碼)
《嵌入式-STM32開發(fā)指南》第二部分 基礎(chǔ)篇 - 第6章串口通信(HAL庫)
【STM32Cube_07】使用USART發(fā)送和接收數(shù)據(jù)(中斷模式)