【星云 Orbit • STM32F4】09. 常用自定义串口通讯协议框架

【星云 Orbit • STM32F4】09. 常用自定义串口通讯协议框架

1. 引言

本教程旨在帮助嵌入式开发小白从零开始,学习如何在STM32F407微控制器上实现一个常用的自定义串口通讯协议。该协议能够通过串口发送和接收数据,并支持基本的错误检测和数据处理功能。教程内容涵盖基础知识、配置步骤、HAL库函数详解,并提供配套例程和代码注释。

2. 硬件准备

  • STM32F407开发板
  • 串口调试工具(如串口助手)

3. 软件准备

  • Keil MDK-ARM开发环境
  • STM32F407标准库

4. 知识储备

在开始编程之前,需要了解以下基础知识:

  • STM32F407的串口(USART)外设
  • 基本的串口通信知识(波特率、数据位、停止位、校验位)
  • C语言编程基础
  • 中断服务函数的基本概念

5. 协议设计

5.1 协议格式

自定义串口通讯协议的数据帧格式如下:

以下是关于串口通信中数据帧结构的详细说明:

数据头1 数据头2 数据头3 数据类型 数据长度高 数据长度低 数据内容… 校验码
0x55 0xAA 0xFF 0x01 0x00 0x0A 数据具体内容 0x3C
各字段说明:
  1. 数据头1 (0x55):
    • 作为数据包的第一个字节,用于标识数据包的开始。
    • 接收端检测到这个字节后,开始准备接收后续数据。
  2. 数据头2 (0xAA):
    • 作为数据包的第二个字节,进一步确认数据包的开始。
    • 与数据头1配合使用,提高数据包识别的准确性。
  3. 数据头3 (0xFF):
    • 作为数据包的第三个字节,用于进一步确认数据包的开始。
    • 通过三个连续的数据头字节,确保数据包的起始位置无误。
  4. 数据类型 (0x01):
    • 表示数据的具体类型,例如温度数据、压力数据等。
    • 接收端根据数据类型对数据进行相应的处理。
  5. 数据长度高 (0x00):
    • 表示数据内容长度的高位部分。
    • 通常与数据长度低字节配合使用,表示较大的数据长度值。
  6. 数据长度低 (0x0A):
    • 表示数据内容长度的低位部分。
    • 与数据长度高字节配合使用,表示数据内容的总长度。
  7. 数据内容…:
    • 实际的数据部分,包含具体的数据信息。
    • 数据内容的长度由数据长度高和数据长度低字节决定。
  8. 校验码 (0x3C):
    • 用于验证数据在传输过程中是否出错。
    • 接收端通过计算接收到的数据内容的校验码,并与接收到的校验码进行比较,判断数据是否正确。
数据帧结构在串口通信中的作用:
  • 数据包识别: 通过数据头1、数据头2和数据头3,接收端能够准确识别数据包的起始位置,确保数据接收的准确性。
  • 数据类型标识: 数据类型字段帮助接收端了解数据的具体内容,从而进行相应的处理。
  • 数据长度指示: 数据长度高和数据长度低字段告诉接收端数据内容的长度,确保接收端能够正确读取数据内容。
  • 数据完整性验证: 校验码用于验证数据在传输过程中是否出错,确保数据的完整性和可靠性。
数据帧结构在程序流程中的处理:
  1. 系统初始化:
    • 程序开始时进行系统初始化,包括时钟配置、GPIO配置等,为后续操作做好准备。
  2. 串口初始化:
    • 配置串口参数(如波特率、数据位、停止位等),使串口准备好接收数据。
  3. 等待中断触发:
    • 程序进入等待状态,等待串口接收到数据后触发中断。
  4. 接收数据:
    • 中断触发后,程序从串口接收数据。
  5. 检测数据头:
    • 检查接收到的数据是否包含有效的数据头(数据头1、数据头2、数据头3),以确保数据的完整性。
  6. 解析数据帧:
    • 根据数据帧结构,解析接收到的数据,提取数据类型、数据长度、数据内容和校验码等信息。
  7. 存储数据:
    • 将解析后的数据存储到内存或外部存储器中,以便后续处理。
  8. 数据处理:
    • 对存储的数据进行进一步的处理,如计算、分析等,根据数据类型进行相应的操作。

通过以上步骤,数据帧结构在串口通信中起到了关键作用,确保了数据的准确传输和处理。

  • 数据头:固定字节序列 0xEB 0x00 0x55,用于标识数据帧的开始。
  • 数据类型:表示数据的类型,例如 0x01 表示普通数据,0x02 表示控制命令。
  • 数据长度:表示数据内容的长度,由高低两字节组成。
  • 数据内容:实际传输的数据。
  • 校验码:用于数据帧的完整性校验。
5.2 协议特点
  • 数据头检测:通过检测固定字节序列来标识数据帧的开始。
  • 数据长度可变:支持不同长度的数据内容。
  • 校验机制:通过简单的累加校验来确保数据帧的完整性。

6. 程序设计

6.1 程序功能概述

程序的主要功能包括:

  • 初始化STM32F407的串口外设
  • 通过中断方式接收串口数据
  • 检测数据头并解析数据帧
  • 存储接收到的数据到缓冲区
  • 提供数据处理接口
6.2 程序模块划分

程序分为以下几个模块:

  • main.c:主程序文件,负责初始化和程序运行逻辑
  • usart.husart.c:串口驱动文件,负责串口的初始化和数据收发
  • protocol.hprotocol.c:协议处理模块,负责数据帧的解析和校验
6.3 程序流程图

【星云 Orbit • STM32F4】09. 常用自定义串口通讯协议框架_第1张图片

流程图说明:
  1. 系统初始化:程序开始时进行系统初始化,包括时钟配置、GPIO配置等。
  2. 串口初始化:配置串口参数(如波特率、数据位、停止位等),使串口准备好接收数据。
  3. 等待中断触发:程序进入等待状态,等待串口接收到数据后触发中断。
  4. 接收数据:中断触发后,程序从串口接收数据。
  5. 检测数据头:检查接收到的数据是否包含有效的数据头,以确保数据的完整性。
  6. 解析数据帧:将接收到的数据按照特定的格式进行解析。
  7. 存储数据:将解析后的数据存储到内存或外部存储器中。
  8. 数据处理:对存储的数据进行进一步的处理,如计算、分析等

7. 代码实现

7.1 串口初始化 (usart.c)
#include "usart.h"

void USART_Init(uint32_t baudrate) {
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 使能GPIO和USART时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    // 配置GPIO引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置USART
    USART_InitStructure.USART_BaudRate = baudrate;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    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);

    // 使能USART
    USART_Cmd(USART1, ENABLE);

    // 配置 NVIC 优先级
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}
7.2 协议处理模块 (protocol.c)
#include "protocol.h"

#define DATA_BUFFER_SIZE 100
#define HEADER_SIZE 3
#define HEADER_1 0xEB
#define HEADER_2 0x00
#define HEADER_3 0x55

static uint8_t data_buffer[DATA_BUFFER_SIZE];
static uint16_t buffer_index = 0;
static uint8_t header_received = 0;

void Protocol_Init(void) {
    buffer_index = 0;
    header_received = 0;
}

void USART1_IRQHandler(void) {
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
        uint8_t received_data = USART_ReceiveData(USART1);

        if (header_received < HEADER_SIZE) {
            if (received_data == header[header_received]) {
                header_received++;
                if (header_received == HEADER_SIZE) {
                    // 数据头检测完成,开始接收数据
                    buffer_index = 0;
                }
            } else {
                header_received = 0;
            }
        } else {
            if (buffer_index < DATA_BUFFER_SIZE) {
                data_buffer[buffer_index++] = received_data;
            }
        }
    }
}

uint8_t* GetDataBuffer(void) {
    return data_buffer;
}

uint16_t GetBufferDataLength(void) {
    return buffer_index;
}
7.3 主程序 (main.c)
#include "usart.h"
#include "protocol.h"

int main(void) {
    // 系统时钟配置
    SystemClock_Config();

    // 初始化串口
    USART_Init(9600);

    // 初始化协议处理模块
    Protocol_Init();

    while (1) {
        // 主循环可以添加其他任务
    }
}

8. 使用示例

8.1 初始化串口

main.c 中调用 USART_Init 函数,配置串口参数:

USART_Init(9600); // 配置波特率为9600
8.2 初始化协议处理模块

调用 Protocol_Init 函数,初始化协议处理模块:

Protocol_Init();
8.3 数据接收与处理

在中断服务函数 USART1_IRQHandler 中,接收到的数据会被存储到 data_buffer 中。主程序可以通过以下函数获取接收到的数据:

uint8_t* data = GetDataBuffer();
uint16_t data_length = GetBufferDataLength();

9. 串口收发思维导图

以下是基于STM32F407的自定义串口通讯协议收发的状态转换过程:

【星云 Orbit • STM32F4】09. 常用自定义串口通讯协议框架_第2张图片

状态图说明:
  1. 系统初始化
    • 程序开始时进行系统初始化,包括时钟配置、GPIO配置等。
    • 初始化完成后,进入“串口初始化”状态。
  2. 串口初始化
    • 配置串口参数(如波特率、数据位、停止位等),使串口准备好接收数据。
    • 配置完成后,进入“等待中断触发”状态。
  3. 等待中断触发
    • 程序进入等待状态,等待串口接收到数据后触发中断。
    • 当串口接收到数据并触发中断时,进入“接收数据”状态。
  4. 接收数据
    • 中断触发后,程序从串口接收数据。
    • 每接收到一个字节的数据后,进入“检测数据头”状态。
  5. 检测数据头
    • 检查接收到的数据是否包含有效的数据头(数据头1、数据头2、数据头3)。
    • 如果数据头有效,进入“解析数据帧”状态。
    • 如果数据头无效,返回“等待中断触发”状态,重新开始接收数据。
  6. 解析数据帧
    • 根据数据帧结构,解析接收到的数据,提取数据类型、数据长度、数据内容和校验码等信息。
    • 解析完成后,进入“存储数据”状态。
  7. 存储数据
    • 将解析后的数据存储到内存或外部存储器中。
    • 存储完成后,进入“数据处理”状态。
  8. 数据处理
    • 对存储的数据进行进一步的处理,如计算、分析等。
    • 处理完成后,返回“等待中断触发”状态,继续接收新的数据。
    • 如果在处理过程中发生错误,返回“系统初始化”状态,重新初始化系统。

​ 这个状态图清晰地展示了基于STM32F407的自定义串口通讯协议的状态转换过程。通过状态图,可以直观地理解串口收发在不同阶段的行为和状态之间的转换逻辑,有助于更好地理解和设计串口通信系统。

10. 总结

通过本教程,读者可以掌握如何在STM32F407上实现一个常用的自定义串口通讯协议。协议通过检测数据头来接收数据,并支持基本的错误检测和数据处理功能。教程内容从零开始,详细讲解了串口配置、数据接收、中断处理等关键步骤,并提供了完整的代码示例和注释,帮助读者快速上手。

你可能感兴趣的:(【星云,Orbit,•,STM32F4】,stm32,嵌入式硬件,单片机)