[STM32 HAL庫]串口空閑中斷+DMA接收不定長數據

一、空閑中斷

STM32的串口具有空閑中斷,什么叫做空閑呢?如何觸發空閑中斷呢?

  • 空閑:串口發送的兩個字符之間間隔非常短,所以在兩個字符之間不叫空閑。空閑的定義是總線上在一個字節的時間內沒有再接收到數據。
  • 觸發條件:空閑中斷是檢測到有數據被接收后,總線上在一個字節的時間內沒有再接收到數據的時候發生的。而總線在什么情況時,會有一個字節時間內沒有接收到數據呢?一般就只有一個數據幀發送完成的情況,所以串口的空閑中斷也叫幀中斷。

開啟空閑中斷后,要重寫對應的回調函數HAL_UARTEx_RxEventCallback,在函數里做些處理。

二、實驗內容

使用USART1外設,接收電腦發來的數據,然后將接收到的數據發送給電腦。
在這里插入圖片描述

二、STM32CUBEMX配置

USART1的模式為異步通信115200波特率數據長度8位無校驗位停止位1位
在這里插入圖片描述
DMA Settings的配置,開啟串口接收的DMA
在這里插入圖片描述
NVIC Settings的配置,開啟USART1的全局中斷。
在這里插入圖片描述
這里兩個中斷優先級我都給了1,根據不同情況修改中斷優先級
在這里插入圖片描述

三、keil代碼

首先確保魔術棒中的Use MicroLIB這個選項勾選上,不然串口發送數據會不正常
在這里插入圖片描述
添加頭文件,因為要使用memset函數

#include "string.h"

定義串口接收數據數組

#define BUFF_SIZE	128			    //接收緩存大小
uint8_t rx_buffer[BUFF_SIZE];      // 創建接收緩存,大小為BUFF_SIZE

手動在usart.h外部聲明hdma_usart1_rx,在main函數中要使用

extern UART_HandleTypeDef huart1;/* USER CODE BEGIN Private defines */
extern DMA_HandleTypeDef hdma_usart1_rx;        //手動外部聲明
/* USER CODE END Private defines */void MX_USART1_UART_Init(void);

main函數初始化添加這兩個函數,不然串口首次無法進入中斷

HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rx_buffer,BUFF_SIZE);	//手動開啟串口DMA模式接收數據
__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);		   	//手動關閉DMA_IT_HT中斷

重定向HAL_UARTEx_RxEventCallback串口接收完成回調函數

/* 串口接收完成回調函數 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{if (huart->Instance == USART1){HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, BUFF_SIZE); // 接收完畢后重啟串口DMA模式接收數據HAL_UART_Transmit(&huart1, rx_buffer, Size, 0xffff);         // 將接收到的數據再發出__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);		   	// 手動關閉DMA_IT_HT中斷memset(rx_buffer, 0, BUFF_SIZE);							// 清除接收緩存}
}	

重定向HAL_UART_ErrorCallback串口錯誤回調函數

/* 串口錯誤回調函數 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef * huart)
{if(huart->Instance == USART1){HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, BUFF_SIZE); //手動開啟串口DMA模式接收數據__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);		   // 手動關閉DMA_IT_HT中斷memset(rx_buffer, 0, BUFF_SIZE);							   // 清除接收緩存}
}

以下是main函數完整代碼

#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"#include "stdio.h"
#include "string.h"void SystemClock_Config(void);#define BUFF_SIZE	128			//接收緩存大小
uint8_t rx_buffer[BUFF_SIZE];  // 創建接收緩存,大小為BUFF_SIZEint main(void)
{HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_DMA_Init();MX_USART1_UART_Init();HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rx_buffer,BUFF_SIZE);	//手動開啟串口DMA模式接收數據__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);		   	//手動關閉DMA_IT_HT中斷	while (1){}
}
void SystemClock_Config(void)
{//...
}
/* USER CODE BEGIN 4 */
/* 串口接收完成回調函數 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{if (huart->Instance == USART1){HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, BUFF_SIZE); /// 接收完畢后重啟串口DMA模式接收數據HAL_UART_Transmit(&huart1, rx_buffer, Size, 0xffff);         // 將接收到的數據再發出__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);		   		// 手動關閉DMA_IT_HT中斷memset(rx_buffer, 0, BUFF_SIZE);							   	// 清除接收緩存}
}
/* 串口錯誤回調函數 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef * huart)
{if(huart->Instance == USART1){HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, BUFF_SIZE);//手動開啟串口DMA模式接收數據__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);		   // 手動關閉DMA_IT_HT中斷memset(rx_buffer, 0, BUFF_SIZE);							   // 清除接收緩存}
}
/* USER CODE END 4 */
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}

四、原理講解

我們使用串口空閑中斷的目的是為了獲取完整的數據,并且是由軟件自動判斷是否接收完整。當軟件判斷接收到完整的數據時,就會產生中斷進入回調函數HAL_UARTEx_RxEventCallback
軟件判斷接收到完整數據有兩種情況:

  • 1、數據接收后,一個字節未接收到數據;
  • 2、當前的數據接收長度預設接收長度相等;預設接收長度HAL_UARTEx_ReceiveToIdle_DMA的參數BUFF_SIZE

預設接收長度要考慮好,若數據接收長度預設接受長度,就會出現數據的丟失情況。因為數據接收長度是隨著接收不斷累加的,其大小等于預設接收長度時就會觸發中斷,程序就會判斷成接收到完整數據,就是第二情況。

1、HAL_UARTEx_ReceiveToIdle_DMA

main函數里我們調用了HAL_UARTEx_ReceiveToIdle_DMA,該函數會開啟USART1的空閑中斷,并啟用串口以DMA方式接收數據,當數據接收完成之后進入HAL_UARTEx_RxEventCallback回調函數。隨后,我們在回調函數里進行數據處理即可。

2、__HAL_DMA_DISABLE_IT

為什么要手動關閉這個中斷,如果不關閉這個中斷,程序在接收一串完整的數據時會進入執行兩次HAL_UARTEx_RxEventCallback。一次是數據傳輸一半時,一次數數據傳輸完成時,這與我們預期的不符。我們只希望在數據傳輸完成時進入回調函數進行數據處理。

__HAL_DMA_DISABLE_IT的回調函數是UART_DMARxHalfCplt,該回調函數會執行一次HAL_UARTEx_RxEventCallback

3、HAL_UARTEx_RxEventCallback

在該回調函數里,需要手動開啟中斷,才能進入下一次的中斷。

4、HAL_UART_ErrorCallback

重寫這個回調函數主要是為了防止一些錯誤情況的發生。若產生錯誤中斷進入HAL_UART_ErrorCallback,則無法進入HAL_UARTEx_RxEventCallback回調函數,也就無法開啟下一次空閑中斷。所以,這里在HAL_UART_ErrorCallback里手動開啟了空閑中斷,做了一些恢復處理。

錯誤示例:開發板設置115200波特率,電腦串口用9600波特率,電腦發送數據之后,程序會進入串口錯誤中斷,而進入不了空閑中斷。若程序當中未在錯誤中斷進行錯誤處理,即使電腦串口修改成115200波特率進行通信,程序也無法進入空閑中斷。

五、參考文章

配置和代碼鏈接,里面詳解了相關一些函數的源碼
鏈接: 【STM32 HAL庫實戰】串口DMA + 空閑中斷 實現不定長數據接收
空閑中斷講解
鏈接: STM32串口之空閑中斷

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/66786.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/66786.shtml
英文地址,請注明出處:http://en.pswp.cn/web/66786.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Unity Line Renderer Component入門

Overview Line Renderer 組件是 Unity 中用于繪制連續線段的工具。它通過在三維空間中的兩個或兩個以上的點的數組,并在每個點之間繪制一條直線。可以繪制從簡單的直線到復雜的螺旋線等各種圖形。 1. 連續性和獨立線條 連續性:Line Renderer 繪制的線條…

純 Python、Django、FastAPI、Flask、Pyramid、Jupyter、dbt 解析和差異分析

一、純 Python 1.1 基礎概念 Python 是一種高級、通用、解釋型的編程語言,以其簡潔易讀的語法和豐富的標準庫而聞名。“純 Python” 在這里指的是不依賴特定的 Web 框架或數據分析工具,僅使用 Python 原生的功能和標準庫來開發應用程序或執行任務。 1.…

SQL記錄學習日志

刪除表 DROP TABLE:徹底刪除表和其數據,無法恢復。 DROP TABLE IF EXISTS:在刪除之前檢查表是否存在。 TRUNCATE TABLE:刪除所有數據,但保留表的結構。 DELETE:刪除表中的所有數據,但保留表的結…

QT:tftp client 和 Server

1.TFTP簡介 TFTP(Trivial File Transfer Protocol,簡單文件傳輸協議)是TCP/IP協議族中的一個用來在客戶機與服務器之間進行簡單文件傳輸的協議,提供不復雜、開銷不大的文件傳輸服務。端口號為69。 FTP是一個傳輸文件的簡單協議,…

WPF5-x名稱空間

1. x名稱空間2. x名稱空間內容3. x名稱空間內容分類 3.1. x:Name3.2. x:Key3.3. x:Class3.4. x:TypeArguments 4. 總結 1. x名稱空間 “x名稱空間”的x是映射XAML名稱空間時給它取的名字(取XAML的首字母),里面的成員(如x:Class、…

前端jquery 實現文本框輸入出現自動補全提示功能

git倉庫:web_study/some-demos/inputAutoFit at main Cong0925/web_study (github.com) 壓縮包:已綁定到指定資源 示例圖: 實現說明: 1.首先,html部分設置好相關的定位標簽如圖: 2.主要函數 3.默認數據

緩存之美:萬文詳解 Caffeine 實現原理(上)

由于社區最大字數限制,本文章將分為兩篇,第二篇文章為緩存之美:萬文詳解 Caffeine 實現原理(下) 大家好,我是 方圓。文章將采用“總-分-總”的結構對配置固定大小元素驅逐策略的 Caffeine 緩存進行介紹&…

Qt實踐:一個簡單的絲滑側滑欄實現

Qt實踐:一個簡單的絲滑側滑欄實現 筆者前段時間突然看到了側滑欄,覺得這個抽屜式的側滑欄非常的有趣,打算這里首先嘗試實現一個簡單的絲滑側滑欄。 首先是上效果圖 (C,GIF幀率砍到毛都不剩了) QProperty…

工作流引擎Camunda與LiteFlow核心組件對比

以下為 Camunda 7 和 LiteFlow 詳細的介紹,包括它們的核心組件和用途。 1. Camunda 7 詳細介紹 Camunda 7 是一個基于 BPMN 2.0 標準的企業級工作流和決策自動化平臺。它被廣泛應用于復雜業務流程的管理和執行,其核心目標是通過流程自動化來提升企業效…

css動畫水球圖

由于echarts水球圖動畫會導致ios卡頓&#xff0c;所以純css模擬 展示效果 組件 <template><div class"water-box"><div class"water"><div class"progress" :style"{ --newProgress: newProgress % }"><…

iOS 權限管理:同時請求相機和麥克風權限的最佳實踐

引言 在開發視頻類應用時&#xff0c;我們常常會遇到需要同時請求相機和麥克風權限的場景。比如&#xff0c;在用戶發布視頻動態時&#xff0c;相機用于捕捉畫面&#xff0c;麥克風用于錄制聲音&#xff1b;又或者在直播功能中&#xff0c;只有獲得這兩項權限&#xff0c;用戶…

Java 泛型上下限詳解:以 Info 泛型類和方法實現為例

本文將通過一個實際示例&#xff0c;來深入講解 Java 泛型中的上下限及其應用場景。在這個示例中&#xff0c;我們會實現一個泛型類 Info 和兩個泛型方法 upperLimit 和 lowerLimit&#xff0c;并解釋其工作機制。 1. 什么是 Java 泛型上下限&#xff1f; Java 泛型的上下限是…

客戶服務創新:數字化時代的策略與實踐

在數字化時代背景下&#xff0c;客戶服務已成為企業競爭的關鍵領域。隨著消費者需求的日益多樣化和個性化&#xff0c;傳統的客戶服務模式已難以滿足市場的要求。因此&#xff0c;企業需要不斷探索和創新客戶服務策略&#xff0c;以適應數字化時代的變化。 一、數字化時代客戶服…

【PyCharm】遠程連接Linux服務器

【PyCharm】相關鏈接 【PyCharm】連接Jupyter Notebook【PyCharm】快捷鍵使用【PyCharm】遠程連接Linux服務器【PyCharm】設置為中文界面 【PyCharm】遠程連接Linux服務器 PyCharm 提供了遠程開發的功能&#xff0c;使得開發者可以在本地編輯代碼或使用服務器資源。 下面將詳…

十三、數據的的輸入與輸出(3)

數據的輸出 writeClipboard&#xff08;&#xff09;函數 writeClipboard&#xff08;&#xff09;函數可以將數據輸出至剪貼板。 例如&#xff0c;將R的內置數據集iris輸出到剪貼板&#xff0c;在進入Excel中點擊"粘貼"。 head(iris) #查看數據集Sepal.L…

PyQt5之QDialog

1.描述 QDialog是對話窗口的基類&#xff0c;對話窗口是頂級窗口&#xff0c;主要用于短期任務和與用戶的簡短通信。 可分為模態對話框和非模態對話框。 模態對話框又可以分為應用程序級別和窗口級別。 ? 應用程序級別&#xff1a;當該種模態的對話框出現時&#xff0c;用…

Next.js:構建大模型智能體GPT研究者應用的 Web開發框架

Next.js&#xff1a;構建大模型智能體GPT研究者應用的 Web開發框架 Next.js 基礎知識 Next.js 是由 Vercel 公司開發維護的框架&#xff0c;極大地簡化了 React 應用的開發流程。其核心特性包括&#xff1a; 服務器端渲染&#xff08;SSR&#xff09;與靜態站點生成&#xff…

車載軟件架構 --- CP和AP作為中央計算平臺的軟件架構雙核心

我是穿拖鞋的漢子&#xff0c;魔都中堅持長期主義的汽車電子工程師。 老規矩&#xff0c;分享一段喜歡的文字&#xff0c;避免自己成為高知識低文化的工程師&#xff1a; 簡單&#xff0c;單純&#xff0c;喜歡獨處&#xff0c;獨來獨往&#xff0c;不易合同頻過著接地氣的生活…

華為EC6110T-海思Hi3798MV310_安卓9.0_通刷-強刷固件包

華為EC6110T-海思Hi3798MV310_安卓9.0_通刷-強刷固件包 刷機教程說明&#xff1a; 適用機型&#xff1a;華為EC6110-T、華為EC6110-U、華為EC6110-M 破解總分為兩個部分&#xff1a;拆機短接破解&#xff08;保留IPTV&#xff09;和OTT卡刷&#xff08;不保留IPTV&#xff09…

Element使用表單重置如果不使用prop,重置無法生效

文章目錄 為什么需要 prop&#xff1f;示例&#xff1a;使用 prop 的正確方式關鍵點總結 在 element-ui 的 el-form 組件中&#xff0c; prop 屬性是與表單驗證和表單字段綁定密切相關的&#xff0c;尤其在使用 resetFields() 重置表單數據時。 如果不使用 prop&#xff0…