STM32 UART串口通訊編程方法
阿新 • • 發佈:2018-05-04
STM32 串口編程 uart 在對通訊時間要求比較高的時候,就需要自己對UART的通訊底層直接進行操作。我以STM32單片機為例,講一下比較快速的UART編程方法。——其實不止是STM32這麽處理,我以前使用過51的單片機,TI的MSP單片機,三菱的16位單片機,都可以采用這種方法。
3. 自己增加的初始化,初始化變量,並打開接收中斷。
基本的處理思路如下:
1. UART接收的處理方法
打開UART的接收中斷,每收到一個字節就放到接收緩沖區,同時更新接收指針。當連續100ms沒有收到接收字符,則認為本次幀接收完畢,置位幀接收完成標誌,由主程序進行處理。
2. UART發送的處理方法
將需要發送的數據放到發送緩沖區,設置發送長度。然後發送第一個字節,並打開發送中斷。在發送中斷中判斷是否已經發送了指定長度的數據。如果沒有發送完成,則繼續發送;發送完成,則關閉發送中斷。
以上方法,說起來比較簡單,主要是容錯的處理,以及細節的考慮。以下我以STM32單片機為例進行說明。
1. 定義需要的變量
uint8_t gcRXDBuffer[50], gcRXDPointer, gcRXDLength; //接收的緩沖區、接收指針、接收的幀長度 uint8_t gcTXDBuffer[50], gcTXDPointer, gcTXDLength; //發送的緩沖區,發送指針,發送的長度 uint8_t gcInRXDMode, gcInTXDMode; //是否處於接收或發送的狀態標誌,在需要切入低功耗模式,或關閉其他功能時需要 |
2. 初始化UART寄存器,以LL庫為例,HAL庫也可以。其實這部分功能使用STM32CUBEMX自己生成就行,不用給自己編寫。
/* USART2 init function */ static void MX_USART2_UART_Init(void) { LL_USART_InitTypeDef USART_InitStruct; LL_GPIO_InitTypeDef GPIO_InitStruct; /* Peripheral clock enable */ LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART2); /**USART2 GPIO Configuration PA9 ------> USART2_TX PA10 ------> USART2_RX */ GPIO_InitStruct.Pin = LL_GPIO_PIN_9; GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; GPIO_InitStruct.Alternate = LL_GPIO_AF_4; LL_GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.Pin = LL_GPIO_PIN_10; GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; GPIO_InitStruct.Alternate = LL_GPIO_AF_4; LL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USART2 interrupt Init */ NVIC_SetPriority(USART2_IRQn, 0); NVIC_EnableIRQ(USART2_IRQn); USART_InitStruct.BaudRate = 9600; USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B; USART_InitStruct.StopBits = LL_USART_STOPBITS_1; USART_InitStruct.Parity = LL_USART_PARITY_NONE; USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX; USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; LL_USART_Init(USART2, &USART_InitStruct); LL_USART_DisableOverrunDetect(USART2); LL_USART_ConfigAsyncMode(USART2); LL_USART_Enable(USART2); } |
/************************************ *不帶流控的USART2函數**************** */ void uart2Init(void) { gcRXDPointer=0; gcRXDLength=0; gcTXDPointer=0; gcTXDLength=0; gcInRXDMode=0; gcInTXDMode=0; LL_USART_EnableIT_RXNE(USART2); //LL_USART_EnableIT_TXE(USART2); } |
4. 以上的初始化就完成了,下面看中斷處理函數的編程方法。
/** * @brief This function handles USART2 global interrupt / USART2 wake-up interrupt through EXTI line 26. */ void USART2_IRQHandler(void) { /* USER CODE BEGIN USART2_IRQn 0 */ uint8_t ucTemp; /* USER CODE END USART2_IRQn 0 */ /* USER CODE BEGIN USART2_IRQn 1 */ if(LL_USART_IsActiveFlag_RXNE(USART2)) { //如果是接收中斷 gcUartCounter=0; //全局變量,每次收到一個自己就清零,到100ms沒有更新認為接收完成。 ucTemp=LL_USART_ReceiveData8(USART2); gcRXDBuffer[gcRXDPointer++]=ucTemp; //接收到的數據放入緩沖區 gcInRXDMode=1; //當前在接收模式 } else if(LL_USART_IsActiveFlag_TXE(USART2)) { //如果是發送中斷 gcTXDPointer++; if(gcTXDPointer<gcTXDLength) { //還沒有發送完成,繼續發送 LL_USART_TransmitData8(USART2, gcTXDBuffer[gcTXDPointer]); } else { //發送完成了,復位發送的變量 gcTXDLength=0; gcTXDPointer=0; LL_USART_DisableIT_TXE(USART2); //關閉發送中斷 gcInTXDMode=0; //退出發送模式 delayms(1); //有流控時需要延時關閉流控,比如RTS,或者485中斷的發送引腳。 RTS_HIGH(); } } /* USER CODE END USART2_IRQn 1 */ } |
5. 在系統的1ms SysTick中斷中,判斷是否100ms沒有收到數據了。
gcUartCounter++; //這個變量在接收中斷中不斷清零 if(gcUartCounter>=100) { //100ms沒有收到數據了,如果有數據,則打包幀 gcUartCounter=100; gcInRXDMode=0; //退出接收模式 if(gcRXDPointer>=3) { //根據協議長度,3是可以改動的。 gcRXDLength=gcRXDPointer; //將長度放入gcRXDLength,由主程序處理 gcRXDPointer=0; } } |
6. 以上程序中,接收數據部分就完成了。在主程序,或主業務中,判斷gcRXDLength就知道是否有數據需要處理。
7. 在需要發送數據的時候:
/************************************ 清空發送緩沖區的函數,需要重新組織發送時調用。*/ void TxdClearBuff(void) { gcTXDPointer=0; gcTXDLength=0; } /************************************ 如果需要多次組織數據,就一次次調用Push函數,將發送數據送入發送緩沖區。*/ void TxdPushToBuff(uint8_t *buffer, unsigned int length) { memcpy(gcTXDBuffer+gcTXDLength, buffer, length); gcTXDLength+=length; } /************************************ 組織完數據後,調用TxdSend,進行發送。*/ void TxdSend(void) { LL_USART_TransmitData8(USART2, gcTXDBuffer[0]); gcTXDPointer=0; LL_USART_EnableIT_TXE(USART2); //打開發送中斷。 gcInTXDMode=1; //進入發送模式。 } |
以上發送需要使用3個函數,有些復雜。如果你一次就能將數據組織完成,就可以寫簡單點。
在對實時處理要求更嚴格的時候,會在接收中斷中直接處理幀頭的判斷(是否是正確的幀頭,不是則接收指針直接清零),並根據幀長度字節,判斷接收是否完成,然後直接調用通訊處理函數。這樣的處理方法最快速,但封裝不好,不易維護。不是必須的時候,不建議這麽使用。
STM32 UART串口通訊編程方法