參考:https://blog.csdn.net/u014470361/article/details/79206352
我這里使用的芯片是 F1 系列的,主要是利用 DMA 數據傳輸方式實現的,在配置工程的時候要注意配置好 DMA,並開啟中斷。
如果出現數據長度對,可是數據接收不完整,把Memory勾選即可:
1、利用STM32 cubemx 建立一個工程,工程建立請參考我以前的文章:https://www.cnblogs.com/xingboy/p/9597464.html
2、利用STM32 cubemx 生成代碼后,我們先定義一些變量來使用
/* 自己添加代碼部分 */ volatile uint8_t rx_len=0; //接收數據長度 volatile uint8_t recv_end_flag=0; //接收完成標記位 uint8_t rx_buffer[100]; //接收緩存 char BUFFER_SIZE=100; //不定長數據的最大長度,設置為100則最大長度為100
這里為什么要定義volatile 關鍵字呢?
主要是因為volatile 關鍵字提醒編譯器定義的變量是易變的,編譯后的程序每次需要存儲或讀取該變量時,會直接從變量地址讀取數據。在中斷或多線程中使用volatile關鍵字可以避免不同優化等級時程序出錯,提高程序的魯棒性。
接着對串口初始化添加一些代碼,程序如下:
/* USART2 init function */ static void MX_USART2_UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 115200; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } /* 自己添加代碼部分 */ __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); //使能idle中斷 HAL_UART_Receive_DMA(&huart2,rx_buffer,BUFFER_SIZE); //打開DMA接收,數據存入rx_buffer數組中。 }
3、接收函數我寫在了另一個文件上,其他文件要用到上面 main文件里面定義的變量就要聲明一個外部變量
extern volatile uint8_t rx_len; extern volatile uint8_t recv_end_flag; extern uint8_t rx_buffer[100]; extern char BUFFER_SIZE;
4、接着修改串口中斷服務函數,在串口中斷服務函數里添加接收代碼,代碼如下:
void USART2_IRQHandler(void) { /* USER CODE BEGIN USART2_IRQn 0 */ /* 自己添加代碼部分 */ uint32_t tmp_flag = 0; uint32_t temp; tmp_flag =__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE); //獲取IDLE標志位 if((tmp_flag != RESET)) //idle標志被置位 { __HAL_UART_CLEAR_IDLEFLAG(&huart2); //清除標志位 temp = huart2.Instance->SR; //清除狀態寄存器SR(F0的HAL庫USART_TypeDef結構體中名字為ISR:USART Interrupt and status register),讀取SR可以清楚該寄存器 temp = huart2.Instance->DR; //讀取數據寄存器中的數據,讀取DR(F0中為RDR:USART Receive Data register)
HAL_UART_DMAStop(&huart2);
temp = hdma_usart2_rx.Instance->CNDTR; //獲取DMA中未傳輸的數據個數,NDTR寄存器分析見下面 rx_len = BUFFER_SIZE - temp; //總計數減去未傳輸的數據個數,得到已經接收的數據個數 recv_end_flag = 1; //接受完成標志位置1 } /* USER CODE END USART2_IRQn 0 */ HAL_UART_IRQHandler(&huart2); /* USER CODE BEGIN USART2_IRQn 1 */ /* USER CODE END USART2_IRQn 1 */ }
上面的 CNDTR 寄存器在 DMA 通道結構體中定義了 CNDTR 寄存器,這個不同的芯片HAL庫里面定義的命名有點不同,有興趣的可以自己去查看一下,那為什么是未傳輸的數據數呢,STM32的中文手冊給出了該寄存器的具體說明。(注意:建立工程的時候要添加串口TX RX 的DMA通道,以及打開DMA中斷)
/** * @brief DMA Controller */ typedef struct { __IO uint32_t CCR; __IO uint32_t CNDTR; __IO uint32_t CPAR; __IO uint32_t CMAR; } DMA_Channel_TypeDef;
寄存器說明如下:
5、接着編寫接收處理函數,代碼如下:
/*************************************************************** *函數名:Data_Turn *輸 入:無 *說 明:串口接收完成,返回串口查看接收情況 *返回值:無 **/ void Data_Turn(void) { if(recv_end_flag ==1) { printf("rx_len=%d\r\n",rx_len); //打印接收長度 HAL_UART_Transmit(&huart2,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; //清除接收結束標志位 } }
6.再主函數里的的while循環里再次打開DMA中斷接收
HAL_UART_Receive_DMA(&huart2,rx_buffer,BUFFER_SIZE); //重新打開DMA接收
運行效果如下圖,我的代碼是接收到Do-0:1字符串,判斷字符串,返回我需要的字符串,效果正確。
補充一點最近新發現的關於串口中斷接收的問題:
串口中斷接收如果使用HAL庫的中斷接收函數,接收到的數據量遠小於設定要接收的數據量,串口一直處於Busy狀態,會出現接收死循環的情況,接收的數據跟設定不符會出錯
注意:
測試過程中發現,用接收串口助手的所有數據都沒問題,不過接收模塊的不定長數據時,如果這個數據之間包含回車換行符會接收不全,例如:AT\r\nOK\r\n,這個就只能接收到AT\r,具體什么原因造成的還沒找出原因,有知道的可以告訴我一下。
不過如果是串口助手發這串數據下來,卻又可以全部接收完成,想不通。為了解決這個問題,我又找出了一個中斷接收的方法,可以實現了不管中間有沒有回車,都可以接收完成。傳輸門:https://www.cnblogs.com/xingboy/p/10154475.html