介紹
串口(UART通用異步收發(fā)器,TTL)通訊是一種設備間的串行全雙工通訊方式。由于UART是異步傳輸,沒有傳輸同步時鐘,為了保證數(shù)據(jù)的正確性,UART采用16倍數(shù)據(jù)波特率的時鐘進行采樣。因為它簡便捷,因此大部分電子設備都支持該通訊方式工程師在調(diào)試設備時也經(jīng)常使用該方式輸出調(diào)試信息。
本文詳細的介紹如何來編寫一個串口收發(fā)程序,我們采用常用的收發(fā)邏輯,發(fā)送直接編寫函數(shù)進行實現(xiàn),而接收使用中斷進行完成。接收中斷使用接收到一個字節(jié)和一幀數(shù)據(jù)兩種中斷觸發(fā)方式。
USART中斷
USART 有多個中斷請求事件。
之所以介紹這個USART中斷請求,是因為很多人在初學階段,對串口怎么判斷串口中斷的狀態(tài)不太了解,所以我這里重點來介紹一下。
一般在我們開始和配置完串口中斷后,進入串口中斷處理程序的情況會有很多,我們也可以自己選擇打開哪些串口中斷情況。一般情況下,我們在接受時主要使用的中斷事件標志是RXNE和IDLE。
RXNE是接收中斷,每接收一個字節(jié)都會出發(fā)這個中斷,也是我們用的最頻繁的中斷請求。
IDLE 是空閑中斷,每接收完一幀數(shù)據(jù),總線就會暫時空閑,就會觸發(fā)這個中斷。
串口狀態(tài)
串口的狀態(tài)可以通過狀態(tài)寄存器 USART_SR 讀取。USART_SR 的各位描述如下:
這里我們關注一下兩個位,第 5、6 位 RXNE 和 TC。
RXNE(讀數(shù)據(jù)寄存器非空),當該位被置 1 的時候,就是提示已經(jīng)有數(shù)據(jù)被接收到了,且可以讀出來了。這時候我們要做的就是盡快去讀取 USART_DR,通過讀 USART_DR 可以該位清零,也可以向該位寫 0,直接清除。
TC(發(fā)送完成),當該位被置位的時候,表示 USART_DR 內(nèi)的數(shù)據(jù)已經(jīng)被發(fā)送完成了。果設置了這個位的中斷,則會產(chǎn)生中斷。該位也有兩種清零方式:
- 讀 USART_SR,USART_DR。
- 直接向該位寫 0。
實例
需求分析
本項目主要編寫一個串口收發(fā)的實例。使用STM32F103C8T6充當MCU,在PC上使用串口調(diào)試助手充當上位機。每次PC向MCU下發(fā)一幀數(shù)據(jù), MCU每接收一個字節(jié)數(shù)據(jù),檢查一下數(shù)據(jù)中是否有指令0x23,當接收到指令0x23的時候,MCU向上位機發(fā)送“PC”。當一幀數(shù)據(jù)接收完畢后,MCU向上位機發(fā)送“Receive a frame data”.
串口初始化
串口初始化的一般步驟可以總結(jié)為如下幾個步驟:
- 串口時鐘使能,GPIO 時鐘使能。
- 設置引腳復用器映射:調(diào)用 GPIO_PinAFConfig 函數(shù)。
- GPIO 初始化設置:要設置模式為復用功能。
- 串口參數(shù)初始化:設置波特率,字長,奇偶校驗等參數(shù)。
- 開啟中斷并且初始化 NVIC,使能中斷(如果需要開啟中斷才需要這個步驟)。
- 使能串口。
#include "usart.h"
#include
#include "stm32f1xx_hal.h
UART_HandleTypeDef huart3
void MX_USART3_UART_Init(void)
huart3.Instance = USART3;
huart3.Init.BaudRate = 115200;
huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;
huart3.Init.Mode = UART_MODE_TX_RX;
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart3.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart3) != HAL_OK)
{
Error_Handler()
__HAL_UART_ENABLE_IT(&huart3,UART_IT_RXNE);//接收中斷使能
__HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);//空閑中斷使能
}
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(uartHandle->Instance==USART3)
{
__HAL_RCC_USART3_CLK_ENABLE()
__HAL_RCC_GPIOB_CLK_ENABLE();
/**USART3 GPIO Configuration
PB10 ------> USART3_TX
PB11 ------> USART3_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct)
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
HAL_NVIC_SetPriority(USART3_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(USART3_IRQn);
我們平時需要改的其實就是串口的一些參數(shù)配置。
- BaudRate:波特率
- WordLength;:字長
- StopBits:停止位
- Parity:奇偶校驗
- Mode:收/發(fā)模式設置
- HwFlowCtl:硬件流設置
- OverSampling:過采樣設置
串口發(fā)送
串口發(fā)送這里使用的非中斷發(fā)送方式。
/*******************************************************************************
* @函數(shù)名稱 USART_Send
* @函數(shù)說明 發(fā)送信息
* @輸入?yún)?shù) _UART:串口號
data:要發(fā)送的信息的首地址
len:發(fā)送的長度
* @輸出參數(shù) 無
* @返回參數(shù) 無
*******************************************************************************/
void USART_Send(USART_TypeDef *_UART,uint8_t *data,uint8_t len)
{
for(int i;i
主要使用的是HAL_UART_Transmit(&huart3,&Res,1,0Xffff);
這是一個阻塞的發(fā)送函數(shù),無需重復判斷串口是否發(fā)送完成。發(fā)送每個字符,直到遇空字符才停止發(fā)送。其中第一個參數(shù)是串口號,第二個參數(shù)是要發(fā)送的數(shù)據(jù)起始地址,第三個是要發(fā)送的數(shù)據(jù)長度,第四個超時時間(超過此長度仍未發(fā)送成功則阻塞完畢,停止發(fā)送,函數(shù)執(zhí)行完畢)。
串口接收
這里串口接收使用的是中斷的方式。
中斷的類別在文章的最上邊已經(jīng)介紹過。我們在初始化時設定觸發(fā)中斷的類型。本文中設置的
__HAL_UART_ENABLE_IT(&huart3,UART_IT_RXNE);//接收中斷使能
__HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);//空閑中斷使能
代表只有接收數(shù)據(jù)和空閑中斷會觸發(fā)。
在stm32f1xx_it.c中有我們的串口中斷處理函數(shù)。我們將這個函數(shù)進行重構(gòu)。
void USART3_IRQHandler(void)
{
uint8_t Res;
if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_RXNE)!=RESET
{
HAL_UART_Receive(&huart3,&Res,1,0Xffff);
if(Res==0x23)
printf("PC");
}
else if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE)!=RESET)//空閑中斷(代表這一幀數(shù)據(jù)傳輸完了)
{
printf("Receive a frame data.");
__HAL_UART_CLEAR_IDLEFLAG(&huart3);
}
這里面的幾個重點,我們來一一介紹。
首先是判斷標志位,我們使用HAL庫中的__HAL_UART_GET_FLAG()函數(shù),里面有兩個參數(shù),前者是串口句柄,后者是具體哪個標志位。
if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_RXNE)!=RESET)用來檢測是否檢測到有單個字節(jié)的中斷。
if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE)!=RESET)用來檢測是否有空閑中斷(代表這一幀數(shù)據(jù)傳輸完了)。
重定向printf和scanf
還有一點需要注意的,使用 fput 和 fgetc 函數(shù)達到重定向 C 語言標準庫輸入輸出函數(shù)必須在 MDK 的工程選項把“Use MicroLIB”勾選上, MicoroLIB 是缺省 C 庫的備選庫,它對標準 C 庫進行了高度優(yōu)化使代碼更少,占用更少資源
為使用 printf、 scanf 函數(shù)需要在文件中包含 stdio.h 頭文件。
/**
* 函數(shù)功能: 重定向c庫函數(shù)printf
* 輸入?yún)?shù): 無
* 返 回 值: 無
* 說 明:無
*/
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
/**
* 函數(shù)功能: 重定向c庫函數(shù)getchar,scanf
* 輸入?yún)?shù): 無
* 返 回 值: 無
* 說 明:無
*/
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart3, &ch, 1, 0xffff);
return ch;
}
效果
-
PC下發(fā):11 22 33 44
-
PC下發(fā):12 23 34 45
-
收發(fā)器
+關注
關注
10文章
3428瀏覽量
106008 -
串口
+關注
關注
14文章
1554瀏覽量
76528 -
uart
+關注
關注
22文章
1235瀏覽量
101404
發(fā)布評論請先 登錄
相關推薦
評論