单片机串口接收状态机STM32

news2024/11/27 2:24:22

单片机串口接收状态机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,      // 等待帧头AA
    STATE_WAIT_FOR_HEAD2,      // 等待帧头55
    STATE_WAIT_FOR_LENGTH,     // 等待命令包长度
    STATE_RECEIVE_DATA,        // 接收命令包数据
    STATE_WAIT_FOR_CHECKSUM1,  // 等待校验和字节1
    STATE_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单片机,国产单片机,在项目开发完成进行降本的时候也是可能会改到这种方式的,如果有不规范的地方请留言,我会更改。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2231864.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

信息学科平台系统开发:基于Spring Boot的最佳实践

3系统分析 3.1可行性分析 通过对本基于保密信息学科平台系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本基于保密信息学科平台系统采用Spring Boot框架&a…

探索 ONLYOFFICE 8.2 版本:更高效、更安全的云端办公新体验

引言 在当今这个快节奏的时代&#xff0c;信息技术的发展已经深刻改变了我们的工作方式。从传统的纸质文件到电子文档&#xff0c;再到如今的云端协作&#xff0c;每一步技术进步都代表着效率的飞跃。尤其在后疫情时代&#xff0c;远程办公成为常态&#xff0c;如何保持团队之间…

51c自动驾驶~合集4

我自己的原文哦~ https://blog.51cto.com/whaosoft/12413878 #MCTrack 迈驰&旷视最新MCTrack&#xff1a;KITTI/nuScenes/Waymo三榜单SOTA paper&#xff1a;MCTrack: A Unified 3D Multi-Object Tracking Framework for Autonomous Driving code&#xff1a;https://gi…

STM32HAL-最简单的长、短、多击按键框架(多按键)

概述 本文章使用最简单的写法实现长、短、多击按键框架,非常适合移植各类型单片机,特别是资源少的芯片上。接下来将在stm32单片机上实现,只需占用1个定时器作为时钟扫描按键即可。 一、开发环境 1、硬件平台 STM32F401CEU6 内部Flash : 512Kbytes,SARM …

【论文精读】LPT: Long-tailed prompt tuning for image classification

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;论文精读_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 摘要 2. …

队列的模拟实现

概念&#xff1a; 队列 &#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出 FIFO(First In First Out) 入队列&#xff1a;进行插入操作的一端称为 队尾&#xff08; Tail/Rear &#xff09; 出队列&a…

Centos安装配置Jenkins

下载安装 注意&#xff1a;推荐的LTS版本对部分插件不适配&#xff0c;直接用最新的版本&#xff0c;jenkins还需要用到git和maven&#xff0c;服务器上已经安装&#xff0c;可查看参考文档[1]、[2]&#xff0c;本次不再演示 访问开始使用 Jenkins 下载jenkins 上传至服务器…

在Python中最小化预测函数的参数

在 Python 中&#xff0c;最小化预测函数的参数通常涉及使用优化算法来调整模型的参数&#xff0c;以减少预测误差。下面介绍几种常见的方法来实现这一目标&#xff0c;主要使用 scipy 和 numpy 库。 1、问题背景 我正在尝试通过解决自己想出的问题来学习Python&#xff0c;我…

统信UOS系统应用开发

包括cpu 、内存 、安全等接口描述。 文章目录 一、内存管理非文件形式的内存动态函数库调用接口二、cpu内置安全飞腾国密加速硬件用户态驱动API说明真随机数真随机数三、cpu多核调度cpu亲和性获取接口用于cpu set集操作的相关宏定义一、内存管理 非文件形式的内存动态函数库调…

postman 获取登录接口中的返回token并设置为环境变量的方法 postman script

postman是一个比较方便的API开发调试工具&#xff0c; 我们在访问API时一般都需要设置一个token来对服务进行认证&#xff0c; 这个token一般都是通过登录接口来获取。 这个postman脚本放到登录接口的sctipt--> post-response里面即可将登陆接口中返回的token值设置到postma…

《华为工作法》读书摘记

无论做什么事情&#xff0c;首先要明确的就是做事的目标。目标是引导行动的关键&#xff0c;也是证明行动所具备的价值的前提&#xff0c;所以目标管理成了企业与个人管理的重要组成部分。 很多时候&#xff0c;勤奋、努力并不意味着就一定能把工作做好&#xff0c;也并不意味…

【大语言模型】ACL2024论文-07 BitDistiller: 释放亚4比特大型语言模型的潜力通过自蒸馏

【大语言模型】ACL2024论文-07 BitDistiller: 释放亚4比特大型语言模型的潜力通过自蒸馏 目录 文章目录 【大语言模型】ACL2024论文-07 BitDistiller: 释放亚4比特大型语言模型的潜力通过自蒸馏目录摘要研究背景问题与挑战如何解决创新点算法模型实验效果代码推荐阅读指数&…

Tomcat 和 Docker部署Java项目的区别

在 Java 项目部署中&#xff0c;Tomcat 和 Docker 是两种常见的选择。虽然它们都可以用来运行 Java 应用&#xff0c;但它们在定位、部署方式、依赖环境、资源隔离、扩展性和适用场景等方面有显著区别。 1. 功能定位 1.1 Tomcat Apache Tomcat 是一种轻量级的 Java 应用服务器…

基于SSM的学生选课系统+LW参考示例

系列文章目录 1.基于SSM的洗衣房管理系统原生微信小程序LW参考示例 2.基于SpringBoot的宠物摄影网站管理系统LW参考示例 3.基于SpringBootVue的企业人事管理系统LW参考示例 4.基于SSM的高校实验室管理系统LW参考示例 5.基于SpringBoot的二手数码回收系统原生微信小程序LW参考示…

Java I/O流详解

文章目录 I/O流概念I/O流的分类字节流&#xff08;Byte Streams&#xff09;字节字节流概述方法主要类和继承关系示例代码字节流读取文件 字符流字符流概述子类Reader1.FileReader&#xff1a;2.CharArrayReader&#xff1a;3.StringReader&#xff1a;4.InputStreamReader&…

基于Multisim数字频率计频率范围0-9999HZ电路(含仿真和报告)

【全套资料.zip】数字频率计仿真电路设计Multisim仿真设计数字电子技术 文章目录 功能一、Multisim仿真源文件二、原理文档报告资料下载【Multisim仿真报告讲解视频.zip】 功能 1.采用纯数字电路&#xff0c;非单片机。 2.频率计测量的频率范围0-9999HZ。 3.使用数码管进行频…

Python画笔案例-095 绘制鼠标画笔

1、绘制 鼠标画笔 通过 python 的turtle 库绘制 鼠标画笔,如下图: 2、实现代码 绘制 鼠标画笔,以下为实现代码: """鼠标画笔.py本程序可以用鼠标指针在屏幕上画画儿。 """ import turtlescreen = turtle.getscreen() screen.setup(

【温酒笔记】SPI

1. SPI基础 物理层 片选线 &#xff1a;选中拉低SCK: 时钟线MOSI:主出从入MISO:主入从出 协议层 CPOL:时钟极性&#xff1a;空闲电平高低 CPHA:时钟相位&#xff1a;第一个还是第二个边沿采样 2. 示例SPI-W25Q16 (见模组分类下文章)

mac电脑设置crontab定时任务,以及遇到的问题解决办法

crontab常用命令 crontab -u user&#xff1a;用来设定某个用户的crontab服务&#xff1b; crontab file&#xff1a;file是命令文件的名字,表示将file做为crontab的任务列表文件并载入crontab。如果在命令行中没有指定这个文件&#xff0c;crontab命令将接受标准输入&#xf…

MySQL中,如何定位慢查询?定位到的慢SQL如何分析?

目录 1. 慢查询发生的场景&#xff1f; 2. MySQL中&#xff0c;如何定位慢查询&#xff1f; 2.1 详细解释 3. 定位到的慢SQL如何分析&#xff1f; 3.1 详细说明 1. 慢查询发生的场景&#xff1f; 2. MySQL中&#xff0c;如何定位慢查询&#xff1f; 介绍一下当时产生问题…