当前位置:   article > 正文

STM32F4无人机实现串口+DMA数据帧解析_无人机与软件系统数据帧组包过程

无人机与软件系统数据帧组包过程

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

目的

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

实验原理

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

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

准备

        MDK5开发环境的成功安装。

        STM32F4xx标准外设库。

        STM32F407 飞控板。

        STM32F4xx 参考手册。

        飞控板电路原理图。

        串口调试助手软件。

        USB转TTL模块。

步骤

  • 在bsp_usart_dma.c文件中定义Parse_Char()函数,用来进行数据获取及状态判断。
  1. uint8_t fcs_check; // 存储校验位状态
  2. uint8_t position = 0; // 存储数据长度
  3. //数据处理及状态判断实数
  4. bool Parse_Char(uint8_t c)
  5. {
  6. bool checkstatus = false; // 检查当前的状态
  7. switch(message.status)
  8. {
  9. case STX_STATE: // 判断数据帧头状态
  10. if(c == MESSAGE_STX)
  11. {
  12. message.status = LEN_STATE;
  13. }
  14. break;
  15. case LEN_STATE: // 判断数据长度状态
  16. message.data = (uint8_t *)malloc(MESSAGE_SIZE + c + 1);
  17. fcs_check = 0;
  18. if(message.data)
  19. {
  20. message.data[STX_STATE] = MESSAGE_STX;
  21. message.data[LEN_STATE] = c;
  22. fcs_check ^= message.data[LEN_STATE]; //
  23. message.status = CMD0_STATE;
  24. }
  25. else
  26. {
  27. message.status = STX_STATE;
  28. }
  29. break;
  30. case CMD0_STATE: // 判断cmd0状态
  31. message.data[CMD0_STATE] = c;
  32. fcs_check ^= message.data[CMD0_STATE];
  33. message.status = CMD1_STATE;
  34. break;
  35. case CMD1_STATE: // 判断cmd1状态
  36. message.data[CMD1_STATE] = c;
  37. fcs_check ^= message.data[CMD1_STATE];
  38. if(message.data[LEN_STATE] > 0)
  39. {
  40. message.status = DATA_STATE;
  41. }
  42. else
  43. {
  44. message.status = FCS_STATE;
  45. }
  46. break;
  47. case DATA_STATE: // 判断数据接收状态
  48. message.data[DATA_STATE+position] = c;
  49. fcs_check ^= message.data[MESSAGE_SIZE + position++];
  50. if(position == message.data[LEN_STATE])
  51. {
  52. message.status = FCS_STATE;
  53. position = 0;
  54. }
  55. break;
  56. case FCS_STATE: // 判断校验位状态
  57. if(c == fcs_check)
  58. {
  59. message.data[MESSAGE_SIZE + message.data[LEN_STATE]] = c;
  60. checkstatus = true;
  61. message.status = STX_STATE;
  62. }
  63. else if(c == MESSAGE_STX)
  64. {
  65. message.status = LEN_STATE;
  66. }
  67. else
  68. {
  69. message.status = STX_STATE;
  70. }
  71. break;
  72. }
  73. return checkstatus;
  74. }
  • 在bsp_usart_dma.c文件中定义数据解析函数DMA2_Parse_Message(),循环获取DMA2_Stream2缓存区的数据,然后通过Parse_Char()函数进行状态判断和数据存储,如果接收数据校验正确,则使能DMA2_Stream7,将数据发送出去。
  1. //数据解析
  2. void DMA2_Parse_Message(void)
  3. {
  4. uint8_t c;
  5. while(usart1_data.parse_index != sizeof(usart1_data.rx_buf) - DMA2_Stream2->NDTR)
  6. {
  7. c = usart1_data.rx_buf[usart1_data.parse_index]; // 依次获取缓冲区中每个字节数据
  8. if(Parse_Char(c))
  9. {
  10. memcpy(usart1_data.tx_buf , message.data , MESSAGE_SIZE + message.data[LEN_STATE] + 1); // 数据拷贝
  11. DMA_SetCurrDataCounter(DMA2_Stream7 , MESSAGE_SIZE + message.data[LEN_STATE] + 1); // 设置发送DMA数据传输量
  12. DMA_Cmd(DMA2_Stream7 , ENABLE); // 使能DMA2_Stream7
  13. free(message.data);
  14. }
  15. if(++usart1_data.parse_index == sizeof(usart1_data.rx_buf))
  16. {
  17. usart1_data.parse_index = 0;
  18. }
  19. }
  20. }

  •  在bsp_usart_dma.h文件中定义结构并进行函数声明
  1. #ifndef __BSP_USART_DMA_H__
  2. #define __BSP_USART_DMA_H__
  3. #include "stm32f4xx.h"
  4. #include <string.h>
  5. #include <stdlib.h>
  6. #include <stdbool.h>
  7. #define STX_STATE 0 // 数据帧头状态
  8. #define LEN_STATE 1 // 数据长度状态
  9. #define CMD0_STATE 2 // cmd0状态
  10. #define CMD1_STATE 3 // cmd1状态
  11. #define DATA_STATE 4 // 接收数据状态
  12. #define FCS_STATE 5 // 校准状态
  13. #define MESSAGE_STX 0xFE // 数据帧头
  14. #define MESSAGE_SIZE 4 // 数据长度
  15. typedef struct
  16. {
  17. uint8_t tx_buf[50];
  18. uint8_t rx_buf[50];
  19. uint8_t send_length;
  20. uint8_t parse_index; // 当前通道传输的字节数
  21. }USART1_DMA_DATA;
  22. typedef struct
  23. {
  24. uint8_t status;
  25. uint8_t *data;
  26. }MESSAGE;
  27. extern USART1_DMA_DATA usart1_data;
  28. void USART1_DMA2_Init(void);
  29. bool Parse_Char(uint8_t c);
  30. void DMA2_Parse_Message(void);
  31. #endif
  • 在main.c文件中调用相关函数。

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

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

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

  1. #include "bsp_systick.h"
  2. #include "bsp_usart.h"
  3. #include "bsp_usart_dma.h"
  4. int main(void)
  5. {
  6. SysTick_Init(1);
  7. USART1_Init(115200); // 串口1初始化函数调用,设置波特率
  8. USART1_DMA2_Init(); // DMA2初始化
  9. while(1)
  10. {
  11. DMA2_Parse_Message(); // 数据解析
  12. SysTick_DelayMS(50); // 延时
  13. }
  14. }

现象

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/610848
推荐阅读
相关标签
  

闽ICP备14008679号