Qt 串口调试入门-2:发送区工程化
Qt 串口调试入门-2:发送区工程化

发送区工程化:HEX/转义/CRC/自动发送的统一入口

第二次汇报学习笔记:把“发送”从按钮点击,做成可配置能力——同一个发送区既能发文本,也能发二进制(HEX/转义),还能按需追加 CRC,并支持定时自动发送。

串口工具里,“发送”看似简单,其实最容易变成一团 if-else:

  • 勾了 HEX 就按 HEX 发;

  • 勾了转义就解析 \n

  • 勾了 CRC 就末尾追加两字节;

  • 勾了自动发送还要挂定时器; 最后写出来的函数通常又长又绕。

我后来调整思路:把发送区抽象成结构体 + 统一的 build 流程,做到每个能力都“插拔式”。


1. 把发送区抽象成结构体:控件不再散落各处

一个发送区(例如发送区 1)通常包含:

  • 文本输入框(QPlainTextEdit)

  • 一组复选框(HEX、转义、CRC、自动发送、参加轮发)

  • 一个周期输入框(ms)

  • 一个定时器(自动发送用)

把这些“天然属于一个发送区”的东西收拢成结构体,代码阅读性会立刻提升:

struct SendAreaEx {
  QPlainTextEdit *edit;
  QCheckBox *cbHex;
  QCheckBox *cbEscape;
  QCheckBox *cbCrc;
  QCheckBox *cbRound;
  QCheckBox *cbAuto;
  QLineEdit *lePeriod;
  QTimer *timer;
};

然后做一个 m_sendArea[3],把 3 个发送区统一管理。


2. 统一发送入口:sendFromArea(i)

我给每个发送区一个索引(0/1/2),所有发送都走同一个入口:

  1. 检查串口是否已打开;

  2. 调用 buildAreaBytes() 把“用户输入 + 复选框选项”转换成最终字节流;

  3. 调用 sendRawBytes(bytes) 发出去。

这样按钮点击(手动发送)和定时器触发(自动发送)都可以复用同一条链路。


3. buildAreaBytes:把“字符串”变成“要发的字节”

核心逻辑就是:输入是 QString,输出是 QByteArray,中间根据用户选项走不同分支:

  • HEX 勾选 → 解析 HEX 字符串;

  • 非 HEX:

    • 勾选转义 → 解析 \n \r \t \0 \\ \xNN 等;

    • 否则 → 按本地编码 toLocal8Bit()

  • 最后如果 CRC 勾选 → 追加 Modbus CRC16(两个字节)。

我喜欢把它写成“从上到下的流水线”,减少嵌套。


4. HEX 解析:容错比“能跑”更重要

HEX 发送经常遇到用户输入:

  • 01 03 00 00 00 02

  • 0x01 0x03 0x00 0x00 0x00 0x02

  • 010300000002

所以解析前先做规范化:

  1. trim;

  2. 去所有空白;

  3. 0x 前缀;

  4. 大写;

  5. 长度必须为偶数;

  6. 每两位转一个字节,遇到非法字符给出明确错误。

当解析失败时,不要悄悄返回空,最好给出 “HEX 包含非法字符:XX” 这种定位明确的信息,用户体验会好很多。


5. 转义解析:让“文本输入框”也能发二进制

转义解析的价值在于:不用切换到 HEX 也能构造二进制 payload,比如:

  • hello\n

  • \x01\x03\x00\x00\x00\x02

实现上本质是一个扫描器:遇到反斜杠就看后续字符,根据规则写入对应字节;否则写入原字符的字节。

这里的关键点是:转义解析输出是 QByteArray,而不是 QString。因为 \0\xNN 都可能产生不可打印字符。


6. 自动发送:用 QTimer 把“能力”挂上去

自动发送的实现不应该写进 on_xxx_clicked() 里,否则会导致逻辑分叉。

更合理的做法是:

  • 给每个发送区都配一个 timer;

  • 复选框 toggled(true) 时启动 timer,interval 从周期输入框读取;

  • timeout 时调用 sendFromArea(i, quiet=true)

  • 周期文本变化时,如果 timer 正在跑就动态更新 interval;

  • 周期输入用 QIntValidator 限制为整数(ms)。

这样自动发送就是一个“组件能力”,不会污染手动发送逻辑。


7. 小结

这一阶段最大的收获是:把“发送”当成可组合能力,而不是按钮事件

  • 结构体把同类控件聚拢;

  • sendFromArea → buildAreaBytes → sendRawBytes 的流水线让所有发送路径统一;

  • HEX/转义/CRC 的差异只存在于 build 阶段;

  • 自动发送只是 timer 调度同一个入口。

下一篇会写“轮发状态机”:当多个发送区参与轮发时,如何支持“定时轮发”和“等回复轮发”,并加入超时重发策略。

如果对您有帮助的话,能否支持一下博主?
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇