# Solar_Debugger **Repository Path**: lsksp/solar_-debugger ## Basic Information - **Project Name**: Solar_Debugger - **Description**: Solar Debugger 是一款基于 Qt6/C++ 开发的跨平台上位机调试工具。工具主打轻量化、易扩展、零代码适配,用户仅需修改 JSON 配置文件,即可快速完成设备参数定义、界面布局与通讯协议适配,无需改动源码、无需重新编译。 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 1 - **Created**: 2026-09-09 - **Last Updated**: 2026-09-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Solar Debugger:一个轻量易扩展的上位机调试助手 > 在我以往从事的项目中,一个系统可能有几十甚至几百个参数,为了便于系统调试,我制作一个可以适配多个项目、简单的、专注于数据写入和显示的上位机:Solar Debugger。 ## 一、项目简介 Solar Debugger 是一款基于 Qt6/C++ 开发的跨平台上位机调试工具。工具主打**轻量化、易扩展、零代码适配**,用户仅需修改 JSON 配置文件,即可快速完成设备参数定义、界面布局与通讯协议适配,无需改动源码、无需重新编译。 本工具面向嵌入式、电机控制、机器人开发等设备调试场景,专注设备通讯交互、参数监控与曲线分析。区别于传统硬编码上位机,本工具实现**参数定义、UI 布局、通讯协议完全解耦**,所有参数的名称、类型、量程、单位、分组及UI样式均由配置文件动态生成,大幅提升设备调试与项目适配效率。 | 项目 | 版本 / 说明 | | -------- | -------------------------------------------------------- | | 语言标准 | C++17 | | GUI 框架 | Qt 6.5 及以上(Core、Widgets、SerialPort、PrintSupport) | | 构建系统 | CMake ≥ 3.19 | | 编译器 | MSVC 2022 / MinGW 13+ / GCC 11+ / Clang 14+ | | 第三方库 | QCustomPlot | | 设计工具 | Qt Creator | ## 二、功能描述 视频演示:[Solar Debugger:一个轻量易扩展的上位机调试助手](https://www.bilibili.com/video/BV1XuY86WEaW/?share_source=copy_web&vd_source=56128f1983846c89c73f56de8a86952a) ### 2.1 下行数据 ![image-20260902211624684](ReadMe/image-20260902211624684.png) 软件页面上半部分包含**系统配置、参数配置**两个可自定义标签页,所有标签页的名称、数量、展示顺序,均可通过 **__paramsConfig.json** 配置文件自定义,无需修改源码。 标签页内支持自定义下发参数控件,如模式选择、参数增益等。用户手动输入参数数值后,点击下发按钮,即可按照预设协议格式向下位机发送数据,所有下发参数的名称、数量、排列顺序均由配置文件动态定义。 界面中央独立设置**开关指令**区域,和下发参数类似,可由用户定义。该类指令为预设开关量,无需手动输入数值,点击即可发送固定格式指令数据。指令的名称、数量、排序同样支持配置文件自定义适配。 ## 2.2 上行数据(下位机上传至上位机) ![image-20260902213034005](ReadMe/image-20260902213034005.png) 上位机上行数据展示界面分为三大区域:左上波形图像显示区、左下功能控制区、右侧参数通道列表区,所有功能均基于配置文件动态适配,灵活度高。 ### 2.2.1 右侧通道列表 通道列表会动态展示配置文件中注册的所有上行参数,包含参数实时数值、波形显示开关等信息。为避免无效数据干扰、提升调试效率,参数不会默认展示,仅当下位机上传对应有效数据后,通道列表才会动态刷新显示,可搭配上行控制功能联动使用。 ### 2.2.2 左上波形显示区 支持多维缩放操作,适配不同数据观测需求:纵向坐标轴侧滚动鼠标滚轮可实现纵向缩放,横向坐标轴侧滚动滚轮可实现横向缩放,坐标轴外侧区域滚动滚轮可实现波形整体缩放,操作方式贴合常规调试工具使用习惯。 ### 2.2.3 左下功能控制区 集成全套波形调试工具,核心功能包含:自定义FIFO数据缓存上限、波形自动适配、数据接收与波形刷新暂停、清空历史波形数据、缓存数据导出CSV文件、光标显示/隐藏切换,满足日常数据观测、记录、调试需求。 ### 2.2.4 上行控制核心功能 该功能可读取配置文件中注册的全部上行参数,用户可自主勾选需要采集的参数,确认发送后,上位机将推送**ID=0 的上行调控特殊帧**。下位机接收响应后,会主动上传**ID=1 的批量上传特殊帧**,一次性回传所有勾选的参数数据,大幅提升批量数据采集效率。 常规数据上传规则:下位机可使用自定义参数ID作为帧ID,单帧数据段可携带该ID对应的多组参数数据。为保留特殊帧预留位,**用户自定义参数帧ID必须大于24**,≤24的ID为系统预留特殊帧专用。 项目配套提供下位机适配函数库,适配逻辑与上位机一致,开发者仅需注册对应参数,即可直接调用封装好的帧构建、响应、编解码相关函数,快速完成下位机协议适配,具体使用方法详见文档第六章。 ![image-20260903141954096](ReadMe/image-20260903141954096.png) ## 三、配置文件说明 上位机所有上下行参数、UI布局、控件属性与通讯映射关系,均由**__paramsConfig.json** 文件统一配置。项目无需硬编码参数,修改配置文件即可完成设备适配与界面自定义。 配置文件分为两大核心节点: - **meta**:配置文件基础信息,包含项目描述、版本号等通用标识。 - **parameters**:参数配置数组,所有上下行通讯参数、UI控件属性均在此定义。 完整可用的配置示例如下: ```jsonc { "meta": { "description": "Solar Debugger Configuration", "version": "1.0" }, "parameters": [ [30, "急停", "急停", "Uint8", 0, 1.0, 0.0, "", 1, 0, 1, "开关指令", 2], [258, "d轴电流", "d轴电流", "Int16", 0, 0.01, 0.0, "A", 0, -1000, 1000, "上行数据", 0], [258, "q轴电流", "q轴电流", "Int16", 2, 0.01, 0.0, "A", 0, -1000, 1000, "上行数据", 0], [259, "温度", "温度", "Int32", 0, 0.01, 0.0, "℃", 0, -1000, 1000, "上行数据", 0], [260, "速度", "速度", "Int16", 0, 0.01, 0.0, "rpm", 0, -500, 500, "上行数据", 0], [261, "电量", "电量", "Uint8", 0, 0.1, 0.0, "", 0, 0, 100, "上行数据", 0], [512, "模式选择", "模式选择", "Uint8", 0, 1.0, 0.0, "", 4, 1, 16, "系统配置", 1], [513, "增益", "增益", "Float", 0, 1.0, 0.0, "LSB", 1.0, 0.001, 10.0, "系统配置", 1], [500, "电流环Kp", "电流环比例增益", "Float", 0, 1.0, 0.0, "", 1.0, 0.0, 100.0, "参数配置", 1], [501, "电流环Ki", "电流环积分增益", "Float", 0, 1.0, 0.0, "", 10.0, 0.0, 1000.0, "参数配置", 1] ] } ``` parameters 数组内每个元素为固定13位参数配置数组,字段顺序、含义固定,完整定义如下: ```text [ID, 名称, 描述, 数据类型, 位偏移量, 比例, 偏置, 单位, 默认值, 最小值, 最大值, 分类, ui显示类型] ``` | 序号 | 字段 | 类型 | 说明 | | ---- | ---------- | ------ | ------------------------------------------------------------ | | 0 | ID | int | 数据段的唯一 ID(必须大于24),对应通讯帧中的 `frameId`。**下行参数**一个 ID 对应一个参数;**上行参数**一个 ID 可对应多个参数(通过 `位偏移量` 区分) | | 1 | 名称 | string | UI 上显示的参数名 | | 2 | 描述 | string | 对参数作用的详细介绍 | | 3 | 数据类型 | string | `Uint8` / `Int8` / `Uint16` / `Int16` / `Uint32` / `Int32` / `Float` | | 4 | 位偏移量 | int | 参数在帧 `data` 段中的起始字节偏移 | | 5 | 比例 | float | 缩放因子:`物理值 = 原始值 × 比例 + 偏置` | | 6 | 偏置 | float | 偏移量 | | 7 | 单位 | string | 物理单位(如 `A`、`r/min`、`℃`),可留空 | | 8 | 默认值 | float | UI 初始化时显示的值 | | 9 | 最小值 | float | UI 输入校验下限 | | 10 | 最大值 | float | UI 输入校验上限 | | 11 | 分类 | string | UI 分组标签,相同分类的参数会自动归到同一个 Tab 页 | | 12 | ui显示类型 | int | 0 = 只读数值显示;1 = 可写数值输入框;2 = 开关按钮 | ## 3.2 下行数据配置规范 下行数据为上位机下发至下位机的控制参数,为简化协议逻辑、提升传输效率,统一遵循以下规范: - **ID唯一绑定**:单个通讯ID仅对应一个下行参数,映射关系唯一。 - **无数据缩放**:下行参数比例固定为1、偏置固定为0,帧原始数据与物理值完全一致,支持直接指针操作,无需类型转换。 - **双UI下发模式**:支持两类可下发控件,底层通讯逻辑完全一致,仅展示形式不同。UI类型1为数值输入框,手动输入数值后下发;UI类型2为功能按钮,点击直接发送默认值,适用于急停、启停等开关指令 ## 3.3 上行数据配置规范 上行数据为下位机上传至上位机的监测参数,支持单ID多参数绑定,提供两种传输模式,适配不同调试场景,且下位机上传数据无需上位机回复响应帧。 - **单ID多参数绑定**:同一个通讯ID可绑定多个监测参数,通过字节偏移量区分不同参数,节省帧ID资源。 - **普通单ID上传**:以参数自定义ID为帧ID,单帧仅传输该ID对应的一组参数数据,适用于单点实时监测。 - **批量统一上传**:固定帧ID为1,单帧可聚合多个自定义ID的参数数据,配合上位机上行控制功能,实现按需批量采集,高效调试多通道数据 ## 四、软件架构 ``` Solar_Debugger/ ├── CMakeLists.txt ├── main.cpp ├── MainWindow.cpp / .h # 主窗口装配 ├── CommunicationManager.cpp / .h # 通讯管理(子线程) ├── __paramsConfig.json # 用户配置文件,需要复制到可执行exe文件同目录 ├── Solar_Debugger.rc # Windows 资源文件 ├── ReadMe.md ├── ui/ # UI 层 │ ├── BaseConfigPage.* # 通用参数配置页(表格 + 输入框) │ ├── ConfigTabwidget.* # 多分类 Tab 容器 │ ├── ConnectionWidgetBase.h # 连接配置对话框基类 │ ├── SerialConnectionWidget.* # 串口连接页面 │ ├── Switchwidget.* # 开关按钮组 │ ├── CurvePlotWidget.* # 实时曲线绘图(基于 QCustomPlot) │ ├── UplinkControlDialog.* # 上行调控对话框(勾选 ID 下发 frameId=0) │ └── Logwidget.* # 通讯日志面板 ├── src/ │ ├── core/ # 数据核心层 │ │ ├── Parameter.h # 参数描述结构 │ │ ├── ParameterRegistry.* # 参数注册表 │ │ ├── DataCacheLayer.* # 数据缓存与节流 │ │ └── RingBuffer.h # 环形缓冲 │ ├── protocol/ # 协议层 │ │ ├── ProtocolCodec.h # 抽象编解码接口 │ │ └── SerialCodec.* # 串口协议实现 │ ├── communication/ # 通讯驱动层 │ │ ├── AbstractCommunicationDriver.h │ │ └── SerialDriver.* │ └── utils/ ├── device_library/ # 下位机调用库(纯 C 实现) │ ├── serial_protocol.* # 串口帧协议(打包、解析、CRC16) │ ├── param_table.* # 参数管理引擎(注册、校验、上传) │ └── param_manager.* # 应用层示例(参数定义、回调处理) ├── qcustomplot/ # 第三方绘图库源码 ├── res/ # 资源文件(可替换自己的qss文件),需要复制到可执行exe文件同目录 └── build/ # 构建输出目录 ``` Solar Debugger 采用 **分层 + 抽象接口** 的设计,便于后续替换通讯方式与协议而无需改动上层 UI。整个系统自上而下分为四层,数据流向如下: ``` UI层 ←→ 核心层(参数注册表+数据缓存) ←→ 协议层(编解码) ←→ 通讯层(串口驱动) ``` - **UI层** (`ui/`):负责界面展示与用户交互,包括参数配置页(`BaseConfigPage`)、Tab容器(`ConfigTabWidget`)、实时曲线绘图(`CurvePlotWidget`)、日志面板(`LogWidget`)等。所有UI均由 `__paramsConfig.json` 驱动自动生成,无需硬编码。 - **核心层** (`src/core/`):`ParameterRegistry`(单例)在启动时从 JSON 加载全部参数定义,供 UI 和协议层查询;`DataCacheLayer` 对高频接收数据做节流降采样,避免 UI 刷新过载。 - **协议层** (`src/protocol/`):`ProtocolCodec` 是纯虚接口(`encode` / `decode`),当前由 `SerialCodec` 实现自定义串口帧协议。替换协议只需新增一个 `ProtocolCodec` 子类。 - **通讯层** (`src/communication/`):`AbstractCommunicationDriver` 定义了打开/关闭/下发等纯虚接口,当前由 `SerialDriver` 实现串口收发。`CommunicationManager` 将驱动运行在独立子线程中,通过信号槽与主线程通信。 ## 五、通讯协议 当前默认采用 **自定义串口帧协议**,结构清晰、自带校验、支持粘包处理。协议的基本格式如下: | 字段名 | 长度(字节) | 数据内容 | 说明 | | -------------- | ------------ | -------------- | ------------------------------------------------------ | | **帧头 (SOF)** | 2 | `0x55`, `0xAA` | 固定起始标志 | | **帧计数** | 1 | `cnt` | 每发送一帧自增 1,用于检测丢包或乱序(溢出后自动归零) | | **帧 ID** | 2 | `frameId` | 参数 ID(沿用 CAN ID 概念),小端模式(低字节在前) | | **数据长度** | 2 | `len` | 数据字段的长度,小端模式 | | **数据** | N | `data` | 实际传输的参数数据,长度由上一字段决定 | | **CRC16** | 2 | `crc` | Modbus CRC16,小端模式。校验范围:从“版本”到“数据” | | **帧尾 (EOF)** | 2 | `0xAA`, `0x55` | 固定结束标志 | 帧ID段和数据段是必要的,帧ID段用于判定数据对应的参数,数据段是数据的载体。对于用户定义的帧ID必须大于24,这是因为对于小于等于24的ID会用于一些特殊帧(响应帧、控制帧、批量上传帧) ### 5.2 数据打包规则 - 物理值与原始字节之间的转换遵循公式:`physical = raw * scale + offsetValue` - 多字节整型按 **小端模式** 打包与解析 - 一个 `frameId`(一次下行帧)可携带多个上行参数,参数在帧内通过 `offset` 字段区分字节位置 ### 5.3 特殊帧 #### 5.3.1 上行调控帧(frameId = 0) `frameId = 0` 是保留的上行调控帧,上位机专属控制帧,无对应业务参数,作用是**主动指定下位机需要上传的参数ID列表**,实现按需采集、筛选数据,减少无效传输。 上行调控帧**不对应参数表中的任何 ID,固定ID为0**,用于上位机主动告诉下位机"我需要看哪些上行数据"。数据段格式: | 帧ID段(2字节) | 数据段(2×N 字节) | | --------------- | -------------------------------------------------------- | | 0 | 按顺序排列用户勾选的 ID号,每个 ID 占 2 字节,小端打包。 | 如下例子所示,若我想要获取温度和速度信息,选择后发送。 ![image-20260903145134619](ReadMe/image-20260903145134619.png) 则下位机将发送下述十六进制源码到下位机: ```tex 55 AA 00 00 00 04 00 03 01 04 01 99 F0 AA 55 ``` 对源码解析如下: ![image-20260901222320437](ReadMe/image-20260901222320437.png) #### 5.3.2 批量上传帧(frameId = 1) 依赖上行调控帧生效,是下位机的批量数据应答帧。下位机严格按照上行调控帧携带的ID顺序,将对应参数数据依次填入数据段,单帧可完成多参数批量上传,大幅提升数据传输效率。 若上位机未提前下发上行调控帧、无对应ID排序记录,收到该帧后会提示“未注册批量上传配置”。 相较于传统单ID绑定多参数的上传方式,该模式支持动态筛选采集参数,适配复杂控制系统上百级参数的调试场景,灵活性更强。 #### 5.3.3 响应帧(frameId = 2) 下位机应答专用帧,用于确认上位机所有下行指令(普通参数、特殊帧)是否执行成功。 **数据段格式**:固定2字节,存储**被应答的目标帧ID**,告知上位机对应指令已正常接收处理。 **超时机制**:上位机下发指令后,100ms内未收到下位机响应帧,判定为通讯异常并打印日志报错。 | 帧ID段(2字节) | 数据段(2 字节) | | --------------- | ---------------- | | 2 | 需要响应的帧ID | ## 六、下位机调用库 为了方便下位机(如 STM32)快速接入 Solar Debugger,工程的 `device_library/` 文件夹提供了一套纯 C 实现的参数通讯库,无需依赖任何第三方库。 ### 6.1 库文件概览 | 文件 | 模块 | 功能 | |------|------|------| | `serial_protocol.h/.c` | 串口帧协议 | 帧打包、帧解析、CRC16 校验 | | `param_table.h/.c` | 参数管理引擎 | 参数注册、范围校验、数据写入、批量/单次上传 | | `param_manager.h/.c` | 应用层示例 | 具体参数定义、UART 回调处理、周期上传逻辑 | 三个模块的依赖关系:`param_manager` → `param_table` → `serial_protocol`,自上而下调用。 ### 6.2 serial_protocol — 串口帧协议 负责将原始字节流解析为帧结构,或将帧数据打包为字节流。解析时采用**零拷贝**方式,`payload` 指针直接指向接收缓冲区。 **对外接口:** | 函数 | 作用 | |------|------| | `protocol_parse_frame(rx_buf, size, frame)` | 从接收缓冲区中解析一帧,校验帧头、帧尾、CRC16,成功返回 `true` | | `protocol_pack_frame(frame_id, data, data_len, out_buf, out_len)` | 将 frameId + 数据段打包为完整协议帧,自动填充帧头、帧计数、CRC、帧尾 | | `param_table_pack_response(responded_id, out_buf, out_len)` | 打包响应帧(frameId=2),用于应答上位机的下行指令 | | `crc16_modbus(data, len)` | 计算 Modbus CRC16 校验值 | ### 6.3 param_table — 参数管理引擎 通用的参数注册表,将整数 ID 映射到应用变量,支持多数据类型、缩放因子、范围校验和批量上传。最多可注册 32 个参数条目。 **核心概念:** - **scale(缩放因子)**:上行传输时,浮点变量会除以 scale 转为整数发送;上位机收到后乘以 scale 还原物理值 - **flags**:`PARAM_FLAG_FLOAT_TO_INT`(变量是 float,传输时转整数)或 `PARAM_FLAG_DIRECT_INT`(直接拷贝原始字节) - **offset(偏移)**:同一 ID 下多个参数通过 offset 区分字节位置(如 d 轴/q 轴电流共享 ID=258) **对外接口:** | 函数 | 作用 | |------|------| | `param_table_init(tbl, min_id)` | 初始化参数表,设置最小合法 ID | | `param_table_register(tbl, id, offset, type, var_ptr, scale, flags, min, max)` | 注册一个参数条目,绑定 ID 与应用变量 | | `param_table_process(tbl, id, data, len)` | 处理接收到的下行数据:校验范围 → 写入绑定变量 → 置修改标志 | | `param_table_set_upload_ids(tbl, ids, count)` | 设置批量上传的 ID 列表(由上行调控帧触发) | | `param_table_batch_upload(tbl, out_buf, out_len)` | 将所有注册的上传 ID 打包为一帧(frameId=1) | | `param_table_upload_single(tbl, id, out_buf, out_len)` | 将单个 ID 的数据打包为一帧 | | `param_table_clear_modified(tbl)` | 清除所有参数的修改标志 | ### 6.4 param_manager — 应用层示例 这是面向具体项目的应用层文件,演示如何使用上述两个模块。开发者可直接参考此文件进行改造。 **对外接口:** | 函数 | 作用 | |------|------| | `param_init()` | 初始化参数表并注册所有上下行参数 | | `param_handle(id, data, len)` | 处理接收到的帧:id ≥ 24 时写入参数并回复 ACK;id = 0 时配置批量上传列表 | | `param_upload()` | 周期调用,将所有上行参数打包发送(需替换其中的模拟数据为真实传感器数据) | ### 6.5 使用示例讲解 以 `param_manager.c` 为例,分三步说明库的使用方式: **第一步:初始化参数(`param_init`)** ```c /** * 初始化参数管理器,注册所有参数条目 */ void param_init(void) { /* 初始化参数表,设置最小ID */ param_table_init(&g_param_tbl, MIN_PARAM_ID); /**************** 上行数据 ***************/ /* 注册电流参数(ID258,不同偏移) */ param_table_register(&g_param_tbl, PARAM_ID_CURRENT, 0, DT_INT16, &d_current_f, 0.01f, 1, -1000, 1000); param_table_register(&g_param_tbl, PARAM_ID_CURRENT, 2, DT_INT16, &q_current_f, 0.01f, 1, -1000, 1000); /* 注册温度、速度、电池参数 */ param_table_register(&g_param_tbl, PARAM_ID_TEMPERATURE, 0, DT_INT32, &temperature_f, 0.01f, 1, -1000, 1000); param_table_register(&g_param_tbl, PARAM_ID_SPEED, 0, DT_INT16, &speed_f, 0.01f, 1, -500, 500); param_table_register(&g_param_tbl, PARAM_ID_BATTERY, 0, DT_UINT8, &battery_f, 0.1f, 1, 0, 100); /**************** 下行数据 ***************/ /* 注册系统参数 */ param_table_register(&g_param_tbl, PARAM_ID_EMERGENCY, 0, DT_UINT8, &emergency, 1.0f, 2, 0, 1); param_table_register(&g_param_tbl, PARAM_ID_MODE, 0, DT_UINT8, &mode, 1.0f, 2, 1, 16); param_table_register(&g_param_tbl, PARAM_ID_GAIN, 0, DT_FLOAT, &gain, 1.0f, 2, 0.001f, 10.0f); } ``` 每个 `register` 调用将一个 ID 绑定到一个全局变量,后续收发数据时库会自动读写该变量。 **第二步:接收处理(UART 回调 → `param_handle`)** ```c /** * 处理接收到的参数帧 * @note 接收到调控帧后调用,处理参数写入和批量上传ID设置。 * 直接在接收到数据帧后调用发送响应帧,一般不建议在中断直接调用全局变量、占用硬件资源等。 * 但考虑仅在上位机下发数据时调用,程序此时应处于调试或等待指令阶段,一般不会影响外部任务。 * 且发送函数内部会等待发送完成,因此暂时不考虑中断安全问题。 */ void param_handle(uint16_t id, uint8_t *data, uint16_t len) { if (id >= MIN_PARAM_ID) { /* 处理普通参数写入 */ param_table_process(&g_param_tbl, id, data, len); /* 打包响应帧 */ param_table_pack_response(id, send_buf, &send_len); /* 等待串口发送完成并发送数据 */ while (uart3_is_busy()) { /* 等待发送完成 */ } debug_send(send_buf, send_len); } else if (id == PARAM_ID_CONTROL) /* 检测到上行控制帧 */ { /* 处理系统参数:设置批量上传ID列表 */ param_table_set_upload_ids(&g_param_tbl, (uint16_t *)data, len / 2); /* 打包响应帧 */ param_table_pack_response(id, send_buf, &send_len); while (uart3_is_busy()) { /* 等待发送完成 */ } debug_send(send_buf, send_len); /* 清除发送标志,允许上传 */ send_flag = 0; } } ``` `param_handle` 内部逻辑: - `id ≥ 24`:调用 `param_table_process` 将数据写入对应变量,然后发送 ACK 响应帧 - `id = 0`:调用 `param_table_set_upload_ids` 配置批量上传列表 **第三步:周期上传(`param_upload`)** ```c /** * 上传参数(由应用层周期调用) */ void param_upload(void) { static uint32_t tick = 0; /* 模拟时间计数器 */ float sin_val = 0.0f; tick++; /* 计算正弦值,周期约为 100 个 tick */ sin_val = sinf((float)tick * 0.0628f); /* 0.0628 ≈ 2π/100 */ /* 模拟 d_current_f:正弦波,范围 -10.0 ~ 10.0 */ d_current_f = sin_val * 10.0f; /* 模拟 q_current_f:余弦波(正弦偏移90度),范围 -10.0 ~ 10.0 */ q_current_f = sinf((float)tick * 0.0628f + 1.5708f) * 10.0f; /* 模拟 temperature_f:锯齿波,范围 0 ~ 100,周期 100 tick */ temperature_f = (float)(tick % 100); /* 模拟 speed_f:三角波,范围 -500.0 ~ 500.0 */ speed_f = (float)((tick % 200) - 100) * 5.0f; /* 模拟 battery_f:反向锯齿波,范围 0 ~ 100 */ battery_f = 100.0f - ((float)(tick % 200) * 0.5f); /* 批量上传 */ param_table_batch_upload(&g_param_tbl, send_buf, &send_len); /* 单参数上传ID为PARAM_ID_CURRENT的数据 */ // param_table_upload_single(&g_param_tbl, PARAM_ID_CURRENT, send_buf, &send_len); /* 串口发送数据 */ debug_send(send_buf, send_len); } ``` 在定时器中断或主循环中周期调用即可,库会自动按注册的 ID 顺序打包数据。 ### 6.6 快速接入步骤 1. 将 `serial_protocol.h/.c` 和 `param_table.h/.c` 加入你的工程 2. 参考 `param_manager.h/.c`,定义自己的全局变量和参数 ID 3. 在初始化函数中调用 `param_table_register()` 注册所有参数 4. 在 UART 接收回调中调用 `protocol_parse_frame()` + `param_handle()` 5. 在定时任务中调用 `param_upload()`,将模拟数据替换为真实传感器读数 ## 七、License 本项目以开源方式发布,欢迎学习、研究、二次开发使用。具体协议见 `LICENSE`(如未附带则默认遵循 MIT 协议)。 ## 八、致谢 - [Qt Project](https://www.qt.io/) — 跨平台 GUI 框架 - [QCustomPlot](https://www.qcustomplot.com/) — 优秀的 Qt 绘图库 --- > 如果这个项目对你有帮助,欢迎 ⭐ Star 支持;问题与建议请提 Issue,PR 欢迎。 > > QQ:2981218238 > > 邮箱:lsk0358@163.com