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

news/2024/10/22 12:31:47/
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;//标志位置1recv_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			   //串口输出PA9GPIO_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			 //串口输入PA10GPIO_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); //初始化串口1USART_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;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;		//响应优先级3NVIC_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;//标志位置1recv_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();//回显数据包}}
}


http://www.ppmy.cn/news/1259752.html

相关文章

1.1 计算机和编程语言

计算机与编程语言的用处 计算机与大家的生活息息相关&#xff0c;例如银行的ATM机就是计算机、日常使用的手机等。大家大部分情况都是使用现有的软件&#xff0c;只有在特定场景、特定需求的环境下才会编写软件 课程目的 计算机是怎么工作的计算机擅长干什么&#xff0c;计算…

[传智杯 #4 初赛] 萝卜数据库

题目描述 花栗鼠很喜欢偷吃生产队的大萝卜&#xff0c;因此花栗鼠科技大学正在研究一种新型的数据库&#xff0c;叫做萝卜数据库。 具体来说&#xff0c;它支持 k(1≤k≤100) 个字段&#xff0c;每个字段名都是整数&#xff0c;里面存储的数值也都是整数。 现在你支持如下操…

*p++和(*p)++的区别

*p和(*p)的区别 *和是同优先级操作符&#xff0c;且都是从右至左结合的 ∗ * ∗p&#xff1a;取p所指单元的值&#xff0c;p指向下一单元&#xff0c;即p自加1&#xff0c;然后p指向下一个地址。和 (p)意思一样 (*p)&#xff1a;()的优先级比和都高&#xff0c;所以作用在()内…

IOday4作业

使用两个子进程完成两个文件的拷贝&#xff0c;子进程1拷贝前一半内容&#xff0c;子进程2拷贝后一半内容&#xff0c;父进程用于回收两个子进程的资源 #include<myhead.h>int main(int argc, const char *argv[]) {int pid-1;int fd -1;int fa -1;int fb -1;//求出文…

【MATLAB源码-第96期】基于simulink的光伏逆变器仿真,光伏,boost,逆变器(IGBT)。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 1. 光伏单元&#xff08;PV Cell&#xff09; 工作原理&#xff1a;光伏单元通过光电效应将太阳光转换为直流电。它们的输出取决于光照强度、单元温度和负载条件。Simulink建模&#xff1a;在Simulink中&#xff0c;光伏单元…

超级详细的 Docker Desktop 安装 Keycloak

一、Keycloak 简介 收先先來看一下Keycloak的基本資訊: 名稱: Keycloak開發使用的程式語言: Java公用: 單點登入驗證與授權工具許可協議: Apache License 2.0公開倉庫: https://github.com/keycloak/keycloak官方網站: https://www.keycloak.org撰寫當下最新版本: 15.0.2 (20…

TS版LangChain实战:基于文档的增强检索(RAG) | 京东云技术团队

LangChain LangChain是一个以 LLM &#xff08;大语言模型&#xff09;模型为核心的开发框架&#xff0c;LangChain的主要特性&#xff1a; 可以连接多种数据源&#xff0c;比如网页链接、本地PDF文件、向量数据库等允许语言模型与其环境交互封装了Model I/O&#xff08;输入…

如何设置不同的网页标题(react)

通常&#xff0c;当我们写一个h5或者网站时&#xff0c;需要根据页面的业务来自定义网页标题。 这个本来是在入口html文件中设置的&#xff0c;但唯一。 <head><meta charset"UTF-8" /><!-- <link rel"icon" type"image/svgxml&qu…