【STM32单片机】#10.5 串口数据包

主要参考学习资料:

B站@江协科技

STM32入门教程-2023版 细致讲解 中文字幕

开发资料下载链接:https://pan.baidu.com/s/1h_UjuQKDX9IpP-U1Effbsw?pwd=dspb

单片机套装:STM32F103C8T6开发板单片机C6T6核心板 实验板最小系统板套件科协

实验:

  • 串口收发HEX数据包
  • 串口收发文本数据包

目录

  • 数据包
    • HEX数据包
    • 文本数据包
  • 数据包收发流程
    • HEX数据包
    • 文本数据包
  • 实验22 串口收发HEX数据包
    • 接线图
    • Serial驱动
    • 主程序
  • 实验23 串口收发文本数据包
    • 接线图
    • Serial驱动
    • 主程序

数据包

数据包的作用是进行多字节的数据通信,将属于同一批的数据进行打包和分割,以便接收方识别。打包和分割的方法可以自行设计。

HEX数据包

HEX数据包直接传输十六进制数据,本实验规定使用FF作为包头、FE作为包尾的方式打包和分割数据包。

针对包头包尾和载荷数据重复的问题,一种解决方法是限制载荷数据的范围,在发送时对数据进行限幅,使其不会重复;第二种方法是尽量使用固定长度的数据包,只要通过包头包尾对齐了数据,就可以严格知道哪个数据时包头包尾,哪个数据是载荷数据;第三种方法是增加包头包尾的数量,并让尽量使用载荷数据不会出现的状态,例如用FF、FE作为包头,FD、FC作为包尾。

包头和包尾并非都需要,可以只保留包头,删除包尾,但载荷与包头重复的问题也会更严重。

文本数据包

在文本数据包中,每个字节经过了一层编码和译码,以文本格式表现。字符在包头包尾的选择中可以有效避免与载荷数据重复的问题,本实验规定以@作为包头,以换行字符\r\n作为包尾。载荷数据接收为字符串,软件通过对字符串判断和操作可以实现各种指令控制功能。

HEX数据包的优点是传输直接,解析简单,适合陀螺仪、传感器等模块发送原始数据,缺点是灵活性不足,载荷容易和包头包尾重复。文本数据包的优点是数据直观易理解,非常灵活,适合通过指令进行人机交互的场合,缺点是解析效率低。

数据包收发流程

数据包发送的过程很简单,只需定义数组,填充数据并发送。此处主要介绍接收流程。

HEX数据包

在接收数据中,针对包头、数据载荷和包尾三种状态需要不同的处理逻辑,因此我们使用状态机思维设计程序,通过一个能记住不同状态的机制,在不同状态执行不同的操作,同时还要进行状态的合理转移。上图为接收固定包长含包头包尾的HEX数据包的状态转移图,变量S标志不同的状态,在满足相应的条件时跟随箭头方向转移。

文本数据包

实验22 串口收发HEX数据包

接线图

Serial驱动

在实验21的基础上更改。

Serial.h

#ifndef __SERIAL_H
#define __SERIAL_H#include <stdio.h>//允许外部调用发送和接收数据包
extern uint8_t Serial_TxPacket[];
extern uint8_t Serial_RxPacket[];void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);
void Serial_SendPacket(void);
uint8_t Serial_GetRxFlag(void);#endif

Serial.c

只展示更改部分:

//在驱动中暂存接受数据包
uint8_t Serial_TxPacket[4];
uint8_t Serial_RxPacket[4];
uint8_t Serial_RxFlag;//新增发送数据包函数
void Serial_SendPacket(void)
{//发送包头Serial_SendByte(0xFF);//发送数据载荷Serial_SendArray(Serial_TxPacket, 4);//发送包尾Serial_SendByte(0xFE);
}//删去Serial_GetRxData函数
//更改中断函数
void USART1_IRQHandler(void)
{//状态变量static uint8_t RxState = 0;//指示数据接收到第几个static uint8_t pRxPacket = 0;if(USART_GetITStatus(USART1, USART_IT_RXNE)){//接收单个字节uint8_t RxData = USART_ReceiveData(USART1);//根据状态不同进入不同的处理程序switch(RxState){//等待包头case 0://收到包头转移状态if(RxData == 0xFF)RxState = 1;break;//接收数据case 1:Serial_RxPacket[pRxPacket] = RxData;pRxPacket ++;//接收满4个数据if(pRxPacket >= 4){RxState = 2;pRxPacket = 0;}break;//等待包尾case 2://收到包尾if(RxData == 0xFE){RxState = 0;//置接收标志位Serial_RxFlag = 1;}break;}USART_ClearITPendingBit(USART1, USART_IT_RXNE);}
}

主程序

#include "stm32f10x.h" 
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "Key.h"uint8_t KeyNum;int main(void)
{OLED_Init();Key_Init();Serial_Init();OLED_ShowString(1, 1, "TxPacket");OLED_ShowString(3, 1, "RxPacket");//初始化待发送数据包Serial_TxPacket[0] = 0x01;Serial_TxPacket[1] = 0x02;Serial_TxPacket[2] = 0x03;Serial_TxPacket[3] = 0x04;while(1){KeyNum = Key_GetNum();//每次按下按键使发送数据包加一并发送if(KeyNum){Serial_TxPacket[0] ++;Serial_TxPacket[1] ++;Serial_TxPacket[2] ++;Serial_TxPacket[3] ++;Serial_SendPacket();OLED_ShowHexNum(2, 1, Serial_TxPacket[0], 2);OLED_ShowHexNum(2, 4, Serial_TxPacket[1], 2);OLED_ShowHexNum(2, 7, Serial_TxPacket[2], 2);OLED_ShowHexNum(2, 10, Serial_TxPacket[3], 2);}//判断标志位接收数据包if(Serial_GetRxFlag()){OLED_ShowHexNum(4, 1, Serial_RxPacket[0], 2);OLED_ShowHexNum(4, 4, Serial_RxPacket[1], 2);OLED_ShowHexNum(4, 7, Serial_RxPacket[2], 2);OLED_ShowHexNum(4, 10, Serial_RxPacket[3], 2);}	}
}

实验23 串口收发文本数据包

实现功能:使用串口发送文本指令控制LED亮灭。

接线图

Serial驱动

在实验22的基础上修改。

Serial.h

#ifndef __SERIAL_H
#define __SERIAL_H#include <stdio.h>extern char Serial_RxPacket[];
//声明标志位为外部可调用
extern uint8_t Serial_RxFlag;void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);#endif

Serial.c

只展示更改部分:

//接收数据类型改为char,上限100个字符
char Serial_RxPacket[100];
uint8_t Serial_RxFlag;
//删除发送数据包函数和获取标志位函数
void USART1_IRQHandler(void)
{static uint8_t RxState = 0;static uint8_t pRxPacket = 0;if(USART_GetITStatus(USART1, USART_IT_RXNE)){uint8_t RxData = USART_ReceiveData(USART1);switch(RxState){case 0://更改包头//为防止程序处理数据包不及时导致数据包错位//需判断接收标志位清零后再接收下一个数据包if(RxData == '@' && !Serial_RxFlag){RxState = 1;pRxPacket = 0;}break;case 1://判断包尾是否到来if(RxData == '\r')RxState = 2;else{Serial_RxPacket[pRxPacket] = RxData;pRxPacket ++;}//无需检测数据数量break;case 2://等待第二个包尾if(RxData == '\n'){RxState = 0;//添加字符串结束标志Serial_RxPacket[pRxPacket] = '\0';Serial_RxFlag = 1;}break;}USART_ClearITPendingBit(USART1, USART_IT_RXNE);}
}

主程序

#include "stm32f10x.h" 
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "LED.h"
//判断字符串的官方库
#include <string.h>int main(void)
{OLED_Init();LED_Init();Serial_Init();OLED_ShowString(1, 1, "TxPacket");OLED_ShowString(3, 1, "RxPacket");while(1){if(Serial_RxFlag){//由于字符串长度不确定,需擦除显示行再覆写下一个字符串OLED_ShowString(4, 1, "                ");OLED_ShowString(4, 1, Serial_RxPacket);//字符串相等返回0//开灯指令if(!strcmp(Serial_RxPacket, "LED_ON")){LED1_ON();//回传反馈Serial_SendString("LED_ON_OK\r\n");OLED_ShowString(2, 1, "                ");OLED_ShowString(2, 1, "LED_ON_OK");}//关灯指令else if(!strcmp(Serial_RxPacket, "LED_OFF")){LED1_OFF();Serial_SendString("LED_OFF_OK\r\n");OLED_ShowString(2, 1, "                ");OLED_ShowString(2, 1, "LED_OFF_OK");}//错误指令else{Serial_SendString("ERROR_COMMAND\r\n");OLED_ShowString(2, 1, "                ");OLED_ShowString(2, 1, "ERROR_COMMAND");				}//清空标志位Serial_RxFlag = 0;}}
}

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

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

相关文章

百度暑期实习岗位超3000个,AI相关岗位占比87%,近屿智能携AIGC课程加速人才输出

今年3月&#xff0c;百度重磅发布3000暑期实习岗位&#xff0c;聚焦大模型、机器学习、自动驾驶等AI方向的岗位比例高达87%。此次实习岗位涉及技术研发、产品策划、专业服务、管理支持、政企解决方案等四大类别&#xff0c;覆盖超300个岗位细分方向。值得一提的是&#xff0c;百…

vue3 + element-plus中el-dialog对话框滚动条回到顶部

对话框滚动条回到顶部 1、需要对话框显示后 2、使用 nextTick 等待 Dom 更新完毕 3、通过开发者工具追查到滚动条对应的标签及class“el-overlay-dialog” 4、设置属性 scrollTop 0 或者 执行方法 scrollTo(0, 0) // 对话框显示标识 const dialogVisible ref(false); //…

C++学习之游戏服务器开发十一DOCKER的基本使用

目录 1.多实例部署方案 2.容器的概念 3.docker初识 4.docker仓库 5.docker镜像 6.docker容器 7.docker和虚拟机的区别 8.docker命令解释 9.dockerfile构建镜像 10.离线分发镜像 1.多实例部署方案 redis 命令&#xff08; redis-cli XXXX &#xff09; set key value:…

2025.4.21总结

工作&#xff1a;开了一场关于大模型版本的会议&#xff0c;回归一个问题单&#xff0c;提了两个单&#xff0c;把用例都执行完。如今都四月中旬了&#xff0c;上班年快要结束了&#xff0c;该到了冲刺KPI的时候了。 今日思考&#xff1a;刷到了jack叔叔的视频&#xff0c;讲了…

vite安装及使用

没特殊要求的项目,还是怎么简单怎么来╮(╯▽╰)╭ 一、Vite 基础知识 1. 什么是 Vite? Vite 是一个前端构建工具,专注于开发服务器速度和优化构建过程。特点: 快速冷启动:利用 ES 模块的原生支持,实现快速的开发服务器启动。即时热更新:在开发过程中,修改代码后可以…

ubuntu下gcc/g++安装及不同版本切换

1. 查看当前gcc版本 $ gcc --version# 查看当前系统中已安装版本 $ ls /usr/bin/gcc*2. 安装新版本gcc $ sudo apt-get update# 这里以版本12为依据&#xff08;也可以通过源码方式安装&#xff0c;请自行Google&#xff01;&#xff09; $ sudo apt-get install -y gcc-12 g…

cdq 系列 题解

从二维数点&#xff08;二维偏序&#xff09;到三维偏序。 用 cdq 分治可以解决二维数点问题。 1.洛谷 P1908 逆序对 题意 求所有数对 ( i , j ) (i,j) (i,j) 的个数&#xff0c;满足 i < j i<j i<j 且 a i > a j a_i>a_j ai​>aj​。 1 ≤ n ≤ 5 1…

计算机组成与体系结构:内存接口(Memory Interface)

目录 什么是内存接口 &#xff1f; 为什么需要特别设计“接口”&#xff1f; 什么是 MIPS&#xff1f;为什么它和内存接口有关&#xff1f; 内存接口的两种访问方式 串行访问&#xff08;Serial Access Model&#xff09; 并行访问&#xff08;Parallel Access Model&…

Java面试(2025)—— Spring MVC

什么是Spring MVC Spring MVC 是 Spring 框架的一个 基于 Java 的 Web 开发模块&#xff0c;它实现了 MVC&#xff08;Model-View-Controller&#xff09;架构模式&#xff0c;用于构建灵活、松耦合的 Web 应用程序。 它是 Spring 生态的核心组件之一&#xff0c;通过简化 HTT…

天翼云手机断开连接2小时关机

2025-04-21 天翼云手机断开连接2小时自动 天翼云手机 4元1个月 天翼云手机永不关机 天翼云手机不休眠 天翼云手机断开连接时&#xff0c;界面显示&#xff1a;离线运行&#xff0c;2小时后自动关机 电脑每小时自动连接一次 手机每小时自动连接一次

Redis——数据结构

目录 1.动态字符串SDS 1.1SDS底层源码 1.2 SDS动态扩容 1.3动态字符串SDS优点 2.IntSet 2.1底层结构 2.2有序性 2.3.IntSet结构扩容 2.4总结 3.Dict 3.1底层结构 3.2.Dict扩容 3.3Dict收缩 3.4.Dict的rehash 1.分配空间 2. 设置 rehashidx 3. 渐进式 rehash…

C++ GPU并行计算开发实战:利用CUDA/OpenCL加速粒子系统与流体模拟

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家、CSDN平台优质创作者&#xff0c;高级开发工程师&#xff0c;数学专业&#xff0c;10年以上C/C, C#, Java等多种编程语言开发经验&#xff0c;拥有高级工程师证书&#xff1b;擅长C/C、C#等开发语言&#xff0c;熟悉Java常用开…

LeetCode算法题(Go语言实现)_54

题目 给你两个正整数数组 spells 和 potions &#xff0c;长度分别为 n 和 m &#xff0c;其中 spells[i] 表示第 i 个咒语的能量强度&#xff0c;potions[j] 表示第 j 瓶药水的能量强度。 同时给你一个整数 success 。一个咒语和药水的能量强度 相乘 如果 大于等于 success &a…

内网穿透快解析免费开放硬件集成SDK

一、行业问题 随着物联网技术的发展&#xff0c;符合用户需求的智能硬件设备被广泛的应用到各个领域&#xff0c;而智能设备的远程运维管理也是企业用户遇到的问题 二、快解析内网穿透解决方案 快解析是一款内网穿透产品&#xff0c;可以实现内网资源在外网访问&#xff0c;…

Python+Word实现周报自动化的完整流程

一、技术方案概述 自动化报表解决方案基于以下技术组件&#xff1a; Python 作为核心编程语言python-docx 库用于处理 Word 文档pandas 库用于数据处理和分析matplotlib 或 plotly 库用于数据可视化Word 模版作为报表的基础格式 这种方案的优势在于&#xff1a;保留了 Word 文…

elastic/go-elasticsearch与olivere/elastic

在 Go 语言中&#xff0c;与 Elasticsearch 交互的客户端库有多种选择&#xff0c;其中 github.com/elastic/go-elasticsearch/v8 和 github.com/olivere/elastic/v7 是两个常用的库。这两个库的功能和用途有一些差异&#xff0c;以下是它们的详细对比&#xff1a; 1. github.c…

deepseek + kimi制作PPT

目录 一、kimi简介二、deepseek生成内容三、生成PPT四、编辑PPT 一、kimi简介 kimi是一款只能ppt生成器&#xff0c;擅长将文本内容生成PPT。 在这里&#xff0c;​​DeepSeek 负责内容生成与逻辑梳理​​&#xff0c;​​Kimi 优化表达与提供设计建议​​。 二、deepseek生…

【八大排序】冒泡、直接选择、直接插入、希尔、堆、归并、快速、计数排序

目录 一、排序的介绍二、排序算法的实现2.1 直接插入排序2.2 希尔排序2.3 直接选择排序2.4 堆排序2.5 冒泡排序2.6 快速排序2.7 归并排序2.8 比较排序算法的性能展示2.9 计数排序 个人主页<— 数据结构专栏<— 一、排序的介绍 我们的生活中有很多排序&#xff0c;比如像…

linux 查询目录文件大小

​ 在 Linux 系统中&#xff0c;准确地掌握目录和文件的大小对于磁盘空间管理至关重要。​本文将详细介绍如何使用 du&#xff08;disk usage&#xff09;命令逐层查看目录和文件的大小&#xff0c;并结合 sort 命令对结果进行排序&#xff0c;以便有效地识别和管理占用…

如何简单几步使用 FFmpeg 将任何音频转为 MP3?

在多媒体处理领域&#xff0c;FFmpeg 以其强大的功能和灵活性而闻名。无论是视频编辑、音频转换还是流媒体处理&#xff0c;它都是专业人士和技术爱好者的首选工具之一。在这篇文章中简鹿办公将重点介绍如何使用 FFmpeg 进行音频格式转换&#xff0c;提供一些常用的转换方式&am…