当前位置: 首页 > news >正文

R60ABD1 串口通信实现

R60ABD1 串口通信实现

引言

在嵌入式开发中,串口通信是一种常见的通信方式,广泛应用于传感器数据采集、设备间通信等场景。本文将详细介绍基于 CH32V30x 微控制器的 R60ABD1 模块的串口通信实现,包括初始化、数据发送与接收、以及数据包处理等功能。适合有一定基础的经验者学习。

本文基于以下代码实现,包含 R60ABD1.cR60ABD1.h 两个文件,代码实现了一个完整的串口通信功能,支持数据包的发送与接收,并结合人机交互界面(HMI)和 MQTT 协议进行数据处理。

代码功能概述

R60ABD1.cR60ABD1.h 实现了一个基于 USART2 的串口通信模块,主要功能包括:

  1. 串口初始化:配置 CH32V30x 的 USART2,包括波特率、GPIO 引脚、中断等。
  2. 数据发送:支持单字节、数组、字符串、数字以及格式化字符串的发送。
  3. 数据包处理:实现数据包的发送与接收,包含包头、数据和包尾的处理。
  4. 数据解析与应用:接收数据包后,根据特定协议解析数据并通过 HMI 显示或通过 MQTT 协议发送。

代码解析

1. 头文件与全局变量

R60ABD1.h 中,定义了必要的函数声明和全局变量:

  • 全局变量

    • Serial_TxPacket[4]:用于存储发送数据包,格式为 FF 01 02 03 04 FE
    • Serial_RxPacket[6]:用于存储接收数据包。
    • Serial_RxFlag:接收数据包的标志位,接收到完整数据包后置 1。
    • setnum[5]getnum[3]:分别用于存储设定值和接收到的数据(如呼吸、心率、体动)。
  • 函数声明

    • 串口初始化、发送字节、发送数组、发送字符串、发送数字、格式化输出、发送数据包、获取接收标志位等。
#ifndef __R60ABD1_H
#define __R60ABD1_H
#include <stdio.h>
#include "ch32v30x.h"
extern uint8_t Serial_TxPacket[];
extern uint8_t Serial_RxPacket[];
void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);
void Serial_SendPacket(void);
uint8_t Serial_GetRxFlag(void);
void R60ABD1_Detect(void);
#endif

2. 串口初始化

Serial_Init 函数负责初始化 USART2,配置包括:

  • 时钟使能:启用 USART2 和 GPIOA 的时钟。
  • GPIO 配置
    • PA2 配置为复用推挽输出,用于发送(TX)。
    • PA3 配置为上拉输入,用于接收(RX)。
  • USART 参数
    • 波特率:115200。
    • 数据位:8 位。
    • 停止位:1 位。
    • 无奇偶校验,无硬件流控制。
  • 中断配置:启用接收中断(RXNE),设置 NVIC 优先级为组 2,抢占优先级和响应优先级分别为 2。
void Serial_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;GPIO_Init(GPIOA, &GPIO_InitStructure);USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 115200;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;USART_InitStructure.USART_Parity = USART_Parity_No;USART_InitStructure.USART_StopBits = USART_StopBits_1;USART_InitStructure.USART_WordLength = USART_WordLength_8b;USART_Init(USART2, &USART_InitStructure);USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;NVIC_Init(&NVIC_InitStructure);USART_Cmd(USART2, ENABLE);
}

3. 数据发送功能

代码提供了多种发送函数,满足不同场景的需求:

  • 发送单字节 (Serial_SendByte):将单个字节写入 USART 数据寄存器,等待发送完成。
  • 发送数组 (Serial_SendArray):遍历数组,逐字节发送。
  • 发送字符串 (Serial_SendString):遍历字符串直到遇到 \0,逐字节发送。
  • 发送数字 (Serial_SendNumber):将数字按位转换为字符并发送,支持指定长度。
  • 格式化输出 (Serial_Printf):通过重定向 fputc 和使用 vsprintf,实现类似 printf 的功能。
void Serial_SendByte(uint8_t Byte)
{USART_SendData(USART2, Byte);while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
}void Serial_Printf(char *format, ...)
{char String[100];va_list arg;va_start(arg, format);vsprintf(String, format, arg);va_end(arg);Serial_SendString(String);
}

4. 数据包发送

Serial_SendPacket 函数用于发送固定格式的数据包,包头为 0xFF,数据部分为 Serial_TxPacket 数组(4 字节),包尾为 0xFE

void Serial_SendPacket(void)
{Serial_SendByte(0xFF);Serial_SendArray(Serial_TxPacket, 4);Serial_SendByte(0xFE);
}

5. 数据接收与状态机

USART2_IRQHandler 中断函数使用状态机处理接收到的数据包,分为三个状态:

  • 状态 0:等待包头(0x530x59)。
  • 状态 1:接收数据部分,存入 Serial_RxPacket,接收 6 个字节后进入状态 2。
  • 状态 2:验证包尾(0x540x43),若正确,设置 Serial_RxFlag 为 1。
void USART2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void USART2_IRQHandler(void)
{static uint8_t RxState = 0;static uint8_t pRxPacket = 0;if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET){uint8_t RxData = USART_ReceiveData(USART2);if (RxState == 0){if (RxData == 0x53) { pRxPacket = 0; }else if (RxData == 0x59) { RxState = 1; pRxPacket = 0; }}else if (RxState == 1){Serial_RxPacket[pRxPacket] = RxData;pRxPacket++;if (pRxPacket >= 6) { RxState = 2; }}else if (RxState == 2){if (RxData == 0x54) {}if (RxData == 0x43){RxState = 0;Serial_RxFlag = 1;}}USART_ClearITPendingBit(USART2, USART_IT_RXNE);}
}

6. 数据处理与应用

R60ABD1_Detect 函数处理接收到的数据包,根据协议解析并更新 getnum 数组(存储呼吸、心率、体动数据),通过 HMI_send_int 显示在人机界面,并通过 SendMQTT 发送到 MQTT 服务器。

void R60ABD1_Detect(void)
{if (Serial_GetRxFlag() == 1){if (Serial_RxPacket[0] == 0x81 && Serial_RxPacket[1] == 0x02){getnum[0] = Serial_RxPacket[4];HMI_send_int("breath.val", getnum[0]);SendMQTT("breath", &getnum[0], 0, 0);}else if (Serial_RxPacket[0] == 0x85 && Serial_RxPacket[1] == 0x02){getnum[1] = Serial_RxPacket[4];HMI_send_int("head.val", getnum[1]);SendMQTT("head", &getnum[1], 0, 0);}else if (Serial_RxPacket[0] == 0x80 && Serial_RxPacket[1] == 0x03){getnum[2] = Serial_RxPacket[4];HMI_send_int("activity.val", getnum[2]);SendMQTT("activity", &getnum[2], 0, 0);}}
}

使用说明

  1. 硬件连接

    • 确保 CH32V30x 微控制器的 PA2(TX)和 PA3(RX)引脚正确连接到 R60ABD1 模块的串口。
    • 根据需要连接蜂鸣器(Buzzer)、HMI 屏幕和 ESP8266 模块(用于 MQTT)。
  2. 代码部署

    • R60ABD1.cR60ABD1.h 集成到项目中。
    • 确保包含必要的库文件(如 ch32v30x.hbuzzer.hesp8266.hhmi.h)。
    • 在主函数中调用 Serial_Init() 初始化串口,周期性调用 R60ABD1_Detect() 处理数据。
  3. 数据协议

    • 发送数据包格式:FF [4字节数据] FE
    • 接收数据包格式:53/59 [6字节数据] 54/43
    • 根据 Serial_RxPacket[0]Serial_RxPacket[1] 判断数据类型(如 0x81 表示呼吸数据)。

注意事项

  • 中断函数名称USART2_IRQHandler 必须与启动文件中定义一致,否则中断无法触发。
  • 数据包验证:确保接收到的包头和包尾正确,避免数据解析错误。
  • 资源占用:串口通信会占用一定 CPU 资源,需合理配置中断优先级。
http://www.dtcms.com/a/348969.html

相关文章:

  • 在 Ubuntu 24.04 或 22.04 LTS 服务器上安装、配置和使用 Fail2ban
  • 【Qwen Image】蒸馏版与非蒸馏版 评测小结
  • 第3篇:配置管理的艺术 - 让框架更灵活
  • 多线程下单例如何保证
  • [身份验证脚手架] 前端认证与个人资料界面
  • 2025.8.18-2025.8.24第34周:有内耗有挣扎
  • Spring Cloud 快速通关之Sentinel
  • 遥感机器学习入门实战教程|Sklearn案例⑩:降维与分解(decomposition 模块)
  • [e3nn] 等变神经网络 | 线性层o3.Linear | 非线性nn.Gate
  • 动态规划--编译距离
  • AI代码生成器全面评测:六个月、500小时测试揭示最强开发助手
  • Redis 高可用篇
  • 51单片机-实现定时器模块教程
  • GaussDB 数据库架构师修炼(十八) SQL引擎-统计信息
  • 用 WideSearch 思路打造「零幻觉、全覆盖」的多 Agent 信息收集器
  • SRE 系列(四)| MTTI 与 On-Call:高效故障响应之道
  • C++标准库算法:从零基础到精通
  • Go语言 Hello World 实例
  • 数据标注的质检环节有多少种
  • 单表查询-分析函数的应用
  • 智能体之推理引擎(3)
  • 记一次使用 C++ 实现多种扑克牌逻辑
  • ptrade `get_fundamentals` - 获取财务数据
  • 58 C++ 现代C++编程艺术7-模板友元
  • VC2022连接mysql
  • 微服务-21.网关路由-路由属性
  • 2025年KBS SCI1区TOP,新颖奖励与ε-贪婪衰减Q-learning算法+局部移动机器人路径规划,深度解析+性能实测
  • AI基础学习周报十
  • AI产品经理面试宝典第74天:技术边界与商业闭环的面试问题与答法
  • Trip Footprint_旅行分享功能模块技术架构天气模块技术架构