串口IDLE空闲中断+DMA实现接收不定长数据基于stm32cubemx
引言:對于串口接收一些不定長的數據,必須面對一個問題:怎么判斷一幀數據接收是否完成?通常使用RXNE非空中斷配合簡單的數據協議,在數據中加入幀頭、幀尾,在程序中判斷是否接收到幀尾來確定數據接收完畢,因此對每個字節都要觸發中斷進行判斷,比較消耗系統資源,尤其是在一些實時性要求較高的場合,而串口空閑中斷可以大大簡化數據接收過程的判斷。本文章介紹基于stm32cubemx使用DMA+IDLE串口空閑中斷實現接收不定長數據。
STM32的IDLE空閑中斷產生機制
IDLE空閑中斷是在監測到數據接收后(即串口的RXNE位被置位)開始檢測,當總線上在一個字節對應的周期內未再有新的數據接收時,控制觸發空閑中斷的IDLE位被硬件置1, 便會激發一個空閑中斷,在中斷處理函數中,我們可以解析接收到的不定長數據。
?使用工具
軟件:stm32cubemx ,keil uVision5,串口收發軟件(vofa+),
硬件:stm32f103rct6,ST-link下載器,數據線.
STM32CUBEMX初始化配置
打開stm32cubemx,點擊紅色方框處,開始新建工程
查找芯片型號,選擇對應芯片型號處雙擊
對sys進行如下配置,便于進行st-link下載
設置rcc,選擇高速時鐘(HSE)為外部晶振
配置時鐘樹,本文使用的是f103系列,HCLK最大為72MHz
配置串口:
1.設置MODE為異步通信(Asynchronous)
2.使用默認配置(波特率為115200 Bits/s,傳輸數據長度為8 Bit,奇偶檢驗無,停止位為1 Bit, 接收和發送都使能)
3.使能串口中斷
?
設置DMA:
工程命名,選擇保存位置,選擇使用的編譯器及版本
?
如下配置,點擊GENERATE CODE生成代碼
代碼編寫
注釋部分為使用雙緩沖區接收數據,可有效防止接收中斷間隔時間非常短(即發送數據幀的速率很快),MCU來不及處理此次接收到的數據,又產生中斷,導致數據會被覆蓋的情況
main.h中添加
#define BUFFER_SIZE 100 void USAR_UART_IDLECallback(UART_HandleTypeDef *huart,uint8_t rxlen );main.c
/* USER CODE BEGIN PV */ uint8_t rxbuffer1[BUFFER_SIZE]={0}; //接收數據緩存數組1 //uint8_t rxbuffer2[BUFFER_SIZE]={0}; //接收數據緩存數組2 //uint8_t flag=0;雙緩沖區切換標志 /* USER CODE END PV */?main函數中注意確保DMA初始化函數放在串口初始化之前,
并在串口初始化之后使能IDLE中斷、開啟串口DMA接收
/* Initialize all configured peripherals */MX_GPIO_Init();MX_DMA_Init();//放在串口初始化之前MX_USART1_UART_Init();/* USER CODE BEGIN 2 */__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中斷HAL_UART_Receive_DMA(&huart1,rxbuffer1,BUFFER_SIZE);//開啟串口DMA接收/* USER CODE END 2 */在/* USER CODE BEGIN 4 */下添加用戶自定義IDLE空閑中斷回調函數(注釋部分為雙緩沖接收)
void USAR_UART_IDLECallback(UART_HandleTypeDef *huart,uint8_t rxlen ){if(huart == &huart1) //判斷是否為串口1產生中斷{/* switch(flag){case 0: flag=1;memset(rxbuffer2,0,BUFFER_SIZE);//清空接收緩存,調用需包含string.h,本例程刪去也基本無影響HAL_UART_Receive_DMA(&huart1,rxbuffer2,BUFFER_SIZE);//重新打開DMA接收HAL_UART_Transmit_DMA(&huart1, rxbuffer1,rxlen);//將接收到的不定長數據發送到上位機 rxlen = 0;//清除數據長度計數break;case 1:flag=0;memset(rxbuffer1,0,BUFFER_SIZE);//清空接收緩存,調用需包含string.h,本例程刪去也基本無影響HAL_UART_Receive_DMA(&huart1,rxbuffer1,BUFFER_SIZE);//重新打開DMA接收HAL_UART_Transmit_DMA(&huart1, rxbuffer2,rxlen);//將接收到的不定長數據發送到上位機 rxlen = 0;//清除數據長度計數break;}*/HAL_UART_Transmit_DMA(&huart1, rxbuffer1,rxlen);//將接收到的不定長數據發送到上位機rxlen = 0;//清除數據長度計數HAL_UART_Receive_DMA(&huart1,rxbuffer1,BUFFER_SIZE);//重新打開DMA接收}}由于hal庫中沒有定義IDLE空閑中斷的中斷處理函數,需要用戶自行定義
打開stm32f1xx_it.c,找到void USART1_IRQHandler(void)函數,并添加如下代碼:
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 */uint8_t rxlen ; //接收一幀數據的長度if(RESET != __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)) //判斷idle標志被置位{ __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除標志位HAL_UART_DMAStop(&huart1); // 停止DMA傳輸rxlen = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx); //總計數減去未傳輸的數據個數,得到已經接收的數據個數USAR_UART_IDLECallback(&huart1,rxlen); // 調用用戶定義空閑中斷回調函數}/* USER CODE END USART1_IRQn 1 */ }下載程序測試
藍色為上位機發送數據,綠色為上位機接收數據
測試正常
總結
以上是生活随笔為你收集整理的串口IDLE空闲中断+DMA实现接收不定长数据基于stm32cubemx的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql多实例(三种方法)
- 下一篇: 邮件服务 交换空间(虚拟内存)