赞
踩
更多交流欢迎关注作者抖音号:81849645041
了解数据帧解析的方法,在使用DMA进行串口数据收发的基础上,实现数据帧解析。
串口通信是目前单片机和DSP等嵌入式系统之间,以及嵌入式系统与PC机或无线模块之间的一种非常重要且普遍的通信方式。串口通信看似简单,但其中仍有许多问题值得研究,例如串口通信过程中的帧同步问题,本实验采用基于状态机的方法,该方法是嵌入式系统串口通信中很有效的帧同步方法,同时也是一种很不错的串口通信程序设计结构。
现代系统的数据通讯,必须要求系统有非常高的可靠性和稳定性,所以必须在通信过程中有一定的同步机制。基于状态机的方法是将数据帧的接收过程分为若干个状态,本实验分为:接收信息头状态、接收数据长状态、2个接收ID状态、接收数据状态及校验状态。程序在开始后依次接收各个数据并判断状态,校验正确后对数据帧进行处理。
MDK5开发环境的成功安装。
STM32F4xx标准外设库。
STM32F407 飞控板。
STM32F4xx 参考手册。
飞控板电路原理图。
串口调试助手软件。
USB转TTL模块。
- 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;
- }
- //数据解析
- 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;
- }
- }
- }
- #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
第一步:引用相关的头文件
第二步:初始化滴答定时器、串口以及串口的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外的各个数据进行异或得到,需要手动计算)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。