开始使用

欢迎使用 Synex

Synex 是面向工程调试的一体化桌面工具,适合串口、UDP、TCP、CAN、MDF/MF4 等多类数据接入和分析场景。它的目标是让你在一个界面里完成设备连接、协议解析、实时观测、控制交互和测试复盘。

如果你正在做电机控制、传感器调试、车载总线分析或嵌入式上位机联调,可以优先从“第一次连接设备”和“协议解析”两节开始。

Synex 主界面
Synex 主工作区:连接配置、实时曲线和可交互 Tile 可以放在同一个调试上下文中。

安装与下载

建议优先从首页下载区获取最新 Windows 发布包。若网络访问 Gitee 不稳定,可以使用百度网盘备用链接。

v3.0.4 开始,Synex 支持软件内自动升级。自动升级会检查当前版本和最新发布包,下载完成后按提示重启即可更新;低于 v3.0.4 的旧版本需要先手动下载新版安装包。

自动升级功能
图示:版本检查、更新提示、下载进度和重启升级入口。

第一次连接设备

推荐先用虚拟数据源确认绘图功能正常,再切换到真实串口、UDP 或 TCP 设备。这样可以快速区分是软件配置问题,还是外部设备/协议链路问题。

  1. 打开连接页,选择连接类型。
  2. 选择协议类型,例如 JustFloatFireWaterRawDataCustomBinary
  3. 设置绘图周期和缓冲区大小。
  4. 点击连接开关,观察实时曲线是否开始刷新。

连接方式

Synex 将串口、UDP、TCP 服务端、TCP 客户端和虚拟数据源统一到同一套连接配置中。对于高吞吐测试,建议优先使用二进制协议和更大的缓冲区。

提示 UDP 和 TCP 适合局域网设备联调;串口适合 MCU 初期 bring-up;虚拟数据源适合演示和验证 UI 布局。

协议解析

连接页的数据引擎决定收到的字节如何变成曲线、图片或控制台原始帧。调试早期优先用 FireWater 或 RawData 验证链路;高频波形用 JustFloat;量产项目或字段结构固定的设备建议发布 CustomBinary。

JustFloat小端 float32 + 帧尾,适合高频多通道曲线。
FireWater一行 CSV 一帧,适合串口助手和人工排查。
CustomBinary帧头、字段、校验、帧尾由协议编辑器配置。
RawData不生成曲线通道,只观察真实收包内容。

JustFloat 高速浮点协议

JustFloat 的普通波形帧由若干个小端 float32 顺序排列,最后追加 0x7F800000 作为帧尾。下面假设电流、转速等业务变量已经在你的控制代码里定义,这里只展示打包和周期发送。

#include <stdint.h>
#include <string.h>
#include "usart.h"

extern UART_HandleTypeDef huart2;
extern float g_synex_iq;
extern float g_synex_id;
extern float g_synex_speed_rpm;

// JustFloat 按 float32 小端顺序发送通道值,并在最后追加 0x7F800000 帧尾。
void Synex_UploadJustFloat(void)
{
    float frame[4];
    const uint32_t tail = 0x7F800000u;

    frame[0] = g_synex_iq;
    frame[1] = g_synex_id;
    frame[2] = g_synex_speed_rpm;
    memcpy(&frame[3], &tail, sizeof(tail));
    HAL_UART_Transmit_DMA(&huart2, (uint8_t *)frame, sizeof(frame));
}

// 在 1 ms 或更快的调度任务中调用,发送频率由你的控制周期决定。
void Synex_JustFloatTask1ms(void)
{
    Synex_UploadJustFloat();
}

FireWater 文本 CSV 协议

FireWater 每行文本是一帧,通道值用英文逗号分隔,行尾必须带 \n。实际工程里它最适合早期联调,直接用 printf 输出当前波形或性能监测值即可。

#include <stdio.h>

extern float g_synex_bus_voltage;
extern float g_synex_iq;
extern float g_synex_id;
extern float g_synex_speed_rpm;

// FireWater 发送一行 CSV;Synex 会按逗号拆成多通道曲线。
void Synex_UploadFireWater(void)
{
    printf("%.2f,%.2f,%.2f,%.1f\n", g_synex_bus_voltage, g_synex_iq, g_synex_id, g_synex_speed_rpm);
}

// FireWater 是文本协议,建议放在 1 ms 到 10 ms 的低速状态任务里。
void Synex_FireWaterTask10ms(void)
{
    Synex_UploadFireWater();
}

图片数据混合流

图片混合流不需要单独协议。设备端先发送一整帧图片包,再紧跟一帧普通波形数据即可;图片包里的 image_size 会告诉 Synex 精确取走多少图片字节,剩余字节会继续按当前协议解析。示例中的 27 表示 JPG,28 表示 PNG;串口 DMA 场景下,Synex_WriteBytes() 需要保证上一段发完再发下一段。

#include <stdint.h>
#include <stdio.h>

extern const uint8_t g_synex_jpg_bytes[];
extern const uint32_t g_synex_jpg_size;
extern float g_synex_iq;
extern float g_synex_id;
extern float g_synex_speed_rpm;
extern int Synex_WriteBytes(const uint8_t *data, uint32_t length);

// FireWater 图片包是 image 头行 + JPG/PNG 原始字节;后面可以继续追加普通 CSV 波形帧。
void Synex_UploadFireWaterImageAndWave(void)
{
    char image_header[48];
    char wave_line[64];
    int image_header_length = snprintf(image_header, sizeof(image_header), "\nimage:0,%lu,-1,-1,27\n", (unsigned long)g_synex_jpg_size);
    int wave_line_length = snprintf(wave_line, sizeof(wave_line), "%.3f,%.3f,%.1f\n", g_synex_iq, g_synex_id, g_synex_speed_rpm);

    if (image_header_length <= 0 || image_header_length >= (int)sizeof(image_header)) return;
    if (wave_line_length <= 0 || wave_line_length >= (int)sizeof(wave_line)) return;

    (void)Synex_WriteBytes((const uint8_t *)image_header, (uint32_t)image_header_length);
    (void)Synex_WriteBytes(g_synex_jpg_bytes, g_synex_jpg_size);
    (void)Synex_WriteBytes((const uint8_t *)wave_line, (uint32_t)wave_line_length);
}
#include <stdint.h>
#include <string.h>

extern const uint8_t g_synex_jpg_bytes[];
extern const uint32_t g_synex_jpg_size;
extern float g_synex_iq;
extern float g_synex_id;
extern float g_synex_speed_rpm;
extern int Synex_WriteBytes(const uint8_t *data, uint32_t length);

// JustFloat 图片头由同步帧尾、5 个 int32 参数和 2 个连续帧尾组成,随后紧跟图片字节。
void Synex_UploadJustFloatImageAndWave(void)
{
    const uint32_t tail = 0x7F800000u;
    uint32_t image_header[8];
    float wave_frame[4];

    image_header[0] = tail;
    image_header[1] = 0u;
    image_header[2] = g_synex_jpg_size;
    image_header[3] = 0xFFFFFFFFu;
    image_header[4] = 0xFFFFFFFFu;
    image_header[5] = 27u;
    image_header[6] = tail;
    image_header[7] = tail;

    wave_frame[0] = g_synex_iq;
    wave_frame[1] = g_synex_id;
    wave_frame[2] = g_synex_speed_rpm;
    memcpy(&wave_frame[3], &tail, sizeof(tail));

    (void)Synex_WriteBytes((const uint8_t *)image_header, sizeof(image_header));
    (void)Synex_WriteBytes(g_synex_jpg_bytes, g_synex_jpg_size);
    (void)Synex_WriteBytes((const uint8_t *)wave_frame, sizeof(wave_frame));
}

CustomBinary 自定义二进制协议

CustomBinary 按协议编辑器发布的字段顺序解析固定长度帧。Header、Tail、Checksum 和大小端都由协议编辑器配置;下位机端直接使用自动生成的 synex_protocol_sender.h / synex_protocol_sender.c。业务代码只填充 Payload,并把底层 UART DMA 发送函数作为回调传给生成的 send_frame

#include <stdint.h>
#include "synex_protocol_sender.h"
#include "usart.h"

extern UART_HandleTypeDef huart2;
extern float g_synex_phase_current;
extern float g_synex_bus_voltage;
extern float g_synex_motor_temp;
extern float g_synex_speed_rpm;

static SynexProtocolSenderFrame g_synex_protocol_frame;
static SynexProtocolSenderPayload g_synex_protocol_payload;

// 适配自动生成代码需要的底层发送回调;这里用 STM32 HAL UART DMA 举例。
int SynexProtocol_WriteBytes(const uint8_t *data, uint16_t length, void *user)
{
    UART_HandleTypeDef *huart = (UART_HandleTypeDef *)user;
    if (data == 0 || length == 0u || huart == 0) return -1;
    return (int)HAL_UART_Transmit_DMA(huart, (uint8_t *)data, length);
}

// 填充自动生成的 Payload,然后交给 send_frame 统一完成组帧、校验和发送。
void Synex_UploadCustomBinary(void)
{
    g_synex_protocol_payload.phase_current = g_synex_phase_current;
    g_synex_protocol_payload.bus_voltage = g_synex_bus_voltage;
    g_synex_protocol_payload.motor_temp = g_synex_motor_temp;
    g_synex_protocol_payload.speed_rpm = g_synex_speed_rpm;

    (void)synex_protocol_sender_send_frame(&g_synex_protocol_frame, &g_synex_protocol_payload, SynexProtocol_WriteBytes, &huart2);
}

// CustomBinary 适合固定字段的高速上报,可以放在 200 us 或 1 ms 调度任务里。
void Synex_CustomBinaryTask200us(void)
{
    Synex_UploadCustomBinary();
}

RawData 原始数据模式

RawData 适合确认线路、波特率、端口和真实输出内容。它不会生成多通道曲线,建议用短文本把关键状态打出来;等链路稳定后,再切换到 FireWater、JustFloat 或 CustomBinary。

#include <stdio.h>

extern float g_synex_iq;
extern float g_synex_id;
extern float g_synex_speed_rpm;

// RawData 只用于观察原始文本,不参与曲线解析。
void Synex_UploadRawDataDebug(void)
{
    printf("iq=%.3f, id=%.3f, speed=%.1f\n", g_synex_iq, g_synex_id, g_synex_speed_rpm);
}

// RawData 主要用于链路检查,周期不宜过高,避免文本日志刷屏。
void Synex_RawDataTask100ms(void)
{
    Synex_UploadRawDataDebug();
}

个人工作台

个人工作台用于把实时观察、控制指令和现场诊断放在同一个页面。你可以按调试对象自由组合绘图、数值、仪表、姿态、图片、串口控制台、滑条、开关和模式选择控件,形成专属于当前设备的联调面板。

个人工作台总览
个人工作台可以把曲线、命令、图片和状态控件放在同一个调试页面。

添加、选择和删除控件

在个人工作台空白区域右键,可以从菜单中添加控件;新控件会优先出现在右键所在位置。单击控件可以激活它,按住 Ctrl 单击可以加入或移出多选,拖动空白区域可以框选多个控件。选中控件后按 Delete 删除,输入框、下拉框和数值编辑框正在编辑时不会触发删除。

复制控件时,先选中目标控件并按 Ctrl + C,再把鼠标移动到希望放置的位置并按 Ctrl + V。粘贴出的控件会保留原控件配置,并生成新的控件 ID。

添加、选择和删除控件
图示:空白区域右键菜单、多选状态、复制粘贴或删除控件的操作入口。

栅格布局与层级

个人工作台采用栅格化布局。当前页面按 12 列、8 行大栅格组织,每个大格再细分为 8 x 8 小格,因此控件既能保持整齐,也能做较细的尺寸调整。工作台允许控件重叠,最近操作的控件会移动到更靠前的层级。

布局建议 实时绘图和图片控件适合占用较大的网格区域;数值、开关、模式选择和滑条适合做成较小控件,放在曲线旁边形成“观察 + 操作”的闭环。
栅格布局与层级
图示:拖动或缩放控件时的网格吸附效果,以及多个控件重叠时的前后层级。

顶部工具栏

实时绘图控件

实时绘图控件用于观察一个或多个通道的高速波形。它支持 YT/XY 图、游标、FFT、截图和右键菜单配置,适合把关键变量放在同一个视图里分析。

✨ 10 亿+ 点 / 秒绘制能力面向高采样率、多通道和长时间缓存场景,降低大量点刷新时的卡顿感。

✨ 长时间大数据观测保留大量历史采样点,配合降采样、动态 Y、局部缩放和游标读取,在趋势和细节之间快速切换。

✨ 实时 FFT 分析时域波形和频域结果可以在同一个控件内联动观察,排查谐波、振动、噪声和控制周期问题更直接。

实时绘图控件外观
图示:通道选择框、曲线绘图区、坐标轴、游标、右上角状态信息和控件外框。

性能状态显示:绘图区域会显示 FPS、点数等渲染状态信息,用于观察当前刷新和绘制压力。高通道数、大缓冲区、高 FPS 或高 MSAA 都会增加 CPU/GPU 负载,必要时可以开启降采样来保持交互流畅。

通道选择:在绘图控件顶部选择需要显示的通道,曲线名称和颜色会跟随右侧通道配置。一个绘图控件可以同时显示多条曲线,适合把同一组相关变量放在同一坐标系里比较。

时域 YT 图:默认模式是 YT 图,X 轴表示时间,Y 轴表示通道值。它适合观察电流、电压、速度、温度等随时间变化的信号。

XY 图:切换到 XY 图后,每条曲线可以选择一个 X 通道源,适合观察轨迹、相图、位置关系或两个变量之间的闭环关系。XY 图不参与时域 X 范围同步。

缩放和平移:在绘图区滚动鼠标滚轮会同时缩放 X/Y 方向;在 X 轴或 Y 轴刻度区域滚动时,只缩放对应轴。按住左键拖动可以平移当前视图。

X 光标:左键双击绘图区可以添加或移动 X 光标,用于读取某一时刻所有曲线的值。拖动 X 光标线可以调整位置。

Y 光标:按住 Ctrl 后左键双击可以添加或移动 Y 光标,用于标记目标值、阈值或对比某个固定幅值。

Tracer 标记:Tracer 用于在曲线上显示当前关注点和数值标签。右键菜单可以切换 Tracer 的标记样式,让它更适合不同密度和颜色的曲线。

自动 Y:顶部工具栏的自动 Y 会立即根据当前可见数据校准纵轴范围,适合数据范围突然变化后快速回到可读视图。

动态 Y:动态 Y 会持续跟随当前可见数据范围变化。它适合波形幅值变化较大但需要一直保持可读的场景,也可以在单个绘图控件的右键菜单里单独开关。

复位视图:复位视图会恢复当前绘图控件的完整 X 范围,并重新调整 Y 轴显示范围。视图被多次缩放、拖拽后,可以用它快速回到整体观察状态。

降采样:长时间运行或大缓冲区场景下,降采样可以减少实际绘制点数,降低显示压力。它不会改变原始缓存数据,只影响绘制负载。

X 光标同步:开启同步后,同一工作台内的 YT 绘图控件会同步 X 光标,适合多图排查同一时刻不同变量的状态。

截图复制:绘图控件支持复制当前主图截图。如果已经打开 FFT,也可以复制主图和 FFT 图的组合截图,便于粘贴到报告、聊天或问题记录中。

CSV 数据保存和加载:顶部工具栏可以保存当前绘图数据,也可以加载 CSV 数据进行离线查看。加载后可以复盘某次实验里的关键波形。

右键菜单功能:绘图控件的右键菜单集中放置显示方式、交互方式和截图工具,常用于临时调整某一个绘图控件的行为。

  • 动态 Y 轴:选择“是”或“否”,控制当前绘图控件是否持续自动调整 Y 轴。
  • 坐标模式:YT图XY图 之间切换。
  • X 通道源:在 XY 图中为曲线选择 X 轴数据来源。
  • X 轴滚动条:控制主图 YT 模式下是否显示 X 轴浮动滚动条。
  • 绘图模式:可选择线图、点图或点线图。线图适合连续波形,点图适合离散采样,点线图适合同时观察趋势和采样点。
  • Tracer 标记:可选择十字形 + 水平、圆圈 + 斜线、菱形 + 斜线三种标记样式。
  • X 轴单位:可选择秒 s、毫秒 ms 或微秒 us
  • 滚轮速度:可选择慢速 10%、中速 20% 或快速 35%,用于改变鼠标滚轮缩放灵敏度。
  • 光标取值方式:可选择线性插值或最近点。线性插值适合平滑读取,最近点适合严格按采样点读取。
  • 隐藏顶部栏:隐藏当前绘图控件顶部的通道和 FFT 配置区域,让曲线区域获得更多空间。
  • 显示顶部栏:恢复被隐藏的顶部配置区域。
  • 复制截图:复制当前主图截图。
  • 复制所有截图:同时复制主图和 FFT 图截图。
实时绘图控件右键菜单
图示:右键菜单展开后的动态 Y、坐标模式、绘图模式、Tracer、单位、滚轮速度、光标取值和截图功能。

FFT 通道:FFT 区域的通道下拉框用于选择参与频域分析的通道。通常选择当前最关心的一条电流、速度、振动或误差信号。

FFT 起始时间:起始时间决定从哪一段时域数据开始截取。你也可以拖动主图上的高亮分析窗口来调整起点。

FFT 分析区域:开启 FFT 后,实时绘图区会显示一段高亮分析区域。按住 Alt + 鼠标左键拖动这段区域,可以直接调整参与 FFT 的时域窗口。

FFT 周期数:周期数决定参与计算的数据长度。周期数越多,频率分辨率通常越高,但会使用更长的一段时域数据。

FFT 基波频率:基波频率用于标定分析窗口和频域参考,适合电机控制、周期振动和固定频率激励场景。

计算一次 FFT:点击计算按钮后,会按当前通道、起始时间、周期数和基波频率计算一次频谱,并显示在 FFT 绘图区。

实时 FFT:打开实时 FFT 后,频谱会跟随新数据刷新。它适合观察频率成分是否随控制状态变化,但计算负载会高于手动计算。

显示 / 隐藏 FFT:可以临时隐藏 FFT 绘图区,只保留主图;需要频域分析时再显示出来。

FFT 图右键菜单:FFT 绘图区也支持样式、Tracer、滚轮速度和截图相关操作,方便频域图单独查看和导出。

实时绘图控件 FFT 功能
图示:FFT 通道、起始时间、周期数、基波频率、计算按钮、实时 FFT 按钮和频谱绘图区。

数值控件

数值控件用于突出显示单个通道的当前值,适合放置电流、电压、转速、温度等关键变量。它会跟随通道配置中的名称和颜色变化,便于和曲线颜色保持一致。

数值控件
图示:数值控件配置窗口和绑定后的显示效果。

仪表控件

仪表控件用于把单个通道显示为圆形表盘,适合转速、母线电压、温度、占空比等有明确范围的变量。配置时通常需要选择通道,并设置标题、最小值、最大值、单位和小数位。

仪表控件
图示:仪表控件显示效果、范围配置和单位配置。

姿态控件

姿态控件用于显示设备或机构的三维姿态,适合 IMU、云台、电机执行机构和机器人关节调试。它可以从欧拉角或四元数通道获取姿态,也可以配置模型文件、模型颜色、透明度、线框、坐标轴和校准参数。

姿态控件
图示:姿态控件、STL 或 glTF/GLB 模型显示、输入通道配置和模型外观配置。

图片控件

图片控件用于显示协议中的图片帧,适合摄像头、视觉算法、中间处理结果或设备截图。右键图片控件可以选择图片通道、填充方式、图片特效和名称显示方式。

图片控件
图示:图片控件右键菜单、填充方式、图片特效列表和不同特效的显示效果。

串口控制台控件

串口控制台控件用于查看和发送原始串口数据,适合协议 bring-up、异常排查和文本日志观察,可以在文本/HEX、时间戳、解码、ANSI、RX/TX 显示和日志记录之间切换。

✨ 高并发大吞吐显示大量 RX/TX 数据连续刷屏时依然保持流畅,适合高频日志和压力测试。

✨ 原始数据完整追踪按真实到达顺序显示输入和输出,便于回看每一段通信过程并定位偶发异常。

串口控制台控件
图示:RX/TX 区域、HEX/时间戳按钮和日志记录入口。

滑条控件

滑条控件用于把连续数值发送给设备,适合速度给定、电流给定、位置给定和占空比调节。配置时通常需要设置显示名称、数值范围、步进、初始值、命令模板和发送方式。

滑条控件
图示:滑条控件、范围配置和命令模板配置。

开关控件

开关控件用于发送双态控制命令,适合使能/失能、运行/停止、闭环/开环等场景。配置时可以设置开关名称、两种状态的显示文本、状态值、命令内容、校验方式和发送目标。

开关控件
图示:开关控件、状态文本、状态值、发送模板和命令绑定配置。

模式选择控件

模式选择控件用于在多个离散模式之间切换,适合控制模式、测试阶段、运行档位和算法分支选择。每个模式都可以配置显示名称、模式值和对应命令。

模式选择控件
图示:模式按钮组、模式文本、模式值、发送模板、同状态重发和命令绑定配置。

指令台

指令台用于维护可复用的设备控制指令。它不是临时输入框,而是一个可以长期保存、分组、排序、重复发送并和个人工作台控件绑定的指令库,适合把使能、停止、清故障、设置目标值、切换模式等现场操作集中管理。

统一维护控制动作 常用指令建议先放进指令台,再让滑条、开关、模式选择等控件绑定它。这样同一条指令的内容、校验和重发策略只需要维护一份。
指令台整体外观
图示:左侧指令台、指令组、单条指令、发送按钮和展开后的配置区域。

指令和指令组

指令台由指令和指令组组成。指令是一条具体发送内容,指令组用于把多条指令按顺序组织起来,例如“进入调试模式”“启动实验流程”“退出并复位”。

指令组和嵌套指令
图示:指令组、子指令组、拖动排序效果和组内顺序发送结构。

发送内容模板

每条指令都可以在 Abc 和 HEX 两种模式之间切换。Abc 适合文本指令和串口调试协议,HEX 适合二进制帧、寄存器写入和带参数的结构化指令。

单条指令配置
图示:指令名称、Abc/HEX 切换、发送内容、校验、重复次数和发送间隔。

校验和 CRC

指令台可以在发送内容中插入校验结果。你只需要在模板中写入 [crc],再选择校验类型和校验范围,Synex 会在发送前生成最终字节内容。

重复发送和顺序发送

单条指令和指令组都可以配置重复次数和发送间隔。单条指令用于重复发送同一个指令,指令组用于按顺序执行一组动作。

绑定个人工作台控件

滑条、开关和模式选择控件可以绑定指令台中的某条指令。绑定后,指令内容、校验、重发参数由指令台统一维护,控件只负责提供当前参数。

控件绑定指令
图示:滑条、开关或模式选择控件右键菜单中的绑定指令入口,以及绑定后的配置预览。

导入、导出和清空

指令台可以导出为 JSON,用于项目备份、团队共享或跨电脑迁移。导入 JSON 会替换当前指令台内容,清空全部指令也会删除当前所有指令和指令组,这两类操作都会在执行前提醒确认。

指令台导入导出
图示:导入、导出、清空全部入口,以及替换当前指令台的确认提示。

协议编辑器

协议编辑器用于设计和发布 CustomBinary 自定义二进制协议。你可以把一帧数据拆成帧头、数据、校验、帧尾等字段积木,逐项配置字节数、字节序、缩放、偏移和曲线输出;发布后,连接页即可选择这个协议解析真实设备数据。

从帧结构到可用协议 协议编辑器关注的是“这一帧长什么样”。字段顺序决定解析顺序,字段属性决定每段字节怎么解释,发布动作则把当前设计同步到连接页和解析器。
协议编辑器整体界面
图示:字段组件库、帧结构时间轴、字段属性面板、协议属性和顶部工具栏。

字段组件库

左侧字段组件库提供常用帧结构积木。点击字段即可追加到当前协议,之后可以在时间轴中选择并配置具体属性。

字段组件库
图示:Header、FrameId、Length、Data、Checksum、Tail、Skip 字段按钮。

帧结构时间轴

中间时间轴按字段顺序展示当前协议的完整帧布局。字段从左到右排列,顺序就是解析器读取字节的顺序;拖动字段可以调整同一协议内的字段顺序,拖动协议行可以调整多个协议在工作区中的排列。

帧结构时间轴
图示:一条包含 Header、多个 Data、Checksum、Tail 的完整帧结构,以及发布状态提示。

字段属性配置

右侧字段属性面板用于决定某段字节的含义。Data 字段通常最常用:你可以设置字段名、类型、字节数、大小端、缩放、偏移,并选择是否输出到曲线。

字段属性配置
图示:Data 字段的字段名、类型、字节序、缩放、偏移和输出到曲线选项。

协议属性与多协议工作区

协议属性用于配置协议名称和代码生成模式。协议编辑器允许在同一个工作区中加载多个协议,适合对比不同版本、临时修改旧协议,或把多个设备的协议放在一起维护。

发布与连接页使用

编辑完成后,需要点击发布,当前协议才会写入本地协议库并同步给连接页。连接页选择对应 CustomBinary 协议后,解析器会按已发布的字段顺序和字段属性处理收到的数据。

固件代码生成

协议编辑器可以根据当前协议生成下位机发送侧 .h / .c 代码。生成代码负责组帧、大小端处理和校验计算,业务代码只需要填充 Payload,并把底层 UART、USB CDC 或网络发送函数作为回调传入。

固件代码生成
图示:代码生成按钮、输出目录选择、生成的 .h/.c 文件和代码生成提示。

CAN 总线分析

CAN 模块面向 CAN/CAN FD 总线调试,采用和个人工作台一致的工作区形态。用户可以创建多个 CAN 工作区,并在每个工作区内自由添加、拖动和调整接收、发送、手动发送、绘图等控件,把总线监控、DBC 信号发送和实时分析放在同一个调试上下文里。

工作区式 CAN 调试 右键空白区域即可添加 CAN 接收、CAN 发送、CAN 手动发送和 CAN 绘图控件;控件可以按实验项目自由布局,适合把“报文观察、信号激励、波形分析、MDF 录制”组合成不同的调试页面。
CAN 工作区整体布局
图示:一个 CAN 工作区内同时放置接收、发送、绘图和手动发送控件。

支持的 CAN 硬件

当前 CAN 配置页支持 KvaserVectorPeak / PCAN创芯ZLG 设备,并提供 Classic CAN 与 CAN FD 两种模式选择。不同硬件的驱动、通道数量、波特率范围和 FD 能力不同,实际连接前建议先确认驱动已经安装并能被系统识别。

CAN 硬件配置
图示:设备类型、通道、Classic CAN / CAN FD、仲裁域和数据域参数配置。

DBC 加载与解析

CAN 页面支持加载 DBC 文件,也可以把 DBC 文件拖入页面解析。DBC 加载后,Synex 会按报文和信号定义解析接收到的总线数据,并把信号以树形结构展示出来,方便直接查看物理值、选择信号绘图,或在发送控件里按物理量编辑报文。

DBC 加载与解析
图示:DBC 文件加载、解析结果、报文与信号定义的基础信息。

CAN 接收控件

CAN 接收控件用于观察实时总线报文。加载 DBC 后,接收控件会把报文按照 DBC 结构展开,既可以看原始报文,也可以直接看解码后的信号值,适合做联调时的总线状态看板。

DBC 接收树
图示:DBC 文件名、接收报文树、信号物理值和单位显示。

CAN 发送控件

CAN 发送控件用于按 DBC 组织报文发送。用户选择要发送的 DBC 报文后,可以在信号级别编辑物理值,由 Synex 根据 DBC 自动打包为 CAN 数据帧,避免手工计算位域、大小端和缩放系数。

DBC 报文发送
图示:DBC 发送列表、信号编辑区、周期发送和触发方式配置。

发送信号生成器

信号生成器用于让某个发送信号按规则自动变化,适合做阶跃响应、扫值测试、边界测试和控制链路验证。配置完成后,CAN 发送控件会在发送任务运行时实时计算信号值,并按 DBC 打包到对应报文里。

CAN 手动发送控件

CAN 手动发送控件不依赖 DBC,适合快速验证硬件连接、发送临时原始帧,或调试还没有整理成 DBC 的私有协议。用户可以直接填写 ID、DLC 和数据字节,并选择单次、周期或按键触发发送。

CAN 手动发送控件
图示:ID、DLC、数据字节、单次发送和周期发送设置。

CAN 绘图控件

CAN 绘图控件用于把 DBC 信号直接送入实时绘图区。它适合观察控制目标、反馈量、状态量之间的动态关系,也可以把多个信号放在同一工作区中和接收、发送控件一起联动分析。

CAN 信号绘图
图示:多个 DBC 信号实时绘图、游标读取和暂停分析。

CAN 报文录制为 MDF

CAN 页面支持把总线数据录制为 MDF,适合把实车、台架或长时间联调过程保存下来,再进入 MDF 离线回放流程做复盘、截图和报告整理。录制前建议确认 DBC、硬件通道和目标保存路径,避免长时间测试结束后发现信号上下文不完整。

MDF 离线回放

MDF/MF4 模块用于离线分析标定和测试数据。你可以加载文件、选择信号、分组显示,并将关键波形截图用于报告。

AI 助手连接 / MCP

Synex 支持通过 MCP 让外部 AI 连接正在运行的应用。开启后,AI 会通过 SynexMcpServer.exe 连接本机 Synex,并访问当前 UI 中的真实指令面板、协议编辑器和个人工作台;关闭后,本机发现文件会被删除,外部 AI 无法继续连接。

安全边界 MCP 只在你手动开启顶部工具栏“AI 助手连接”后生效。当前桥接走本机 IPC,不开放网络端口;会修改界面的工具调用建议先让 AI 预览,再确认应用。

当前 MCP 能力

MCP 开启后,外部 AI 不是简单读取静态文件,而是连接正在运行的 Synex UI。它可以读取当前界面状态,也可以在你确认后修改真实工作区配置。

MCP 联动个人工作台
图示:AI 可以读取个人工作台页面、控件布局和控件配置,也可以按确认后的方案创建或调整调试页面。
MCP 联动协议编辑器
图示:AI 可以读取协议字段、校验配置和发布状态,并辅助生成协议草稿、示例帧和下位机参考代码。
MCP 联动指令台
图示:AI 可以读取指令组和单条指令,预览指令台修改内容,并在确认后应用到当前运行中的 Synex。

Claude Code 接入

如果使用 Claude Code,可以把发布包中的 SynexMcpServer.exe 注册为 stdio server。下面路径按安装目录举例,实际使用时替换为你本机的 Synex 安装路径。

claude mcp add --scope user --transport stdio synex -- "D:\Synex_v3.0.5\mcp\SynexMcpServer.exe"
claude mcp list
claude mcp get synex

如果后续不再需要让 Claude Code 连接 Synex,可以单独执行卸载命令。

claude mcp remove synex --scope user

Codex 接入

Codex CLI 和 Codex IDE 扩展共用 MCP 配置。你可以用命令行添加,也可以把同等配置写入 ~/.codex/config.toml 或项目内受信任的 .codex/config.toml

codex mcp add synex -- "D:\Synex_v3.0.5\mcp\SynexMcpServer.exe"
[mcp_servers.synex]
command = 'D:\Synex_v3.0.5\mcp\SynexMcpServer.exe'
startup_timeout_sec = 10
tool_timeout_sec = 60

如果使用 Codex 桌面端,验证时不需要再打开命令行。先重启 Codex 或刷新当前会话,让它重新读取 MCP 配置;然后在 Codex 的 MCP/工具列表里确认能看到 synex,最后让 Codex 调用 app_ping

1. 确认 Synex 顶部工具栏已经开启“AI 助手连接”。
2. 重启 Codex 桌面端,或刷新当前 Codex 会话。
3. 打开 Codex 的 MCP/工具列表,确认能看到 synex。
4. 对 Codex 说:请调用 Synex MCP 的 app_ping 检查连接。

VS Code Copilot 接入

VS Code 的 Copilot Agent 模式可以读取 MCP server。建议使用用户级配置:在命令面板运行 MCP: Open User Configuration,把下面内容写入 VS Code 打开的用户配置文件。

{
  "servers": {
    "synex": {
      "command": "D:\\Synex_v3.0.5\\mcp\\SynexMcpServer.exe"
    }
  }
}

验证 VS Code Copilot 是否安装成功时,先从命令面板查看 MCP server 状态,再在 Copilot Chat 的工具选择里确认 Synex 工具可用。最后同样调用 app_ping,用它确认当前运行中的 Synex 已经开启 AI 助手连接。

1. 打开命令面板,运行 MCP: List Servers。
2. 找到 synex,确认它可以启动且没有报错。
3. 打开 Copilot Chat 的 Agent 模式,进入 Configure Tools,确认能看到 synex 的工具。
4. 对 Copilot 说:请调用 Synex MCP 的 app_ping 检查连接。

配置完成后,先在 Synex 顶部工具栏开启“AI 助手连接”,再让外部 AI 调用 app_ping 检查连接。若 Synex 重启或重新开启连接,token 会重新生成;长期运行的 AI 客户端遇到连接失败时,重启 MCP server 通常即可恢复。

校验算法说明

Synex 中的指令台、连接页发送、协议编辑器和 CustomBinary 解析共用同一套校验算法。下位机如果需要解析 Synex 下发的指令,或者要手写与上位机一致的协议收发代码,可以按本节规则实现。

先确认校验范围 算法名称只决定怎么算;真正容易出错的是哪些字节参与计算、校验值写在什么位置、2 字节结果按小端还是大端写入。

支持的算法

名称 长度 计算规则
sum8 1 字节 参与字节逐字节相加,结果保留低 8 位。
sum16 2 字节 参与字节逐字节相加,结果保留低 16 位。
xor8 1 字节 初值 0x00,参与字节逐字节异或。
crc8 1 字节 CRC-8,非反射,高位先算,初值 0x00,多项式 0x07
crc8-maxim 1 字节 CRC-8/MAXIM,反射算法,初值 0x00,反射多项式 0x8C
crc16-modbus 2 字节 CRC-16/MODBUS,反射算法,初值 0xFFFF,反射多项式 0xA001
crc16-ccitt 2 字节 CRC-16/CCITT-FALSE 风格,非反射,高位先算,初值 0xFFFF,多项式 0x1021
crc16-xmodem 2 字节 CRC-16/XMODEM,非反射,高位先算,初值 0x0000,多项式 0x1021

参与校验的数据范围

校验结果写入方式

下位机 C 参考实现

下面的实现按 Synex 固件代码生成器中的算法整理。若你使用协议编辑器直接生成 CustomBinary 发送代码,生成文件里已经包含对应校验函数;只有在下位机需要手写解析 Synex 下发指令或自定义接收逻辑时,才需要复制这些函数。

#include <stdint.h>

static uint16_t Synex_CheckSum8(const uint8_t *data, uint16_t length)
{
    uint8_t sum = 0u;
    for (uint16_t index = 0u; index < length; ++index)
    {
        sum = (uint8_t)(sum + data[index]);
    }
    return sum;
}

static uint16_t Synex_CheckSum16(const uint8_t *data, uint16_t length)
{
    uint16_t sum = 0u;
    for (uint16_t index = 0u; index < length; ++index)
    {
        sum = (uint16_t)(sum + data[index]);
    }
    return sum;
}

static uint16_t Synex_CheckXor8(const uint8_t *data, uint16_t length)
{
    uint8_t value = 0u;
    for (uint16_t index = 0u; index < length; ++index)
    {
        value ^= data[index];
    }
    return value;
}

static uint16_t Synex_CheckCrc8(const uint8_t *data, uint16_t length)
{
    uint8_t crc = 0x00u;
    for (uint16_t index = 0u; index < length; ++index)
    {
        crc ^= data[index];
        for (uint8_t bit = 0u; bit < 8u; ++bit)
        {
            crc = (crc & 0x80u) ? (uint8_t)((crc << 1) ^ 0x07u) : (uint8_t)(crc << 1);
        }
    }
    return crc;
}

static uint16_t Synex_CheckCrc8Maxim(const uint8_t *data, uint16_t length)
{
    uint8_t crc = 0x00u;
    for (uint16_t index = 0u; index < length; ++index)
    {
        crc ^= data[index];
        for (uint8_t bit = 0u; bit < 8u; ++bit)
        {
            crc = (crc & 0x01u) ? (uint8_t)((crc >> 1) ^ 0x8Cu) : (uint8_t)(crc >> 1);
        }
    }
    return crc;
}

static uint16_t Synex_CheckCrc16Modbus(const uint8_t *data, uint16_t length)
{
    uint16_t crc = 0xFFFFu;
    for (uint16_t index = 0u; index < length; ++index)
    {
        crc ^= data[index];
        for (uint8_t bit = 0u; bit < 8u; ++bit)
        {
            crc = (crc & 0x0001u) ? (uint16_t)((crc >> 1) ^ 0xA001u) : (uint16_t)(crc >> 1);
        }
    }
    return crc;
}

static uint16_t Synex_CheckCrc16Ccitt(const uint8_t *data, uint16_t length)
{
    uint16_t crc = 0xFFFFu;
    for (uint16_t index = 0u; index < length; ++index)
    {
        crc ^= (uint16_t)(data[index] << 8);
        for (uint8_t bit = 0u; bit < 8u; ++bit)
        {
            crc = (crc & 0x8000u) ? (uint16_t)((crc << 1) ^ 0x1021u) : (uint16_t)(crc << 1);
        }
    }
    return crc;
}

static uint16_t Synex_CheckCrc16Xmodem(const uint8_t *data, uint16_t length)
{
    uint16_t crc = 0x0000u;
    for (uint16_t index = 0u; index < length; ++index)
    {
        crc ^= (uint16_t)(data[index] << 8);
        for (uint8_t bit = 0u; bit < 8u; ++bit)
        {
            crc = (crc & 0x8000u) ? (uint16_t)((crc << 1) ^ 0x1021u) : (uint16_t)(crc << 1);
        }
    }
    return crc;
}

static void Synex_WriteU16Le(uint8_t *dest, uint16_t value)
{
    dest[0] = (uint8_t)(value & 0x00FFu);
    dest[1] = (uint8_t)((value >> 8) & 0x00FFu);
}

static void Synex_WriteU16Be(uint8_t *dest, uint16_t value)
{
    dest[0] = (uint8_t)((value >> 8) & 0x00FFu);
    dest[1] = (uint8_t)(value & 0x00FFu);
}

常见问题

连接成功但没有数据显示

先确认设备确实在发送数据,再检查协议类型、波特率或网络端口。若协议不确定,可先切换到 RawData 观察原始数据。

波形刷新卡顿

降低绘图刷新频率、减少同时显示通道数,或开启更适合当前机器的降采样策略。高吞吐场景也建议使用二进制协议。

自定义协议解析失败

重点检查帧头帧尾、字节序、字段长度、缩放系数和校验算法。协议编辑完成后建议用固定样本帧先验证。

反馈与支持

如果你希望补充新的硬件适配、协议模板或示例工程,可以在项目页面提交反馈。反馈时请尽量附带软件版本、连接方式、协议配置和复现步骤。