<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>运动控制 on Zesean 的博客</title><link>https://zeseanblog.top/tags/%E8%BF%90%E5%8A%A8%E6%8E%A7%E5%88%B6/</link><description>Recent content in 运动控制 on Zesean 的博客</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><lastBuildDate>Sat, 05 Sep 2026 14:53:14 +0800</lastBuildDate><atom:link href="https://zeseanblog.top/tags/%E8%BF%90%E5%8A%A8%E6%8E%A7%E5%88%B6/index.xml" rel="self" type="application/rss+xml"/><item><title>冷阴极 X 射线源球壳钛合金焊缝检测装置（下位机）</title><link>https://zeseanblog.top/projects/xray-motion-control/</link><pubDate>Tue, 01 Sep 2026 22:30:00 +0800</pubDate><guid>https://zeseanblog.top/projects/xray-motion-control/</guid><description>&lt;h2 id="项目概述"&gt;&lt;a href="#%e9%a1%b9%e7%9b%ae%e6%a6%82%e8%bf%b0" class="header-anchor"&gt;&lt;/a&gt;项目概述&#13;&#10;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;国家重点研发项目&lt;/strong&gt;子课题：冷阴极 X 射线源球壳钛合金焊缝检测装置的运动控制系统（下位机部分）。&lt;/p&gt;&#10;&lt;p&gt;装置通过 X 射线源与平板探测器沿弧形轨道协同扫查，对球壳钛合金焊缝成像检测。下位机负责对 X 射线源、探测器的&lt;strong&gt;协同控制与远程控制&lt;/strong&gt;：配合 QT 上位机调整射线源与探测器的速度与动作，完成单段焊缝、截面焊缝两种检测模式，并实时上报系统状态。&lt;/p&gt;&#10;&lt;h2 id="系统组成"&gt;&lt;a href="#%e7%b3%bb%e7%bb%9f%e7%bb%84%e6%88%90" class="header-anchor"&gt;&lt;/a&gt;系统组成&#13;&#10;&lt;/h2&gt;&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart TB&#10; PC["QT 上位机"] &lt;--&gt;|"RS485 自定义协议帧&lt;br/&gt;UART 中断+DMA · 字节状态机 · 环形缓冲"| MCU["STM32F429 下位机&lt;br/&gt;FreeRTOS 多任务"]&#10; MCU --&gt;|"Modbus RTU + CRC16"| SERVO["禾川伺服电机驱动器"]&#10; SERVO --&gt; AXIS["弧形轨道多电机协同"]&#10; AXIS --&gt; DEV["X 射线源 + 平板探测器"]&lt;/pre&gt;&lt;p&gt;&lt;img alt="检测装置整机：弧形轨道与伺服驱动系统" class="gallery-image" data-flex-basis="179px" data-flex-grow="74" height="1706" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://zeseanblog.top/projects/xray-motion-control/images/device-overview.jpg" srcset="https://zeseanblog.top/projects/xray-motion-control/images/device-overview_hu_53f8fa1bc98513c9.jpg 800w, https://zeseanblog.top/projects/xray-motion-control/images/device-overview.jpg 1279w" width="1279"&gt;&lt;/p&gt;&#10;&lt;p&gt;整机实拍：左侧为提升立柱与弧形齿轮轨道，右侧为探测器载滑车，地面为多路禾川伺服驱动器与供电线缆。&lt;/p&gt;&#10;&lt;h2 id="软件分层与模块"&gt;&lt;a href="#%e8%bd%af%e4%bb%b6%e5%88%86%e5%b1%82%e4%b8%8e%e6%a8%a1%e5%9d%97" class="header-anchor"&gt;&lt;/a&gt;软件分层与模块&#13;&#10;&lt;/h2&gt;&lt;p&gt;软件分为两层：&lt;strong&gt;APP 应用层&lt;/strong&gt;承载对外协议、运动语义与任务调度；&lt;strong&gt;驱动层&lt;/strong&gt;提供寄存器级能力（Modbus 协议模板与 CRC、电机寄存器配置序列、OLED/菜单/按键、FLASH 参数存储）。&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;模块&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;职责&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;app_conf.h&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;全部可调参数集中管理：协议常量、超时、限幅、软限位、任务栈与优先级、RS485 开关&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;host_proto.h/.c&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;RS485/USART6 二进制协议栈&lt;/strong&gt;：TX DMA + 环形缓冲 + 组包 CRC；RX IDLE+DMA + 环形缓冲 + 字节状态机解析；命令分发回调；心跳时间戳；收发统计&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;motion_api.h/.c&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;统一运动控制接口&lt;/strong&gt;：轴状态机（IDLE/READY/MOVING/FAULT）、方向（速度符号）/速度（±3000 限幅）/动作时序（松闸稳定→运动→清速减速→抱闸）、编码器事务三重校验、软限位、安全关断收敛入口、故障复位&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;dev_task.h/.c&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;四大逻辑任务&lt;/strong&gt;：motionTask（动作执行+ACK）、statusTask（50ms 状态轮询+100ms 状态帧）、monitorTask（心跳超时/故障沿上报/IWDG 喂狗点）、装配函数 &lt;code&gt;APP_TasksInit()&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h2 id="软件架构"&gt;&lt;a href="#%e8%bd%af%e4%bb%b6%e6%9e%b6%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;软件架构&#13;&#10;&lt;/h2&gt;&lt;h3 id="数据流下行指令--上行状态"&gt;&lt;a href="#%e6%95%b0%e6%8d%ae%e6%b5%81%e4%b8%8b%e8%a1%8c%e6%8c%87%e4%bb%a4--%e4%b8%8a%e8%a1%8c%e7%8a%b6%e6%80%81" class="header-anchor"&gt;&lt;/a&gt;数据流（下行指令 → 上行状态）&#13;&#10;&lt;/h3&gt;&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart TB&#10; PC["上位机"] --&gt;|"A5 5A 协议帧"| RX["USART6 RX&lt;br/&gt;IDLE 中断 + DMA 批量入环形缓冲"]&#10; RX --&gt; PARSE["parseTask（AboveNormal）&lt;br/&gt;7 态字节状态机 + CRC16 查表校验"]&#10; PARSE --&gt;|"CRC 通过"| Q["motionQueue&lt;br/&gt;8 深消息队列削峰"]&#10; Q --&gt; MOTION["motionTask（High）&lt;br/&gt;动作时序执行 + ACK 上行"]&#10; MOTION --&gt;|"轴级互斥锁串行化"| MB["UART4/5 RS485&lt;br/&gt;Modbus RTU"]&#10; MB --&gt; DRIVE["禾川伺服驱动器&lt;br/&gt;速度环 + 电流环硬件闭环"]&#10; DRIVE --&gt; AXIS["弧形轨道双轴电机"]&#10;&#10; STATUS["statusTask（Normal · 50ms）&lt;br/&gt;编码器轮询 + 软限位检查"] --&gt; MB&#10; STATUS --&gt;|"100ms 状态帧 0x82&lt;br/&gt;TX 环形缓冲 + DMA"| PC&#10; MON["monitorTask（Normal · 100ms）&lt;br/&gt;心跳超时 / 故障沿 / IWDG"] --&gt;|"SafeShutdown + 0x8F 上报"| MOTION&lt;/pre&gt;&lt;h3 id="任务划分"&gt;&lt;a href="#%e4%bb%bb%e5%8a%a1%e5%88%92%e5%88%86" class="header-anchor"&gt;&lt;/a&gt;任务划分&#13;&#10;&lt;/h3&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;任务&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;CMSIS 优先级&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;栈&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;阻塞点&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;职责&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;parseTask&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;AboveNormal&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2KB&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;线程标志（50ms 超时）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;协议解析（状态机+CRC）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;motionTask&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;High&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2KB&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;消息队列（100ms 超时）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;动作执行 + 定时动作截止判定&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;statusTask&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Normal&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2KB&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;osDelay(50)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;编码器轮询 + 状态反馈&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;monitorTask&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Normal&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1.5KB&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;osDelay(100)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;心跳超时/故障沿/安全关断触发&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;oledShowMenuTask&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Low&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;512B&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;事件标志&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;菜单显示（按键扫描定时器 10ms）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;defaultTask&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Normal&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;512B&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;osDelay(1000)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;LED 心跳&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;两个关键的执行体设计：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;协议解析与动作执行分离&lt;/strong&gt;：解析出指令后经 8 深消息队列投递给 motionTask 按序执行，队列削峰保证解析节奏不受动作耗时拖累；&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;编码器轮询放在任务而非软件定时器中&lt;/strong&gt;：定时器回调与任务可能并发访问同一 Modbus 通道，统一到任务上下文并配合轴级互斥锁后，「请求-应答」事务被彻底串行化。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="任务优先级设计依据"&gt;&lt;a href="#%e4%bb%bb%e5%8a%a1%e4%bc%98%e5%85%88%e7%ba%a7%e8%ae%be%e8%ae%a1%e4%be%9d%e6%8d%ae" class="header-anchor"&gt;&lt;/a&gt;任务优先级设计依据&#13;&#10;&lt;/h3&gt;&lt;p&gt;优先级不是按“功能重要性”排的，而是按三条工程准则：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&lt;strong&gt;截止期与后果&lt;/strong&gt;：动作执行（清速/抱闸/安全关断）有硬时序且后果不可逆 → 最高应用优先级；&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;阻塞特性&lt;/strong&gt;：事件驱动型任务平时全部阻塞在同步原语上，高优先级只影响唤醒延迟，不产生 CPU 争抢；&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;依赖方向&lt;/strong&gt;：数据流上游不能被下游长时间阻塞（parse 喂 motion 的队列；status 的编码器数据被 motion 的轴锁依赖）。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;完整优先级谱系（NVIC 中断优先级与任务优先级是两套量纲，勿混）：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 2&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 3&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 4&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 5&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 6&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 7&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 8&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 9&#10;&lt;/span&gt;&lt;span class="lnt"&gt;10&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NVIC 中断 (任何 ISR 可抢占任何任务)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ UART4/5/6/8 抢占优先级=5（恰好等于 FreeRTOS 可调用 API 的阈值，&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ISR 内允许 FromISR API；只做批量入环形缓冲 + flagsSet，µs 级）&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;任务 (FreeRTOS 原生优先级，抢占式 + 同级时间片轮转)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ 38 High motionTask ← 安全动作的执行权最高&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ 31 AboveNormal parseTask ← 必须高于周期型任务，否则状态轮询可推迟命令解析&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ 23 Normal statusTask / monitorTask / defaultTask&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ 8 Low oledShowMenuTask（人机界面，空闲余量处理）&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ 2 Timer Service（按键扫描定时器，CubeMX 默认值）&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─ 0 Idle&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;几个关键取舍：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;为什么 motion(38) &amp;gt; parse(31)&lt;/strong&gt;：命令队列已削峰（8 深），解析晚 1–2ms 不影响正确性；反之若 parse 更高，解析长帧期间到达的停止/安全命令会被推迟执行——&lt;strong&gt;安全动作的执行权高于协议的解析权&lt;/strong&gt;。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;status/monitor 同级 Normal&lt;/strong&gt;：两者都是周期型软实时（50ms/100ms），判定窗口（软限位/心跳 500ms）远大于调度延迟；它们持锁时间短（单次 Modbus 事务约 6ms），motion 高优先级被轴锁阻塞时由&lt;strong&gt;互斥量优先级继承&lt;/strong&gt;临时提升持锁任务，消除优先级反转。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;关中断快照 vs 互斥量&lt;/strong&gt;：跨任务读轴状态用 &lt;code&gt;__disable_irq()&lt;/code&gt; 短临界区（µs 级、无反转风险）；锁只用于&lt;strong&gt;必须串行化的 Modbus 事务&lt;/strong&gt;（同一 UART 的请求-应答配对）。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;一个已知限制&lt;/strong&gt;：定时器守护任务优先级为 CubeMX 默认值 2，低于所有应用任务——当前成立的条件是所有任务都有周期阻塞点，定时器总能在间隙运行；若未来把安全逻辑放进软件定时器（如限位去抖），必须提高该优先级。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Realtime(47) 刻意留空&lt;/strong&gt;：为硬实时扩展预留（5–20ms 节拍的位置环、限位 EXTI 的紧急处理链）。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="轴状态机"&gt;&lt;a href="#%e8%bd%b4%e7%8a%b6%e6%80%81%e6%9c%ba" class="header-anchor"&gt;&lt;/a&gt;轴状态机&#13;&#10;&lt;/h3&gt;&lt;pre class="mermaid" style="visibility:hidden"&gt;stateDiagram-v2&#10; [*] --&gt; IDLE&#10; IDLE --&gt; READY: InitAll（伺服参数+原点捕获+松闸）&#10; READY --&gt; MOVING: SetSpeed / MoveTimed&#10; MOVING --&gt; READY: Stop（清速→等减速→抱闸）&#10; READY --&gt; FAULT: Safe_Shutdown（异常保护）&#10; MOVING --&gt; FAULT: Safe_Shutdown&#10; FAULT --&gt; READY: FaultReset（重初始化+重捕获原点+松闸）&lt;/pre&gt;&lt;p&gt;任何异常都收敛到&lt;strong&gt;单一安全关断入口&lt;/strong&gt; &lt;code&gt;MOTION_SafeShutdown()&lt;/code&gt;：清速 → 等 150ms（驱动器 100ms 减速斜坡完成，避免高速抱闸冲击）→ 断抱闸继电器 → 锁定 FAULT + 故障码 → monitorTask 沿检测上报。恢复必须显式故障复位指令。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="下位机主控板：STM32F429 + 多路 RS485（RJ45 座）与继电器输出" class="gallery-image" data-flex-basis="320px" data-flex-grow="133" height="1279" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://zeseanblog.top/projects/xray-motion-control/images/control-board.jpg" srcset="https://zeseanblog.top/projects/xray-motion-control/images/control-board_hu_6a4d8b99c70223cc.jpg 800w, https://zeseanblog.top/projects/xray-motion-control/images/control-board_hu_957865d0400ddf81.jpg 1600w, https://zeseanblog.top/projects/xray-motion-control/images/control-board.jpg 1706w" width="1706"&gt;&lt;/p&gt;&#10;&lt;h2 id="上位机帧协议"&gt;&lt;a href="#%e4%b8%8a%e4%bd%8d%e6%9c%ba%e5%b8%a7%e5%8d%8f%e8%ae%ae" class="header-anchor"&gt;&lt;/a&gt;上位机帧协议&#13;&#10;&lt;/h2&gt;&lt;h3 id="帧格式"&gt;&lt;a href="#%e5%b8%a7%e6%a0%bc%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;帧格式&#13;&#10;&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;┌──────┬──────┬─────┬──────┬─────────────┬─────────────┬──────────────┐&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 0xA5 │ 0x5A │ SEQ │ LEN │ CMD │ DATA[LEN] │ CRC16_LO/HI │&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└──────┴──────┴─────┴──────┴─────────────┴─────────────┴──────────────┘&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 帧头1 帧头2 序号 载荷长 命令字 0~64B 小端 查表 CRC16，低字节先&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;双帧头 A5 5A&lt;/strong&gt;：误同步率 1/65536；A5/5A 位翻转丰富，示波器上可辨；&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;SEQ 序号&lt;/strong&gt;：下行主机自增（ACK 回显 → 去重/丢帧检测），上行设备独立自增；&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;LEN 显式变长&lt;/strong&gt;：状态机据此收数，天然支持命令扩展；&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;CRC16 覆盖 SEQ..DATA&lt;/strong&gt;（不含帧头与自身），算法与 Modbus 完全一致（同一张查表），全协议栈一套 CRC；&lt;/li&gt;&#10;&lt;li&gt;帧开销 7 字节；载荷中可能出现假帧头，由 CRC 兜底丢弃（更高可靠场景可选 COBS 变长编码）。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="命令集"&gt;&lt;a href="#%e5%91%bd%e4%bb%a4%e9%9b%86" class="header-anchor"&gt;&lt;/a&gt;命令集&#13;&#10;&lt;/h3&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;CMD&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;方向&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;载荷&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;语义&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0x01&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;下行&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;轴号 + 开关&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;抱闸控制&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0x02&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;下行&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;轴号 + rpm(int16)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;速度指令（符号=方向，±3000 限幅）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0x03&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;下行&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;轴号 + rpm + 时长(100ms 单位)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;定时动作（动作时序演示）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0x04&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;下行&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;轴号（0xFF=全轴）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;停止&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0x05&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;下行&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;seq 回显&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;心跳（200ms 周期，不回 ACK）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0x06&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;下行&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;轴号（0xFF=全部）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;故障复位&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0x07&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;下行&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;无&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;立即请求状态帧（回 0x82）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0x81&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;上行&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;回显命令/序号 + status&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;命令应答（OK/BUSY/REJECT/BAD_ARG）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0x82&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;上行&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;状态字×2 + 双轴速度 + 故障码&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;周期状态帧（100ms）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0x8F&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;上行&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;故障码 + 轴号 + 保留&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;故障上报（故障沿触发一次）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="实例帧crc-实算可直接用于联调"&gt;&lt;a href="#%e5%ae%9e%e4%be%8b%e5%b8%a7crc-%e5%ae%9e%e7%ae%97%e5%8f%af%e7%9b%b4%e6%8e%a5%e7%94%a8%e4%ba%8e%e8%81%94%e8%b0%83" class="header-anchor"&gt;&lt;/a&gt;实例帧（CRC 实算，可直接用于联调）&#13;&#10;&lt;/h3&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;帧&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;字节&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;轴1 速度 -500rpm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;A5 5A 01 03 02 01 FE 0C 55 D7&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;轴2 800rpm 转 3.0s&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;A5 5A 02 04 03 02 20 03 1E FD CE&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;心跳&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;A5 5A 03 01 05 5A D2 CB&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;状态帧（READY/MOVING，sp1=1500，fault=0x0001）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;A5 5A 10 08 82 01 02 DC 05 00 00 01 00 97 A8&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;故障帧（驱动器无应答，轴1）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;A5 5A 11 05 8F 02 00 01 00 00 E2 F4&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="解析鲁棒性7-态状态机--三重防护"&gt;&lt;a href="#%e8%a7%a3%e6%9e%90%e9%b2%81%e6%a3%92%e6%80%a77-%e6%80%81%e7%8a%b6%e6%80%81%e6%9c%ba--%e4%b8%89%e9%87%8d%e9%98%b2%e6%8a%a4" class="header-anchor"&gt;&lt;/a&gt;解析鲁棒性（7 态状态机 + 三重防护）&#13;&#10;&lt;/h3&gt;&lt;ol&gt;&#10;&lt;li&gt;&lt;strong&gt;LEN &amp;gt; 64&lt;/strong&gt; → 丢弃回找头（防超长帧打爆缓冲）；&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;帧内字节间隔 &amp;gt; 50ms&lt;/strong&gt; → 强制回找头（防半帧卡死）；&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;CRC 失败&lt;/strong&gt; → 丢当前帧回找头（假同步自愈）；RX 环满 → 丢最新并计数上报统计。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;接收链路硬件侧使用 UART 中断 + DMA 不定长接收，以 IDLE 总线空闲事件判帧：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;__HAL_UART_ENABLE_IT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;huart6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UART_IT_IDLE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 帧长 = 接收缓冲区大小 - __HAL_DMA_GET_COUNTER(&amp;amp;hdma_usart6_rx) */&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;ISR 只做批量搬移入环形缓冲（SPSC 免锁）+ 置线程标志，解析全部在 parseTask 完成——ISR 保持极短是实时性的关键：若在中断里做逐字节处理甚至打印，9600 波特率下一帧即可阻塞几十 ms。&lt;/p&gt;&#10;&lt;h2 id="统一运动控制-api"&gt;&lt;a href="#%e7%bb%9f%e4%b8%80%e8%bf%90%e5%8a%a8%e6%8e%a7%e5%88%b6-api" class="header-anchor"&gt;&lt;/a&gt;统一运动控制 API&#13;&#10;&lt;/h2&gt;&lt;p&gt;对上层屏蔽多电机与 Modbus 细节，全部运动能力收敛为一组 &lt;code&gt;MOTION_*&lt;/code&gt; 接口：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;API&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;语义&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;MOTION_InitAll()&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;伺服参数写入 + 原点捕获 + 松闸（任务上下文）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;MOTION_SetSpeed(ax, rpm)&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;方向=符号；自动先松闸再设速&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;MOTION_MoveTimed(ax, rpm, dur)&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;速度 + 截止期（定时动作）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;MOTION_Stop(ax)&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;清速 → 150ms 减速等待 → 抱闸&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;MOTION_Brake(ax, open)&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;手动抱闸（运动中拒绝）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;MOTION_Tick()&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;定时动作到期判定（100ms 粒度）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;MOTION_PollFeedback()&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;双轴编码器轮询 + 无应答计数 + 软限位&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;MOTION_GetSnapshot(ax, &amp;amp;s)&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;状态/速度/位置/故障原子快照&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;MOTION_SafeShutdown(ax, fault)&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;安全关断唯一入口&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;MOTION_FaultReset(ax)&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;FAULT → READY：重初始化 + 重捕获原点 + 松闸&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;底层由一组寄存器级驱动接口支撑：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;Motor_Init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MOTOR1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 初始化：Modbus 链路建立 + 驱动器寄存器配置&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;openMotor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MOTOR1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 电机使能&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;speed_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MOTOR1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 速度模式设定：负速度转补码写入，±3000rpm 软件限幅&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;closeMotor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MOTOR1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 去使能（配合抱闸继电器安全停机）&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h2 id="modbus-rtu-驱动器接口"&gt;&lt;a href="#modbus-rtu-%e9%a9%b1%e5%8a%a8%e5%99%a8%e6%8e%a5%e5%8f%a3" class="header-anchor"&gt;&lt;/a&gt;Modbus RTU 驱动器接口&#13;&#10;&lt;/h2&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;功能码&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;用途&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0x03&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;读保持寄存器（状态/速度回收）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0x06&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;写单寄存器（速度设定等）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0x10&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;写多寄存器（批量参数下发）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;帧结构：&lt;code&gt;地址(1B) + 功能码(1B) + 数据(NB) + CRC16(2B，低字节在前)&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;CRC16 参数：多项式 &lt;strong&gt;0x8005&lt;/strong&gt;（反射 0xA001）、初值 &lt;strong&gt;0xFFFF&lt;/strong&gt;，实现查表法与逐位法两种等价算法——上位机协议栈的 CRC 直接复用同一张表&lt;/li&gt;&#10;&lt;li&gt;实例帧：&lt;code&gt;01 06 03 03 05 DC&lt;/code&gt; → CRC = &lt;code&gt;7B 47&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;错误应答识别：功能码 + 0x80&lt;/li&gt;&#10;&lt;li&gt;编码器读事务做&lt;strong&gt;功能码 + 长度 + CRC 三重校验&lt;/strong&gt;，连续无应答即触发单轴安全关断&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="异常保护体系"&gt;&lt;a href="#%e5%bc%82%e5%b8%b8%e4%bf%9d%e6%8a%a4%e4%bd%93%e7%b3%bb" class="header-anchor"&gt;&lt;/a&gt;异常保护体系&#13;&#10;&lt;/h2&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;保护&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;触发条件&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;动作&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;心跳丢失&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;500ms 无任何合法帧（首帧后生效）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;全轴 SafeShutdown + 0x8F 上报&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;驱动器失联&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;连续 3 次编码器读无应答/校验失败&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;单轴 SafeShutdown&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;软限位&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;位置超出 ±170°（可配）且运动中&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;单轴 SafeShutdown&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;硬限位&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;EXTI 接口预留（硬件常闭开关已具备）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;后续接入 SafeShutdown 链&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;CRC 错帧&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;校验失败&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;丢帧 + 收发统计&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;IWDG&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;喂狗点已置于 monitorTask（启用为编译开关）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;复位兜底&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;抱闸 failsafe&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;硬件特性：断电即抱死&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;物理兜底&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h2 id="关于伺服三环控制的工程结论"&gt;&lt;a href="#%e5%85%b3%e4%ba%8e%e4%bc%ba%e6%9c%8d%e4%b8%89%e7%8e%af%e6%8e%a7%e5%88%b6%e7%9a%84%e5%b7%a5%e7%a8%8b%e7%bb%93%e8%ae%ba" class="header-anchor"&gt;&lt;/a&gt;关于伺服三环控制的工程结论&#13;&#10;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;电流环/速度环/位置环要不要在 MCU 里做？——不需要，这是伺服系统的标准级联结构&lt;/strong&gt;：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;位置环（软件，按曲线节拍）→ 速度环（驱动器硬件闭环）→ 电流环（驱动器硬件闭环，µs 级带宽）；&lt;/li&gt;&#10;&lt;li&gt;若强行在 MCU 做三环：RS485 半双工往返 + RTOS 任务抖动，内环带宽根本撑不起来，反而劣化性能；&lt;/li&gt;&#10;&lt;li&gt;本项目 MCU 只做&lt;strong&gt;位置外环&lt;/strong&gt;：协议与运动接口预留位置指令扩展，速度/电流环交给禾川驱动器。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;轨迹跟踪业务层的实现方案：双电机按 FLASH 中的 1000 点位置/速度曲线做 S 曲线协同跟踪——位置 PID + 速度前馈 + 双电机交叉耦合补偿，位置环输出直接写驱动器速度寄存器；上位机经 LoRa 链路完成曲线分块下传（块号 ACK 隐含重传）、误差曲线回读、前馈/耦合系数在线整定与点动控制；限位开关走 EXTI 硬件链路。&lt;/p&gt;&#10;&lt;h2 id="已知限制与后续项"&gt;&lt;a href="#%e5%b7%b2%e7%9f%a5%e9%99%90%e5%88%b6%e4%b8%8e%e5%90%8e%e7%bb%ad%e9%a1%b9" class="header-anchor"&gt;&lt;/a&gt;已知限制与后续项&#13;&#10;&lt;/h2&gt;&lt;ol&gt;&#10;&lt;li&gt;驱动器应答等待仍为固定 5ms 盲等，改进方向是信号量 + 超时；&lt;/li&gt;&#10;&lt;li&gt;IWDG 未启用（喂狗点与启用步骤已就位，编译开关打开即用）；&lt;/li&gt;&#10;&lt;li&gt;硬限位 EXTI 尚未接入软件保护链（常闭开关硬件与 EXTI 驱动已具备，接入方案：ISR 直接抱闸 + 通知任务走 SafeShutdown）；&lt;/li&gt;&#10;&lt;li&gt;USART6 波特率 9600，协议帧建议提升到 115200；&lt;/li&gt;&#10;&lt;li&gt;printf 复用上位机串口，正式版应以编译开关关闭或改用独立调试口；&lt;/li&gt;&#10;&lt;li&gt;位置闭环当前为速度模式的接口预留，轨迹跟踪按上文 S 曲线方案扩展。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="项目状态"&gt;&lt;a href="#%e9%a1%b9%e7%9b%ae%e7%8a%b6%e6%80%81" class="header-anchor"&gt;&lt;/a&gt;项目状态&#13;&#10;&lt;/h2&gt;&lt;p&gt;下位机软件已完成「RS485 协议栈 + 统一运动接口 + 四大逻辑任务 + 多级异常保护」的完整架构实现；驱动层经整机联调实测，Modbus 通信、双轴运动控制、上位机链路稳定工作，支撑完成单段焊缝与截面焊缝检测流程。&lt;/p&gt;&#10;</description></item></channel></rss>