STM32串口接收数据包(自定义帧头帧尾)

1、基本概述

     本实验基于stm32c8t6单片机,串口作为基础且重要的外设,具有广泛的应用。本文主要理解串口数据包的发送与接收是如何实现的,重要的是理解程序的实现思路。

2、关键程序

定义好需要用到的变量:

uint8_t rxd_buf[4];//定义数组接收数据包,定长
uint8_t rxd_flag = 0;//接收标志
uint8_t rxd_index = 0;//接收索引

 程序理解:

程序放在串口中断函数里面实现。

首先,我们需要定义一个变量用于接收调试串口发过来的数据

u8 recv_dat;

 recv_dat =USART_ReceiveData(USART1);   USART_ReceiveData()是自带的函数,不需要我们定义,我们使用变量recv_dat接收上位机发送的数据。

2.1接收到数据后,进入switch判断,第一次默认从case 0进入。

2.2如果接收到帧头0xFE,将recv_state置1和索引置0,否则继续等待。

recv_state=1后就进入case 1中,将数据一个一个存入数组rxd_buf[4]中,即rxd_index++。然后我们需要判断是否接收完数据,我这个是接收4个定长数据。

2.3判断接收数据完成后,判断是否接收到帧尾0xFF,接收到帧尾后,将标志位置1,这个标志位是为了方便我们在其它程序里判断执行其它功能。记得将状态清零recv_state =0以接收下一包数据。

void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 recv_dat;
	static uint8_t recv_state = 0;//默认从索引0开始
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断
	{
		recv_dat =USART_ReceiveData(USART1);//(USART1->DR);	//读取接收到的数据
		switch(recv_state)
		{ 
			case 0:
                  if(recv_dat == 0XFE)//接收到包头
				  {
				     recv_state =1;//切换状态
					 rxd_index = 0;
				  }
				  else
				  {
				     recv_state =0;//切换状态
				  }
			      break;
			case 1:
                  rxd_buf[rxd_index]=recv_dat;//接收字符
			      rxd_index++;
			      if(rxd_index>=4)//判断是否接收数据包完成1
				  {
				     recv_state =2;//切换状态
				  }
			      break;
			case 2:
                  if(recv_dat == 0XFF)//接收到包尾
				  {
				     rxd_flag = 1;//标志位置1
					 recv_state =0;//并将状态清零,接收下一包数据 
				  }
			      break;
		}
		USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除中断标志位	 
	
	} 
}

完整程序: 

usart.c

#include "usart.h"	
#include "led.h"	


uint8_t txd_buf[4]={1,2,3,4};//数据包
uint8_t rxd_buf[4];//定义数组接收数据包,定长
uint8_t rxd_flag = 0;//接收标志
uint8_t rxd_index = 0;//接收索引


/*******************************************************************************
* 函 数 名         : USART1_Init
* 函数功能		   : USART1初始化函数
* 输    入         : bound:波特率
* 输    出         : 无
*******************************************************************************/ 
void USART1_Init(u32 bound)
{
   //GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
 
	
	/*  配置GPIO的模式和IO口 */
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//TX			   //串口输出PA9
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;	    //复用推挽输出
	GPIO_Init(GPIOA,&GPIO_InitStructure);  /* 初始化串口输入IO */
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//RX			 //串口输入PA10
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;		 //浮空输入
	GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化GPIO */
	

	//USART1 初始化设置
	USART_InitStructure.USART_BaudRate = bound;//波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
	USART_Init(USART1, &USART_InitStructure); //初始化串口1
	
	USART_Cmd(USART1, ENABLE);  //使能串口1 
	
	USART_ClearFlag(USART1, USART_FLAG_TC);
		
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断

	//Usart1 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;		//响应优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器、	
}


//重定义printf函数
int fputc(int ch,FILE *p)  //函数默认的,在使用printf函数时自动调用
{
	USART_SendData(USART1,(u8)ch);	
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
	return ch;
}

//发送字符
void send_byte(uint8_t byte)
{
   USART_SendData(USART1,byte);
   while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);//等待发送完成
}

//发送字符串
void send_string(uint8_t *str)//为什么是指针参数
{
   while(*str!='\0') //当字符串不为空时
   {
      send_byte(*str++);
   }
 
}

//发送一组数据
void send_buf(uint8_t *buf,uint16_t len)
{
    uint16_t i;
	for(i=0;i<len;i++)
	{
	   send_byte(buf[i]);
	}
}
	
//定义数据包
void send_pack(void)
{
    send_byte(0xFE);//包头
	send_buf(rxd_buf,4);//数据包
	send_byte(0xFF);//包尾
}


/*******************************************************************************
* 函 数 名         : USART1_IRQHandler
* 函数功能		   : USART1中断函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/ 
void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 recv_dat;
	static uint8_t recv_state = 0;//默认从索引0开始
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断
	{
		recv_dat =USART_ReceiveData(USART1);//(USART1->DR);	//读取接收到的数据
		switch(recv_state)
		{ 
			case 0:
                  if(recv_dat == 0XFE)//接收到包头
				  {
				     recv_state =1;//切换状态
					 rxd_index = 0;
				  }
				  else
				  {
				     recv_state =0;//切换状态
				  }
			      break;
			case 1:
                  rxd_buf[rxd_index]=recv_dat;//接收字符
			      rxd_index++;
			      if(rxd_index>=4)//判断是否接收数据包完成1
				  {
				     recv_state =2;//切换状态
				  }
			      break;
			case 2:
                  if(recv_dat == 0XFF)//接收到包尾
				  {
				     rxd_flag = 1;//标志位置1
					 recv_state =0;//并将状态清零,接收下一包数据 
				  }
			      break;
		}
		USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除中断标志位	 
	
	} 
}

usart.h

#ifndef _usart_H
#define _usart_H

#include "system.h" 
#include "stdio.h" 


extern uint8_t rxd_flag;//接收标志

void USART1_Init(u32 bound);
void send_byte(uint8_t byte);
void send_string(uint8_t *str);
void send_buf(uint8_t *buf,uint16_t len);
void send_pack(void);


#endif


main.c

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "pwm.h"
#include "usart.h"
#include "key.h"
#include "oled.h"


int main()
{
	SysTick_Init(72);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断优先级分组
	USART1_Init(115200);
	USART2_Init(115200);
	OLED_Init();
	KEY_Init();
	LED_Init();
	send_string("hello stm32");
	while(1)
	{	
        if(rxd_flag == 1)
		{
			rxd_flag = 0;
			send_pack();//回显数据包
		
		}
		
		

		
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Gti GUI添加标签

通过Git Gui打开项目&#xff0c;通过菜单打开分支历史&#xff0c;我这里是名为"develop"的分支 选中需要打标签的commit&#xff0c;右键-Create tag即可 但貌似无法删除标签&#xff0c;只能通过git bash

MySQL之锁

MySQL之锁 锁是计算机在执行多线程或线程时用于并发访问同一共享资源时的同步机制&#xff0c;MySQL中的锁是在服务器层或者存储引擎层实现的&#xff0c;保证了数据访问的一致性与有效性 MySQL锁可以按模式分类为&#xff1a;乐观锁与悲观锁。 按粒度分可以分为全局锁、表级锁…

Python pass语句及其作用及(for和while)循环嵌套及用法

Python pass语句及其作用 很多程序都提供了“空语句”支持&#xff0c;Python 也不例外&#xff0c;Python 的 pass 语句就是空语句。 有时候程序需要占一个位、放一条语句&#xff0c;但又不希望这条语句做任何事情&#xff0c;此时就可通过 pass 语句来实现。通过使用 pass …

设计一算法,对单链表实现就地逆置

对单链表逆置&#xff0c;要联想到单链表的头插性质 举个例子&#xff1a;现在有一个空链表&#xff0c;我们依次对它进行头插123 那么形成的链表是321&#xff0c;这样就形成了逆置 //单链表就地逆置 //思路&#xff1a;把原表接到一个新表上&#xff0c;然后对原表进行头插 …

Nero刻录光盘软件-极好用

目录 一、下载Nero 二、软件安装 三、刻录数据 前言 刻录之前准备一张新的光盘&#xff0c;之前一旦使用过&#xff0c;就无法刻录&#xff0c;一定要新的光盘。 一、下载Nero nero官网下载地址&#xff1a;Nero下载 csdn免费下载地址&#xff1a;https://download.csdn.…

今日现货黄金最新建议

近期现货黄金价格再度逼近历史高位&#xff0c;很多本来在场外观望的投资者&#xff0c;都纷纷希望进场一试身手。然而大涨大跌的行情并不是很适合新手投资者参与&#xff0c;如果大家还没做好技术上的准备&#xff0c;可以多听听正规交易平台的专业人士的意见。 在正式入市之前…

重磅!苹果官方发布大模型框架:一个可以充分利用苹果统一内存的新的大模型框架MLX,你的MacBook可以一键运行LLaMA了

本文来自DataLearnerAI官方网站&#xff1a;重磅&#xff01;苹果官方发布大模型框架&#xff1a;一个可以充分利用苹果统一内存的新的大模型框架MLX&#xff0c;你的MacBook可以一键运行LLaMA了 | 数据学习者官方网站(Datalearner)https://www.datalearner.com/blog/105170187…

西工大计算机学院计算机系统基础实验一(函数编写1~10)

还是那句话&#xff0c;千万不要慌&#xff0c;千万不要着急&#xff0c;耐下性子慢慢来&#xff0c;一步一个脚印&#xff0c;把基础打的牢牢的&#xff0c;一样不比那些人差。回到实验本身&#xff0c;自从​​​​​​按照西工大计算机学院计算机系统基础实验一&#xff08;…

CMMI5大成熟度等级和4大过程域

CMMI&#xff08;Capability Maturity Model Integration&#xff0c;能力成熟度模型集成&#xff09;模型系列是帮助组织改进其过程的最佳实践的集合。这些模型由来自产业界、政府以及软件工程研究所&#xff08;Software Engineering Institute&#xff0c; SEI&#xff09;的…

2024年十大最好猫罐头有哪些?2024年10款最好的猫罐头盘点

我发现不少人有这样的困扰&#xff01;买到各种数值都很好的猫罐头后&#xff0c;猫咪一点都不吃。或者是猫咪吃了猫罐头之后&#xff0c;吃了一段时间后就软便身体不舒服。 通过本文&#xff0c;我将与大家盘点2024年10款最好的猫罐头&#xff0c;并提供一些选购猫罐头的小妙招…

JavaSE基础50题:6. 求出0~999之间的所有“水仙花数”并输出

概念 “水仙花数”是指一个三位数&#xff0c;其各位数字的立方和确好等于该数本身。 如&#xff1a;153 135333,则153是一个“水仙花数”。 【方法】 如何获得每一位的数&#xff1a;如(153) 个位: 153 % 10 3 十位: 153 / 10 15 15 % 10 5 百位: 153 / 100 1 代码 pu…

SimplePIR——目前最快单服务器匿踪查询方案

一、介绍 这篇论文旨在实现高效的单服务器隐私信息检索&#xff08;PIR&#xff09;方案&#xff0c;以解决在保护用户隐私的同时快速检索数据库的问题。为了实现这一目标&#xff0c;论文提出了两种新的PIR方案&#xff1a;SimplePIR和DoublePIR。这两种方案的实现基于学习与错…

浅谈基于Pytest框架的自动化测试开发实践

Pytest是Python的一种易用、高效和灵活的单元测试框架&#xff0c;可以支持单元测试和功能测试。本文不以介绍Pytest工具本身为目的&#xff0c;而是以一个实际的API测试项目为例&#xff0c;将Pytest的功能应用到实际的测试工程实践中&#xff0c;教大家将Pytest用起来。 在开…

JFrog Artifactory—高性能软件制品管理仓库

产品概述 JFrog Artifactory是一个可扩展的通用二进制存储库管理器&#xff0c;可在整个应用程序开发和交付过程中自动管理工件和依赖项。JFrog Artifactory支持大多数开发语言&#xff0c;是整个DevOps流水线中大多数软件包、容器映像和Helm图表的单一数据源。Artifactory对元…

二叉搜索树——模拟

对于一个无穷的满二叉排序树&#xff08;如图&#xff09;&#xff0c;节点的编号是1,2,3&#xff0c;…。对于一棵树根为X的子树&#xff0c;沿着左节点一直往下到最后一层&#xff0c;可以获得该子树编号最小的节点&#xff1b;沿着右节点一直往下到最后一层&#xff0c;可以…

Java TCP协议实现一对一聊天与UDP协议实现群聊案例

JavaTCP协议实现一对一聊天与UDP协议实现群聊案例 1.TCP协议实现一对一聊天 1.1服务端运行结果 1.2客服端运行结果 1.3代码汇总 服务端 package twentyone;import java.io.IOException; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.…

2023.12.5 关于 Spring Boot 统一数据格式返回

目录 引言 统一数据格式 实例理解 特殊 String 类型处理 实例理解 分析返回的流程 补充知识 分析报错原因 解决方案一 解决方案二 最终测试 引言 统一数据格式能 方便前端程序员更好的接收和解析后端返回的数据统一数据格式能 降低约定前后端交互接口的成本&#xf…

Vue2中v-html引发的安全问题

前言&#xff1a;v-html指令 1.作用&#xff1a;向指定节点中渲染包含html结构的内容。 2.与插值语法的区别&#xff1a; (1).v-html会替换掉节点中所有的内容&#xff0c;{{xx}}则不会。 (2).v-html可以识别html结构。 3.严重注意&#xff1a;v-html有安全性问题&#xff0…

搭梯子之后电脑连接WIFI打不开浏览器网页:远程计算机或者设备不接受连接

问题描述&#xff1a; 打不开网页&#xff0c;但是能正常使用微信等app windows网络诊断&#xff1a; 远程计算机或者设备不接受连接 解决办法&#xff1a; 电脑搜索【internet选项】 进入连接&#xff0c;点击局域网设置&#xff0c;将里面的代理服务器选项关掉就可以正常打开…

总结|哪些平台有大模型知识库的Web API服务

截止2023/12/6 笔者个人的调研&#xff0c;有三家有大模型知识库的web api服务&#xff1a; 平台类型文档数量文档上传并解析的结构api情况返回页码文心一言插件版多文档有问答api&#xff0c;文档上传是通过网页进行上传有&#xff0c;而且是具体的chunk id&#xff0c;需要设…