原文:http://www.javashuo.com/article/p-kdcsduhv-hc.htmlgit
以前的一篇文章中我爲了能夠實現USART接收任意長度的數據,對HAL的庫進行了修改,能夠實現接收以0x0a結尾的任意長度數據,即認爲接收到0x0a時接收結束,見連接:HAL USART接收任意長度。
然而,上述這種方法並不合適,原則上HAL庫通常不去修改,不便於其餘人移植程序,下降了程序中庫的適用性,這是很很差的習慣,因此這種方法並不可取。
後查資料得知STM32中還能夠利用DMA的方式實現串口的任意長度數據的接收,故開始學習DMA+串口接收任意長度的數據這種方式。github
首先,第一步都是進行時鐘樹的配置,配置好系統的時鐘,不一樣的芯片配置不一樣的時鐘頻率,如圖。
接着,配置USART1,選擇異步asynchronous,軟件自動配置了PA9和PA10管腳。
而後,繼續添加USART1的發送和接收DMA,其他默認便可。
接着,勾選上USART1的中斷使能。
最後,生成MDK-ARM V5版本環境的程序。
數組
//添加變量,爲何用關鍵字volatile見連接:[連接](http://blog.csdn.net/u014470361/article/details/78830147) volatile uint8_t rx_len=0; volatile uint8_t recv_end_flag=0; uint8_t rx_buffer[200]; static void MX_USART1_UART_Init(void) { 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(__FILE__, __LINE__); } //上面的usart配置代碼爲cubemx自動生成的,在下方添加使能idle中斷和打開串口DMA接收語句 __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);//使能idle中斷 HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//打開DMA接收,數據存入rx_buffer數組中。 }
接下來修改串口中斷函數。緩存
void USART1_IRQHandler(void) { uint32_t tmp_flag = 0; uint32_t temp; tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //獲取IDLE標誌位 if((tmp_flag != RESET))//idle標誌被置位 { __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除標誌位 temp = huart1.Instance->SR; //清除狀態寄存器SR,讀取SR寄存器能夠實現清除SR寄存器的功能 temp = huart1.Instance->DR; //讀取數據寄存器中的數據 HAL_UART_DMAStop(&huart1); // temp = hdma_usart1_rx.Instance->NDTR;// 獲取DMA中未傳輸的數據個數,NDTR寄存器分析見下面 rx_len = BUFFER_SIZE - temp; //總計數減去未傳輸的數據個數,獲得已經接收的數據個數 recv_end_flag = 1; // 接受完成標誌位置1 } HAL_UART_IRQHandler(&huart1); }
DMA通道結構體中定義了NDTR寄存器,那爲何是未傳輸的數據數呢,STM32的中文手冊給出了該寄存器的具體說明。markdown
typedef struct { __IO uint32_t CR; /*!< DMA stream x configuration register */ __IO uint32_t NDTR; /*!< DMA stream x **number of data register** */ __IO uint32_t PAR; /*!< DMA stream x peripheral address register */ __IO uint32_t M0AR; /*!< DMA stream x memory 0 address register */ __IO uint32_t M1AR; /*!< DMA stream x memory 1 address register */ __IO uint32_t FCR; /*!< DMA stream x FIFO control register */ } DMA_Stream_TypeDef;
接着,編寫主函數中串口中斷的處理函數。異步
int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_DMA_Init(); MX_USART1_UART_Init(); while (1) { if(recv_end_flag ==1) { printf("rx_len=%d\r\n",rx_len);//打印接收長度 HAL_UART_Transmit(&huart1,rx_buffer, rx_len,200);接收數據打印出來 for(uint8_t i=0;i<rx_len;i++) { rx_buffer[i]=0;//清接收緩存 } rx_len=0;//清除計數 recv_end_flag=0;//清除接收結束標誌位 } HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//從新打開DMA接收 } }
程序的運行效果以下圖所示 ,輸入任意長度數據,串口打印出接收的數據長度並打印出接收的數據。本程序設置的接收長度最大BUFFER_SIZE是200,若想接收更長的數據,也能夠把BUFFER_SIZE和數組長度改大。
###DMA參數和函數解析
DMA的基本原理、參數和函數解析在下一篇文章進行分析(連接)。async
本文章的源代碼下載地址:https://download.csdn.net/download/u014470361/10234803函數