STM32CubeMX學(xué)習(xí)筆記(6)——USART串口使用

一、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 NUSER 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ù)(中斷模式)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末忙灼,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子钝侠,更是在濱河造成了極大的恐慌该园,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帅韧,死亡現(xiàn)場離奇詭異里初,居然都是意外死亡,警方通過查閱死者的電腦和手機忽舟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門双妨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人叮阅,你說我怎么就攤上這事刁品。” “怎么了浩姥?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵挑随,是天一觀的道長。 經(jīng)常有香客問我及刻,道長镀裤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任缴饭,我火速辦了婚禮暑劝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘颗搂。我一直安慰自己担猛,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布丢氢。 她就那樣靜靜地躺著傅联,像睡著了一般。 火紅的嫁衣襯著肌膚如雪疚察。 梳的紋絲不亂的頭發(fā)上蒸走,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機與錄音貌嫡,去河邊找鬼比驻。 笑死该溯,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的别惦。 我是一名探鬼主播狈茉,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼掸掸!你這毒婦竟也來了氯庆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤扰付,失蹤者是張志新(化名)和其女友劉穎堤撵,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體羽莺,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡粒督,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了禽翼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡族跛,死狀恐怖闰挡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情礁哄,我是刑警寧澤长酗,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站桐绒,受9級特大地震影響夺脾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茉继,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一咧叭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧烁竭,春花似錦菲茬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至终吼,卻和暖如春镀赌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背际跪。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工商佛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留喉钢,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓威彰,卻偏偏與公主長得像出牧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子歇盼,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容