当前位置: 首页 > news >正文

单片机串口接收状态机STM32

单片机串口接收状态机stm32

前言

项目的芯片stm32转国产,国产芯片的串口DMA接收功能测试不通过,所以要由原本很容易配置的串口空闲中断触发DMA接收数据的方式转为串口逐字节接收的状态机接收数据

两种方式各有优劣,不过我的芯片已经主频跑到72m,对于接收115200波特率的数据,绰绰有余。

给一张图,接收状态机就是设置串口每次接收1byte的数据就触发一次中断,在中断函数里面逐次统计数据,最后把有用的数据包放进缓存区给到处理区

协议图.drawio

1.首先现在cubemx的串口的dma接收关闭,具体配置看截图,然后导出下代码

image

image

image

2.开始配置代码,这里我用的代码编辑器是vscode,个人认为vscode编辑代码+keil调试的方式是最舒服的,有兴趣的可以试试,绝对提一档。

image

先定义一个uint8_t的变量用来接收每次收到的数据,一定注意,只要你是用的cubemx生成的project,在生成的文件下,都有把你改的代码写到/* USER CODE BEGIN 0 */​下面,不然等你下次在cubemx更改底层配置的时候会把你改好的代码擦洗掉。

uint8_t USART1_rxdata = 0;

image

往下滑,找到这个串口1的初始化函数,加这个串口接收中断启动函数

HAL_UART_Receive_IT(&huart1, &USART1_rxdata, 1);

然后我们来到中断c文件下,在串口1中断里增加我们的接收状态机

image

  rec_buff_scan(USART1_rxdata);   HAL_UART_Receive_IT(&huart1, &USART1_rxdata, 1); 

这里的rec_buff_scan就是我们定义的函数,因为我的习惯是在我自己创建的文件下写代码,避免更改cube生成的东西,所以文件名可能就不一样,这里就只看代码就好了

首先先把定义全部cv下来,我们协议是[0XAA,0X55,命令包长度,…(命令包内容),校验和高位,校验和底位,的格式,如果你们的不一样就改下RecState里面的变量名

image

#define REC_BUFF_SIZE 100
uint8_t rec_buf_scan[REC_BUFF_SIZE];    // 数据接收缓冲区
uint8_t rec_index = 0;                  // 数据接收索引
extern uint8_t USART1_rxdata;                  // 存储接收的单个字节// 状态枚举
typedef enum {STATE_WAIT_FOR_HEAD1,      // 等待帧头AASTATE_WAIT_FOR_HEAD2,      // 等待帧头55STATE_WAIT_FOR_LENGTH,     // 等待命令包长度STATE_RECEIVE_DATA,        // 接收命令包数据STATE_WAIT_FOR_CHECKSUM1,  // 等待校验和字节1STATE_WAIT_FOR_CHECKSUM2   // 等待校验和字节2
} RecState;RecState rec_state = STATE_WAIT_FOR_HEAD1;  // 初始状态
uint8_t packet_length = 0;                  // 数据包长度
uint16_t checksum = 0;  

然后把状态机整段cv,状态机就是把接收部分分成一个个的状态,条件符合就会跳到下一段,最后会在校验和验证整段数据是否接收正常,正常的话就会送入数据处理的函数里面,我这里定义了一个rec_buf_scan[REC_BUFF_SIZE]是跟我之前的接收数据缓存区做兼容而已,你们可以只导入数据包的内容,不需要把头和校验和导入,这里其实就是我懒了嘻嘻

我这里的处理其实你们不用借鉴的 ,直接在STATE_WAIT_FOR_CHECKSUM2状态判断校验和成功后,把接收成功flag=true;然后在你另外的处理函数就可以处理数据包了,这里的memcpy一下,也是很有必要的,我们的处理缓存和接收缓存一定要区分开来,因为没有自锁,所以只能在成功的时候把接受的数据塞入处理缓存,至于我这里为什么塞了全部数据而不是只有数据包,主要是配合我以前的处理,无需借鉴,总之你上班就明白我的难处,要改来改去还要兼容是很麻烦的事情

image

void rec_buff_scan(uint8_t byte) {switch (rec_state) {case STATE_WAIT_FOR_HEAD1:if (byte == 0xAA) {rec_state = STATE_WAIT_FOR_HEAD2;rec_index = 0;  // 重置接收索引checksum = 0;   // 重置校验和rec_buf_scan[rec_index++] = byte;//可以不用导入}break;case STATE_WAIT_FOR_HEAD2:if (byte == 0x55) {rec_state = STATE_WAIT_FOR_LENGTH;rec_buf_scan[rec_index++] = byte;//可以不用导入} else {rec_state = STATE_WAIT_FOR_HEAD1;}break;case STATE_WAIT_FOR_LENGTH:packet_length = byte;rec_buf_scan[rec_index++] = byte;//可以不用导入rec_state = STATE_RECEIVE_DATA;break;case STATE_RECEIVE_DATA:if (rec_index-3 < packet_length) {//这里if里面的条件把-3去掉 rec_buf_scan[rec_index++] = byte;}if (rec_index-3 == packet_length) {//这里if里面的条件把-3去掉 checksum = Frame_CalculationChecksum(&rec_buf_scan[3], packet_length);  // 计算校验和rec_state = STATE_WAIT_FOR_CHECKSUM1;}break;case STATE_WAIT_FOR_CHECKSUM1:if (byte == ((checksum >> 8) & 0xFF)) {rec_state = STATE_WAIT_FOR_CHECKSUM2;rec_buf_scan[rec_index++] = byte;//可以不用导入} else {rec_state = STATE_WAIT_FOR_HEAD1;}break;case STATE_WAIT_FOR_CHECKSUM2:if (byte == (checksum & 0xFF)) {//这里做你自己的处理就好了 ,程序跑到这里就已经验证通过了rec_buf_scan[rec_index++] = byte;//可以不用导入memcpy(&rec_buff[0], &rec_buf_scan[0], rec_index);Device_data.device_state = JUDGE_FLAG;Data_queue_rx.Interrupt_Len = rec_index;}// 无论校验是否通过,回到初始状态rec_state = STATE_WAIT_FOR_HEAD1;break;}
}

用到的Frame_CalculationChecksum函数是校验和计算函数,具体操作就是把接收的数据需要校验的那一段的第一个元素地址放进去,把数据包长度放进去,他会算完把结果返回,我这里就是简单的数据包加和校验

uint16_t Frame_CalculationChecksum(uint8_t *pData, uint8_t u8Length)
{uint16_t u16check_sum = 0;uint8_t i;for (i = 0; i < u8Length; i++){u16check_sum = u16check_sum + pData[i];}return u16check_sum;
}

这样基本上就把接收状态机整完了,可以仿真下试试看。

3.仿真测试

可以看到断点在第二步的时候0xaa已经存入数据缓存去了

image

同理我们直接看到接收成功这里打断点,可以看到我们在把接收成功时,已经把所有数据都塞进处理缓存区了。这里再严谨点最好是在移完数据后把接收缓存清零一下,但是不清也不影响。

image

image

4.小结

串口中断状态机是最基本的协议解析接收方式,作为一个嵌入式人员这个你必须要学会的,写法不限制,但是流程就是跟我的差不多的,具体根据协议来定的。

这种接收方式适合没有dma外设的单片机比如51单片机,国产单片机,在项目开发完成进行降本的时候也是可能会改到这种方式的,如果有不规范的地方请留言,我会更改。

相关文章:

单片机串口接收状态机STM32

单片机串口接收状态机stm32 前言 项目的芯片stm32转国产&#xff0c;国产芯片的串口DMA接收功能测试不通过&#xff0c;所以要由原本很容易配置的串口空闲中断触发DMA接收数据的方式转为串口逐字节接收的状态机接收数据 两种方式各有优劣&#xff0c;不过我的芯片已经主频跑…...

ipv6的 fc00(FC00::/7) 和 fec0(FEC0::/10)

ipv6的 fc00(FC00::/7)&#xff08;唯一本地地址&#xff09; 替代了 fec0(FEC0::/10) &#xff08;站点本地地址&#xff0c;已弃用&#xff09; ipv6的 fc00(FC00::/7) 替代了 fec0(FEC0::/10) , 在IPv6地址中&#xff0c;FC00::/7&#xff08;通常简写为FC00&#xff09;和…...

Chat GPT英文学术写作指令

目录 Chat GPT英文学术写作指令 Chat GPT英文学术写作指令 1."为我捉供一些建议和技巧,以提高我的学术写作质最和风格" (Provide me with some suggestions andtips to improve the quality andstyleofmyacademic writing.) 2."帮我提写一个清晰而有逻辑的…...

超详细Pycharm安装汉化教程,Python环境配置和使用指南,Python零基础入门看这个就够了!

包含编程资料、学习路线图、源代码、软件安装包等&#xff01;【[点击这里]】&#xff01; PyCharm 是由 JetBrains 打造的一款 Python IDE &#xff08;集成开发环境&#xff0c;Integrated Development Environment&#xff09;&#xff0c;带有一整套可以帮助用户在使用 Py…...

react-native:解决使用webView后部分场景在安卓10崩溃闪退问题

app闪退问题原因&#xff1a; 安卓7以上版本&#xff08;7和7以下版本不会出现闪退&#xff09;&#xff1a;在屏幕不可视区域加载webView或者webView不在可视区域内切换页面时app崩溃闪退&#xff08;在屏幕可视区域加载webView或者webView在可视区域内切换页面不会闪退&…...

大数据工具 flume 的安装配置与使用 (详细版)

参考网址&#xff1a;Flume 1.9用户手册中文版 — 可能是目前翻译最完整的版本了 1&#xff0c;上传安装包 安装包链接&#xff1a;文件下载-奶牛快传 Download &#xff5c;CowTransfer 口令&#xff1a;x8bhcg 1&#xff0c;切换盘符到安装目录 cd /opt/moudles 解压文件…...

智慧城市智慧城市项目方案-大数据平台建设技术方案(原件Word)

第1章 总体说明 1.1 建设背景 1.2 建设目标 1.3 项目建设主要内容 1.4 设计原则 第2章 对项目的理解 2.1 现状分析 2.2 业务需求分析 2.3 功能需求分析 第3章 大数据平台建设方案 3.1 大数据平台总体设计 3.2 大数据平台功能设计 3.3 平台应用 第4章 政策标准保障…...

C语言比较两个字符串是否相同

在不使用string.h中的内置函数的情况下 #include <stdio.h> #include <string.h> void main(){char arr1[]"hello world";char arr2[]"hello world";int i,a0;if(strlen(arr1)!strlen(arr2)){print("不相等");return 0;}for(i0;arr…...

丹摩征文活动|FLUX.1图像生成模型:AI工程师的创新实践

文章目录 一、对"FLUX.1"系列模型版本分析 二、AI工程师与FLUX.1系列模型 三、ComfyUI在线部署四、FLUX.1在线部署五、添加工作流呈现效果图展示六、总结 黑森林实验室&#xff08;Black Forest Labs&#xff09;推出的FLUX.1图像生成模型&#xff0c;凭借120亿参数的…...

ZABBIX API获取监控服务器OS层信息

Zabbix 是一款强大的开源监控解决方案,能够通过其 API 接口自动化管理和获取监控数据。在这篇文章中,详细讲解如何通过 Zabbix API 批量获取服务器的系统名称、IP 地址及操作系统版本信息,并将数据保存到 CSV 文件中。本文适合对 Python 编程和 Zabbix 监控系统有一定基础的…...

SpringBoot基础系列学习(五):JdbcTemplate 访问数据库

文章目录 一丶介绍二丶引入依赖三丶配置配置文件四丶创建表五丶java代码 一丶介绍 Spring Boot作为Spring的集大成者&#xff0c;自然会将JdbcTemplate集成进去。Spring Boot针对JDBC的使用提供了对应的Starter包&#xff1a;spring-boot-starter-jdbc&#xff0c;它其实就是在…...

JavaEE-多线程初阶(3)

目录 1.线程的状态 1.1 NEW、RUNNABLE、TERMINATED 1.2 TIMED_WAITING 1.3 WAITING 1.4 BLOCKED 2.多线程带来的风险-线程安全&#xff08;重点&#xff09; 2.1 观察线程不安全的现象 2.2 分析产生该现象的原因 2.3 产生线程安全问题的原因 2.3.1 抢占式执行&#x…...

从入门到精通:如何在Vue项目中有效运用el-image-viewer

Element UI之el-image-viewer组件详解 引言 在现代 Web 应用中,高质量的用户体验是不可或缺的一环。Element UI 作为一款基于Vue.js 2.0 的桌面端组件库,以其丰富的组件集、良好的文档和支持赢得了广大开发者的好评。本文将深入探讨el-image-viewer组件,这是一个用于在网页…...

uniapp组件实现省市区三级联动选择

1.导入插件 先将uni-data-picker组件导入我们的HBuilder项目中&#xff0c;在DCloud插件市场搜索uni-data-picker 点击下载插件并导入到我们的项目中 2.组件调用 curLocation &#xff1a;获取到的当前位置&#xff08;省市区&#xff09; <uni-data-picker v-slot:defa…...

【C++】异常处理机制(对运行时错误的处理)

&#x1f308; 个人主页&#xff1a;谁在夜里看海. &#x1f525; 个人专栏&#xff1a;《C系列》《Linux系列》 ⛰️ 天高地阔&#xff0c;欲往观之。 目录 引言 1.编译器可以处理的错误 2.编译器不能处理的错误 3.传统的错误处理机制 assert终止程序 返回错误码 一、…...

C++ boost steady_timer使用介绍

文章目录 1. 引入必要的头文件2. 基本用法2.1 同步定时器解释:2.2 异步定时器解释:3. 异步定时器与回调函数4. 设置定时器的超时时间4.1 使用秒、毫秒、微秒4.2 修改定时器的到期时间5. 多次使用定时器6. 循环执行任务7. 错误处理总结:C++ Boost 库提供了 boost::asio::stea…...

JVM 由多个模块组成,每个模块负责特定的功能

Java虚拟机&#xff08;JVM, Java Virtual Machine&#xff09;是一个抽象的计算机&#xff0c;它提供了一个运行环境&#xff0c;使得Java字节码可以在不同的平台上执行。JVM 由多个模块组成&#xff0c;每个模块负责特定的功能。以下是 JVM 的主要模块及其功能&#xff1a; …...

ORACLE批量插入更新如何拆分大事务?

拆分大事务 一、批量插入更新二、拆分事务之前文章MYSQL批量插入更新如何拆分大事务?说明了Mysql如何拆分,本篇文章探讨Oracle或OceanBase批量插入更新拆分大事务的问题 一、批量插入更新 oracle批量插入更新可使用merge语法eg: merge test ausing test_tmp bon (a.id = b.id…...

kafka+zookeeper的搭建

kafka从2.8版本开始&#xff0c;就可以不用配置zookeeper了&#xff0c;但是也可以继续配置。我目前使用的kafka版本是kafka_2.12-3.0.0.tgz&#xff0c;其中前面的2.12表示是使用该版本的scala语言进行编写的&#xff0c;而后面的3.00才是kafka当前的版本。 通过百度网盘分享…...

Spark中的宽窄依赖

一、什么是依赖关系 这里通过一张图来解释&#xff1a; result_rdd是由tuple_rdd使用reduceByKey算子得到的&#xff0c; 而tuple_rdd是由word_rdd使用map算子得到的&#xff0c;word_rdd又是由input_rdd使用flatMap算子得到的。它们之间的关系就称为依赖关系&#xff01; 二…...

centos 7 部署awstats 网站访问检测

一、基础环境准备&#xff08;两种安装方式都要做&#xff09; bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats&#xff0…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?

uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件&#xff0c;用于在原生应用中加载 HTML 页面&#xff1a; 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

stm32wle5 lpuart DMA数据不接收

配置波特率9600时&#xff0c;需要使用外部低速晶振...

小智AI+MCP

什么是小智AI和MCP 如果还不清楚的先看往期文章 手搓小智AI聊天机器人 MCP 深度解析&#xff1a;AI 的USB接口 如何使用小智MCP 1.刷支持mcp的小智固件 2.下载官方MCP的示例代码 Github&#xff1a;https://github.com/78/mcp-calculator 安这个步骤执行 其中MCP_ENDPOI…...

leetcode_69.x的平方根

题目如下 &#xff1a; 看到题 &#xff0c;我们最原始的想法就是暴力解决: for(long long i 0;i<INT_MAX;i){if(i*ix){return i;}else if((i*i>x)&&((i-1)*(i-1)<x)){return i-1;}}我们直接开始遍历&#xff0c;我们是整数的平方根&#xff0c;所以我们分两…...