STM32:串口轮询模式、中断模式、DMA模式和接收不定长数据
一.串口轮询模式底层机制:
在STM32每个串口的内部都有两个寄存器:发送数据寄存器(TDR)/发送移位寄存器,当我们调用HAL_UART_Transmit 把数据发送出去时,CPU会将数据依次将数据发送到数据寄存器中,移位寄存器中的数据会根据我们设置的比特率传化成高低电平从TX引脚输出。待发送移位寄存器中发数据发送出去后,CPU就会将下一个数据进行相同的发送。
当我们调用HAL_UART_Receive把数据接收过来时,数据会通过RX引脚收到的电平信号进行转化后,会将数据存进接收移位寄存器。接收移位寄存器每接收完1帧就会将数据放到接收数据寄存器。而后CPU会将接收数据寄存器中的数据存到变量中

而在轮询模式下。在发送整个数据的过程中,CPU都要不断地轮询“发送数据寄存器”中的数据是否移动到“发送移位寄存器”下,直到把本次要发送的数据全部发完,或者用时超过设置的超时时间才算结束。
因此,采用轮询模式,在数据接收和发送过程中,CPU不会去做其他事情,主程序中的代码会进行阻塞直到IO结束。
具体的案例在下面链接:
STM32:TTL串口调试-CSDN博客
二.串口的中断模式
(1).中断模式机制
采用中断模式便可以解决在IO过程中主程序阻塞问题。原理是接收和发送数据时,CPU并不会轮询发送/接收数据寄存器是否有数据。而是发送/接收数据寄存器当每数据时会发送一个中断主动通知CPU。因此CPU在将数据寄存器中的数据移动到移位寄存器后,就可以去执行其他任务了。当发送移位寄存器中的数据发送出去后就会触发“发送移位寄存器空”中断再把CPU叫回来。如此反复完成IO。
(2).中断模式案例
在STM32:TTL串口调试-CSDN博客这个案例下,改造成中断函数形式。
1. 打开CubeIDE,开启USART2中断,生成代码

2.查看stm32f1xx_it文件中 USART2_IRQHandler() 中断处理函数的定义。由于每个USART中只有一个中断向量,并且这个中断向量是USART中断请求共用的,所以中断处理函数也是被USART共用的。因此,为了单独写发送数据的逻辑写在中断处理函数中就不太合适。因此需要判断哪些原因触发了这个中断处理函数,分别实现逻辑,而这个判断HAL_UART_IRQHandler(&huart2) 函数中已经帮我们准备好了。

转到HAL_UART_IRQHandler(&huart2)的定义。可以看见经过一系列判断等逻辑后就会根据判断的结果执行Callback函数。因此当某个事件发生时,就会调用回调函数。而数据接收完成后执行的回调函数就是:
__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
注:Cplt 指 完成 。 __weak 关键字作为前缀代表是一个弱定义,我们可以在其他地方重新定义此函数
因此我们可以实现这个回调函数来实现传输数据又不阻塞主程序。
(3).示例代码
main.c关键代码如下:
注: HAL_UART_Receive_IT(&huart2, &message, size);
HAL_UART_Transmit_IT(&huart2,&message, size);是中断形式的UART接收/发送数据的函数,由于不阻塞主程序因此不需要设置超时时间。
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;
uint8_t recvDate[2];/* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){// HAL_UART_Receive(huart, pData, Size, Timeout)
// HAL_UART_Receive(&huart2, recvDate, 2, HAL_MAX_DELAY);HAL_UART_Transmit_IT(&huart2,recvDate,2);GPIO_PinState pinstate= GPIO_PIN_RESET;if(recvDate[1] == '1'){pinstate = GPIO_PIN_SET;}if(recvDate[0] == 'R'){HAL_GPIO_WritePin(redLED_GPIO_Port, redLED_Pin, pinstate);}else if(recvDate[0] == 'B'){HAL_GPIO_WritePin(blueLED_GPIO_Port,blueLED_Pin, pinstate);}else if(recvDate[0] == 'G'){HAL_GPIO_WritePin(greenLED_GPIO_Port,greenLED_Pin, pinstate);}HAL_UART_Receive_IT(&huart2, recvDate, 2);}
/* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART2_UART_Init();/* USER CODE BEGIN 2 */HAL_UART_Receive_IT(&huart2, recvDate, 2);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
...
三.DMA 模式
虽然采用中断模式便可以解决在IO过程中主程序阻塞问题,但是CPU切换过于频繁。而直接内存访问(DMA,Direct Memory Access)是一些计算机总线架构提供的功能,它能使数据从附加设备(如磁盘驱动器)直接发送到计算机主板的内存上。
CPU和寄存器就像老师与学生。轮询模式就像老师每讲完一段知识点,老师都会不断地问学生学好了没,直到学会才会讲下一个知识点。中断模式就像老师每讲完一段知识点后就开始干自己的事,等待学生举手示意自己学习完后才开始讲下一个知识点。而DMA就像一名助教,负责提前学习老师要讲给学生的知识,助教再将所学知识讲给学生。直到学生把助教所学的知识都学完后,助教再让教师再传授一部分知识。
再CubeIDE设计界面中,connective ->USART2-> DMA Settings 可以配置DMA通道(如下图).
想要发送数据(TX),即内存向外设传输数据,默认通道为Channal7,而接收为Channal6。目前采用默认配置就行。

配置完后,传输和发送数据的函数就变成了
注: HAL_UART_Receive_DMA(&huart2, &message, size);
HAL_UART_Transmit_DMA(&huart2,&message, size);
当然,还可以利用中断来通知CPU传输/发送数据,只不过就不是原先的串口中断,而是DMA传输完成中断了。

四.接收不定长数据
(1) 接收不定长数据的原理
接收不定长数据主要关心的是"串口空闲(Idle)中断",即接收串口(RX引脚)上无后续数据进入便会触发。通常这个场景代表一帧数据包接收完成。
而数据接收关键函数就变成了:
HAL_UARTEx_ReceiveToIdle(&huart2, pData, Size, RxLen, Timeout)
//size为允许装入的最大数据长度。
HAL_UARTEx_ReceiveToIdle_IT(&huart2, &message,maxsize);
HAL_UARTEx_ReceiveToIdle_DMA(&huart2, &message, maxsize);
回调函数就变成了
// Size参数传入数值为本次接收的数据长度
__weak void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
对于该回调函数,除了"串口空闲中断"会调用以外,DMA传输过半中断也会调用。因此需要根据业务要求决定无关中断是否要屏蔽。
(2).采用DMA方式接收不定长数据的示例代码
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM */
uint8_t recvDate[20];
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;
DMA_HandleTypeDef hdma_usart2_tx;
DMA_HandleTypeDef hdma_usart2_rx;/* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size){if(huart == &huart2){//把接收到的数据,发给终端进行打印HAL_UART_Transmit_DMA(&huart2,recvDate,Size);GPIO_PinState pinstate= GPIO_PIN_RESET;if(recvDate[1] == '1'){pinstate = GPIO_PIN_SET;}if(recvDate[0] == 'R'){HAL_GPIO_WritePin(redLED_GPIO_Port, redLED_Pin, pinstate);}else if(recvDate[0] == 'B'){HAL_GPIO_WritePin(blueLED_GPIO_Port,blueLED_Pin, pinstate);}else if(recvDate[0] == 'G'){HAL_GPIO_WritePin(greenLED_GPIO_Port,greenLED_Pin, pinstate);}//继续接收即将要接收的数据HAL_UARTEx_ReceiveToIdle_DMA(&huart2, recvDate, sizeof(recvDate));//关闭DMA传输过半中断__HAL_DMA_DISABLE_IT(&hdma_usart2_rx,DMA_IT_HT);}
}
/* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_DMA_Init();MX_USART2_UART_Init();/* USER CODE BEGIN 2 *///接收数据,并屏蔽DMA传输过半中断HAL_UARTEx_ReceiveToIdle_DMA(&huart2, recvDate, sizeof(recvDate));__HAL_DMA_DISABLE_IT(&hdma_usart2_rx,DMA_IT_HT);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
相关文章:
STM32:串口轮询模式、中断模式、DMA模式和接收不定长数据
一.串口轮询模式底层机制: 在STM32每个串口的内部都有两个寄存器:发送数据寄存器(TDR)/发送移位寄存器,当我们调用HAL_UART_Transmit 把数据发送出去时,CPU会将数据依次将数据发送到数据寄存器中,移位寄存器中的数据会根据我们设置…...
【OJ for Divide and Conquer】OJ题解
文章目录 A - Ultra-QuickSortB - Hanoi Tower Troubles Again! [找规律递归]C - Fibonacci Again[找规律]E - [Fire Net](https://programmerall.com/article/7276104269/)[DFS 搜索 ⭐⭐]F - Gridland[找规律]G - Maximum Subarray Sum[动态规划/分治..经典⭐]I - Quoit Desi…...
使用 Sealos 一键部署 Kubernetes 集群
Sealos 是一款以 Kubernetes 为内核的云操作系统发行版,使用户能够像使用个人电脑一样简单地使用云。 与此同时,Sealos 还提供一套强大的工具,可以便利地管理整个 Kubernetes 集群的生命周期。 Sealos 不仅可以一键安装一个单节点的 Kubern…...
解读电力系统中的GPS北斗卫星同步时钟系统
随着电力系统的快速发展,变电站中的各类系统 :计算机监控系统、水情测报系统、视频监控系统 状态监测系统 生产信息管理系统等,各类装置:继电保护装置、故障录波装置、PMU装置、事件顺序记录SOE功能越来越强大,需要采集、记录的数…...
原子类:Java并发编程的利器
在多线程环境下,确保数据的一致性和原子性是至关重要的。Java提供了一些原子类,用于解决多线程并发问题。这些原子类能够确保操作在多线程环境下是原子的,即不会被其他线程干扰。本文将介绍Java中的原子类及其应用。 一、原子类概述 原子类…...
99%网工都会遇到的经典面试问题
①问题:介绍TCP连接的三次握手?追问:为什么TCP需要握手三次? 三次握手: 第一步:A向B发送一个SYN报文表示希望建立连接 第二步:B收到A发过来的数据包后,通过SYN得知这是一个建立连接的请求,于是发送ACK确认,由于TCP的全双工模式ÿ…...
html和css中图片加载与渲染的规则是什么?
浏览器渲染web页面的过程 解析html,构成dom树 2.加载css,构成样式规则树 3.加载js,解析js代码 4.dom树和样式树进行匹配,构成渲染树 5.计算元素位置进行页面布局 5.绘制页面,呈现到浏览器中 图片加载和渲染的过程 1.解…...
YOLO轻量化改进 , 边缘GPU友好的YOLO改进算法!
在本文中,作者根据现有先进方法中各种特征尺度之间缺少的组合连接的问题,提出了一种新的边缘GPU友好模块,用于多尺度特征交互。此外,作者提出了一种新的迁移学习backbone采用的灵感是来自不同任务的转换信息流的变化,旨…...
第15届蓝桥杯Scratch选拔赛中级(STEMA)真题2023年8月
第15届蓝桥杯Scratch选拔赛中级(STEMA)真题2023年8月 一、单选题 第 1 题 单选题 点击以下积木块,生成的随机数是一个( )。 A.整数 B.小数 C.整数或小数 D.以上都不对 第 2 题 单选题 运行以下程序࿰…...
c++二叉树遍历
参考文献 数据结构c语言版,严蔚敏_吴伟民著。 二叉树 中序遍历代码实现 #include<vector> #include<iostream> using namespace std;//Definition for a binary tree node. struct TreeNode {int val;TreeNode *left;TreeNode *right;TreeNode() : v…...
day14_集合
今日内容 零、 复习昨日 一、集合框架体系 二、Collection 三、泛型 四、迭代 五、List(ArrayList、LinkedList) 零、 复习 throw和throws什么区别 throwthrows位置方法里面方法签名上怎么写throw 异常对象throws异常类名(多个)作用真正抛出异常对象声明抛出的异常类型 运行时…...
私有云:架构图
私有云:架构图 1、架构图2、服务器分配及配置3、本地物理机hosts文件配置4、相关软件包5、本地物理机电脑配置参考【内存最好20G往上】 机缘巧合之下突然想玩玩虚拟化,然后就查资料本地自己搭建一套私有云 使用【VMware Workstation】这个虚拟化软件来进…...
在安装和配置DVWA渗透测试环境遇到的报错问题
安装环境 前面的安装我参考的这个博主:渗透测试漏洞平台DVWA环境安装搭建及初级SQL注入-CSDN博客 修改bug 1.首先十分感谢提供帮助的博主,搭建DVWA Web渗透测试靶场_dvwa 白屏-CSDN博客,解决了我大多数问题,报错如下࿱…...
深度学习_2 数据操作
数据操作 机器学习包括的核心组件有: 可以用来学习的数据(data);如何转换数据的模型(model);一个目标函数(objective function),用来量化模型的有效性&…...
win 下安装 nvm 的使用与配置
nvm 全名 node.js version management,是一个 nodejs 的版本管理工具。通过它可以安装和切换不同版本的 nodejs。 注:如果已经安装了 nodejs 需先卸载后再安装 nvm 为了确保 nodejs 已彻底删除,可以看看安装目录中是否有 node 文件夹&#x…...
Git笔记
删除最后一次提交 git reset --hard HEAD~1...
省钱兄共享茶室共享娱乐室小程序都有哪些功能
随着共享经济的兴起,共享茶室和共享娱乐室作为一种新型的共享空间,逐渐受到了年轻人的青睐。省钱兄共享茶室共享娱乐室小程序作为该领域的优秀代表,集多种功能于一身,为用户提供了一个便捷、舒适、高效的社交娱乐平台。本文将详细…...
vue-cli方式创建vue3工程
创建工程前,可先用命令行查看是否安装vue-cli。 通过命令行查看vue-cli版本 vue --version 如果已安装vue-cli,则会显示当前安装版本 vue/cli 4.5.13 如果没有安装vue-cli,会提示安装 vue : 无法识别“vue”命令 需要通过npm全局安装v…...
四、W5100S/W5500+RP2040树莓派Pico<TCP Server数据回环测试>
文章目录 1. 前言2. 协议简介2.1 简述2.2 优点2.3 应用 3. WIZnet以太网芯片4. TCP Server数据回环测试4.1 程序流程图4.2 测试准备4.3 连接方式4.4 相关代码4.5 测试现象 5. 注意事项6. 相关链接 1. 前言 在计算机网络中,TCP Server是不可或缺的角色,它…...
技术视角下的跑腿小程序开发:关键挑战和解决方案
跑腿小程序作为连接服务提供者和用户的桥梁,面临着诸多技术挑战。本文将聚焦于技术层面的关键挑战,并提供解决方案,以帮助开发者应对技术上的复杂问题。 1. 实时性与性能挑战 挑战: 跑腿小程序需要实时地匹配订单、更新状态和提…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
