STM32F4无人机实现串口+DMA数据帧解析

更多交流欢迎关注作者抖音号:81849645041

目的

        了解数据帧解析的方法,在使用DMA进行串口数据收发的基础上,实现数据帧解析。

实验原理

        串口通信是目前单片机和DSP等嵌入式系统之间,以及嵌入式系统与PC机或无线模块之间的一种非常重要且普遍的通信方式。串口通信看似简单,但其中仍有许多问题值得研究,例如串口通信过程中的帧同步问题,本实验采用基于状态机的方法,该方法是嵌入式系统串口通信中很有效的帧同步方法,同时也是一种很不错的串口通信程序设计结构。

        现代系统的数据通讯,必须要求系统有非常高的可靠性和稳定性,所以必须在通信过程中有一定的同步机制。基于状态机的方法是将数据帧的接收过程分为若干个状态,本实验分为:接收信息头状态、接收数据长状态、2个接收ID状态、接收数据状态及校验状态。程序在开始后依次接收各个数据并判断状态,校验正确后对数据帧进行处理。

准备

        MDK5开发环境的成功安装。

        STM32F4xx标准外设库。

        STM32F407 飞控板。

        STM32F4xx 参考手册。

        飞控板电路原理图。

        串口调试助手软件。

        USB转TTL模块。

步骤

  • 在bsp_usart_dma.c文件中定义Parse_Char()函数,用来进行数据获取及状态判断。
uint8_t fcs_check; // 存储校验位状态
uint8_t position = 0; // 存储数据长度
//数据处理及状态判断实数
bool Parse_Char(uint8_t c)
{
    bool checkstatus = false; // 检查当前的状态
	switch(message.status)
	{
	    case STX_STATE: // 判断数据帧头状态
	    if(c == MESSAGE_STX)
	    {
		message.status = LEN_STATE;
	    }
	    break;
		
	    case LEN_STATE: // 判断数据长度状态
		message.data = (uint8_t *)malloc(MESSAGE_SIZE + c + 1);
		fcs_check = 0;
		if(message.data)
		{
		    message.data[STX_STATE] = MESSAGE_STX;
		    message.data[LEN_STATE] = c;
		    fcs_check ^= message.data[LEN_STATE]; // 
		    message.status = CMD0_STATE; 
		}
		else
		{
		    message.status = STX_STATE;
		}
	    break;
		
	    case CMD0_STATE: // 判断cmd0状态
		message.data[CMD0_STATE] = c;
		fcs_check ^= message.data[CMD0_STATE];
		message.status = CMD1_STATE;
	    break;
		
	    case CMD1_STATE: // 判断cmd1状态
		message.data[CMD1_STATE] = c;
		fcs_check ^= message.data[CMD1_STATE];
		if(message.data[LEN_STATE] > 0)
		{
		    message.status = DATA_STATE;
		}
		else
		{
		    message.status = FCS_STATE;		
		}
	    break;
			
	    case DATA_STATE: // 判断数据接收状态
		message.data[DATA_STATE+position] = c;
		fcs_check ^= message.data[MESSAGE_SIZE + position++];
		if(position == message.data[LEN_STATE])
		{
		    message.status = FCS_STATE;	
		    position = 0;
		}			
	    break;
			
	    case FCS_STATE: // 判断校验位状态
		if(c == fcs_check)
		{
		    message.data[MESSAGE_SIZE + message.data[LEN_STATE]] = c;
		    checkstatus = true;				
		    message.status = STX_STATE;
		}
		else if(c == MESSAGE_STX)
		{
		    message.status = LEN_STATE;
		}
		else 
		{
		    message.status = STX_STATE;
		}			
	    break;
	}
	return checkstatus;
} 
  • 在bsp_usart_dma.c文件中定义数据解析函数DMA2_Parse_Message(),循环获取DMA2_Stream2缓存区的数据,然后通过Parse_Char()函数进行状态判断和数据存储,如果接收数据校验正确,则使能DMA2_Stream7,将数据发送出去。
//数据解析
void DMA2_Parse_Message(void)
{
uint8_t c;
	while(usart1_data.parse_index != sizeof(usart1_data.rx_buf) - DMA2_Stream2->NDTR)
	{
	    c = usart1_data.rx_buf[usart1_data.parse_index]; // 依次获取缓冲区中每个字节数据
	    if(Parse_Char(c))
	    {
		memcpy(usart1_data.tx_buf , message.data , MESSAGE_SIZE + message.data[LEN_STATE] + 1); // 数据拷贝
		DMA_SetCurrDataCounter(DMA2_Stream7 , MESSAGE_SIZE + message.data[LEN_STATE] + 1); // 设置发送DMA数据传输量
		DMA_Cmd(DMA2_Stream7 , ENABLE); // 使能DMA2_Stream7
		free(message.data);
	    }
        if(++usart1_data.parse_index == sizeof(usart1_data.rx_buf))
	    {
		usart1_data.parse_index = 0;
	    }
    }
}

  •  在bsp_usart_dma.h文件中定义结构并进行函数声明
#ifndef __BSP_USART_DMA_H__
#define __BSP_USART_DMA_H__

#include "stm32f4xx.h"
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

#define STX_STATE		0 // 数据帧头状态
#define LEN_STATE 	1 // 数据长度状态
#define CMD0_STATE 	2 // cmd0状态
#define CMD1_STATE 	3 // cmd1状态
#define DATA_STATE 	4 // 接收数据状态
#define FCS_STATE 	5 // 校准状态

#define MESSAGE_STX 0xFE // 数据帧头

#define MESSAGE_SIZE 4 // 数据长度

typedef struct
{
	uint8_t tx_buf[50];
	uint8_t rx_buf[50];
	uint8_t send_length;
	uint8_t parse_index; // 当前通道传输的字节数
}USART1_DMA_DATA;

typedef struct
{
	uint8_t status;
	uint8_t *data;
}MESSAGE;

extern USART1_DMA_DATA usart1_data;

void USART1_DMA2_Init(void);
bool Parse_Char(uint8_t c);
void DMA2_Parse_Message(void);

#endif
  • 在main.c文件中调用相关函数。

        第一步:引用相关的头文件

        第二步:初始化滴答定时器、串口以及串口的DMA

        第三步:在while循环中每隔50ms调用一次数据解析函数。

#include "bsp_systick.h"
#include "bsp_usart.h"
#include "bsp_usart_dma.h"

int main(void)
{
	SysTick_Init(1);
	USART1_Init(115200); // 串口1初始化函数调用,设置波特率
	USART1_DMA2_Init(); // DMA2初始化
	while(1)
	{
	    DMA2_Parse_Message(); // 数据解析	
	    SysTick_DelayMS(50); // 延时
	}
}

现象

        将程序下载到开发板中,打开串口调试助手,在发送缓冲区发送一串数据帧,如果校准正确,则会通过上面窗口将数据显示出来(注:数据帧最后一位为校准位,将前面除去数据帧头0xFE外的各个数据进行异或得到,需要手动计算)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奚海蛟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值