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();//回显数据包
		
		}
		
		

		
	}
}

参考资源链接:[STM32单片机实现USART串口HEX数据包收发教程](https://wenku.youkuaiyun.com/doc/1xf29vmu4z?utm_source=wenku_answer2doc_content) 为了在STM32单片机上实现USART串口数据包的收发,并通过OLED显示数据包内容,同时实现按键触发发送功能,你需要具备STM32的基础知识、USART串口通信、HEX数据包格式、OLED显示屏应用和按键输入功能等。以下是一个详细的解决方案: 首先,你需要设置STM32的USART外设,配置相应的波特率、数据位、停止位和校验位。使用STM32CubeMX可以方便地完成这些配置,并生成初始化代码。 其次,定义HEX数据包格式。在本例中,数据包包括一个包FF,四个字节的中间数据,以及一个包FE。在发送和接收数据时,检查数据包是否符合这种格式来验证数据的正确性。 在编程上,你需要编写中断服务程序来处理USART接收到的数据。当接收到包FF时,开始接收接下来的字节,直到遇到包FE,这时可以认为一个完整的数据包已经接收到。 对于OLED显示,使用适合STM32的库函数来控制OLED显示屏。比如使用STM32 HAL库中的LCD显示功能,编写函数来将接收到的HEX数据包内容显示在屏幕上。 对于按键触发,配置GPIO输入端口,编写按键读取函数,实现按键的去抖动处理,并在主循环中检测按键状态,当检测到按键被按下时,发送定义好的HEX数据包。 这些步骤涉及到的编程实践包括初始化串口、编写串口接收中断服务程序、编写显示数据的函数以及按键检测逻辑。这些都需要遵循STM32的架构和编程模型,并合理利用STM32的硬件资源。 通过上述步骤,你可以实现数据的收发、显示以及按键触发发送的过程。为了更深入地了解和实践这一过程,推荐阅读《STM32单片机实现USART串口HEX数据包收发教程》,该资料详细介绍了这些操作的实现方法,并提供了实际的项目案例供学习和参考。 参考资源链接:[STM32单片机实现USART串口HEX数据包收发教程](https://wenku.youkuaiyun.com/doc/1xf29vmu4z?utm_source=wenku_answer2doc_content)
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值