body { font-family: 'Microsoft YaHei', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 20px; }
.container { max-width: 1400px; margin: 0 auto; }
.header { text-align: center; color: white; margin-bottom: 30px; animation: fadeInDown 0.8s ease-out; }
.header h1 { font-size: 2.5em; margin-bottom: 10px; text-shadow: 2px 2px 4px rgba(0,0,0,0.2); }
.tabs { display: flex; justify-content: center; gap: 10px; margin-bottom: 30px; animation: fadeIn 1s ease-out; }
.tab-btn { padding: 12px 30px; background: rgba(255, 255, 255, 0.9); border: none; border-radius: 50px; cursor: pointer; font-size: 16px; font-weight: bold; color: #667eea; transition: all 0.3s ease; box-shadow: 0 4px 15px rgba(0,0,0,0.1); }
.tab-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0,0,0,0.15); }
.tab-btn.active { background: white; color: #764ba2; transform: scale(1.05); }
.diagram-container { background: white; border-radius: 20px; padding: 40px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); animation: fadeInUp 0.8s ease-out; }
.tab-content { display: none; }
.tab-content.active { display: block; animation: slideIn 0.5s ease-out; }
svg { width: 100%; height: auto; filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1)); }
.legend { margin-top: 30px; padding: 20px; background: #f8f9fa; border-radius: 10px; border-left: 5px solid #667eea; }
.legend h3 { color: #333; margin-bottom: 15px; }
.legend-item { display: inline-flex; align-items: center; margin-right: 30px; margin-bottom: 10px; }
.legend-color { width: 30px; height: 20px; margin-right: 10px; border-radius: 3px; border: 1px solid #ddd; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes fadeInDown { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } }
@keyframes fadeInUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
@keyframes slideIn { from { opacity: 0; transform: translateX(-20px); } to { opacity: 1; transform: translateX(0); } }
.pulse { animation: pulse 2s infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
.flow { animation: flow 3s infinite; }
@keyframes flow { 0% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: -30; } }</style>🎯 伺服控制系统架构设计
基于STM32的实时伺服控制中间件方案
<div class="tabs"> <button class="tab-btn active" onclick="showTab('overview')">系统总览</button> <button class="tab-btn" onclick="showTab('communication')">通信架构</button> <button class="tab-btn" onclick="showTab('dataflow')">数据流</button> <button class="tab-btn" onclick="showTab('realtime')">实时机制</button> </div>
<div class="diagram-container"> <!-- 系统总览 --> <div id="overview" class="tab-content active"> <svg viewBox="0 0 1200 700"> <!-- 背景网格 --> <defs> <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse"> <path d="M 40 0 L 0 0 0 40" fill="none" stroke="#e0e0e0" stroke-width="1"/> </pattern>
<!-- 渐变定义 --> <linearGradient id="pcGrad" x1="0%" y1="0%" x2="100%" y2="100%"> <stop offset="0%" /> <stop offset="100%" /> </linearGradient>
<linearGradient id="stm32Grad" x1="0%" y1="0%" x2="100%" y2="100%"> <stop offset="0%" /> <stop offset="100%" /> </linearGradient>
<linearGradient id="servoGrad" x1="0%" y1="0%" x2="100%" y2="100%"> <stop offset="0%" /> <stop offset="100%" /> </linearGradient> </defs>
<rect width="1200" height="700" fill="url(#grid)" />
<!-- 上位机 --> <g transform="translate(100, 100)"> <rect x="0" y="0" width="200" height="150" rx="15" fill="url(#pcGrad)" stroke="#4CAF50" stroke-width="3"/> <text x="100" y="40" text-anchor="middle" fill="white" font-size="20" font-weight="bold">上位机</text> <text x="100" y="70" text-anchor="middle" fill="white" font-size="14">控制软件</text> <text x="100" y="90" text-anchor="middle" fill="white" font-size="12">• 路径规划</text> <text x="100" y="110" text-anchor="middle" fill="white" font-size="12">• 状态监控</text> <text x="100" y="130" text-anchor="middle" fill="white" font-size="12">• 参数配置</text> </g>
<!-- STM32中间件 --> <g transform="translate(500, 50)"> <rect x="0" y="0" width="300" height="600" rx="15" fill="url(#stm32Grad)" stroke="#2196F3" stroke-width="3"/> <text x="150" y="30" text-anchor="middle" fill="white" font-size="22" font-weight="bold">STM32 控制器</text>
<!-- 命令处理模块 --> <rect x="20" y="60" width="260" height="80" rx="10" fill="rgba(255,255,255,0.2)" stroke="white" stroke-width="2"/> <text x="150" y="85" text-anchor="middle" fill="white" font-size="14" font-weight="bold">命令处理引擎</text> <text x="150" y="105" text-anchor="middle" fill="white" font-size="12">• 优先级队列管理</text> <text x="150" y="125" text-anchor="middle" fill="white" font-size="12">• 紧急停止处理</text>
<!-- 通信管理 --> <rect x="20" y="160" width="260" height="80" rx="10" fill="rgba(255,255,255,0.2)" stroke="white" stroke-width="2"/> <text x="150" y="185" text-anchor="middle" fill="white" font-size="14" font-weight="bold">通信管理器</text> <text x="150" y="205" text-anchor="middle" fill="white" font-size="12">• RS232 (上位机)</text> <text x="150" y="225" text-anchor="middle" fill="white" font-size="12">• Modbus RTU (伺服)</text>
<!-- 状态缓存 --> <rect x="20" y="260" width="260" height="80" rx="10" fill="rgba(255,255,255,0.2)" stroke="white" stroke-width="2"/> <text x="150" y="285" text-anchor="middle" fill="white" font-size="14" font-weight="bold">状态缓存系统</text> <text x="150" y="305" text-anchor="middle" fill="white" font-size="12">• 10ms定时更新</text> <text x="150" y="325" text-anchor="middle" fill="white" font-size="12">• 实时状态监控</text>
<!-- 安全机制 --> <rect x="20" y="360" width="260" height="80" rx="10" fill="rgba(255,255,255,0.2)" stroke="white" stroke-width="2"/> <text x="150" y="385" text-anchor="middle" fill="white" font-size="14" font-weight="bold">安全保护机制</text> <text x="150" y="405" text-anchor="middle" fill="white" font-size="12">• 看门狗监控</text> <text x="150" y="425" text-anchor="middle" fill="white" font-size="12">• 异常自动处理</text>
<!-- DMA控制 --> <rect x="20" y="460" width="260" height="80" rx="10" fill="rgba(255,255,255,0.2)" stroke="white" stroke-width="2"/> <text x="150" y="485" text-anchor="middle" fill="white" font-size="14" font-weight="bold">DMA传输控制</text> <text x="150" y="505" text-anchor="middle" fill="white" font-size="12">• 零CPU占用传输</text> <text x="150" y="525" text-anchor="middle" fill="white" font-size="12">• 双缓冲机制</text> </g>
<!-- 伺服驱动器 --> <g transform="translate(900, 100)"> <rect x="0" y="0" width="200" height="150" rx="15" fill="url(#servoGrad)" stroke="#FF9800" stroke-width="3"/> <text x="100" y="40" text-anchor="middle" fill="white" font-size="20" font-weight="bold">伺服驱动器</text> <text x="100" y="70" text-anchor="middle" fill="white" font-size="14">Modbus从站</text> <text x="100" y="90" text-anchor="middle" fill="white" font-size="12">• 位置控制</text> <text x="100" y="110" text-anchor="middle" fill="white" font-size="12">• 速度控制</text> <text x="100" y="130" text-anchor="middle" fill="white" font-size="12">• 状态反馈</text> </g>
<!-- 连接线 --> <!-- RS232连接 --> <g> <path d="M 300 175 L 500 175" stroke="#4CAF50" stroke-width="4" fill="none" stroke-dasharray="10,5" class="flow"/> <text x="400" y="165" text-anchor="middle" fill="#4CAF50" font-size="14" font-weight="bold">RS232</text> <text x="400" y="195" text-anchor="middle" fill="#666" font-size="12">115200bps</text> </g>
<!-- Modbus连接 --> <g> <path d="M 800 175 L 900 175" stroke="#FF9800" stroke-width="4" fill="none" stroke-dasharray="10,5" class="flow"/> <text x="850" y="165" text-anchor="middle" fill="#FF9800" font-size="14" font-weight="bold">Modbus RTU</text> <text x="850" y="195" text-anchor="middle" fill="#666" font-size="12">19200bps</text> </g>
<!-- 关键特性标注 --> <g transform="translate(100, 400)"> <rect x="0" y="0" width="1000" height="200" rx="15" fill="rgba(255,255,255,0.95)" stroke="#ddd" stroke-width="2"/> <text x="500" y="30" text-anchor="middle" fill="#333" font-size="18" font-weight="bold">🚀 系统关键特性</text>
<g transform="translate(50, 60)"> <circle cx="10" cy="10" r="5" fill="#4CAF50"/> <text x="25" y="15" fill="#333" font-size="14">响应时间 < 5ms</text> </g>
<g transform="translate(250, 60)"> <circle cx="10" cy="10" r="5" fill="#2196F3"/> <text x="25" y="15" fill="#333" font-size="14">双队列优先级调度</text> </g>
<g transform="translate(480, 60)"> <circle cx="10" cy="10" r="5" fill="#FF9800"/> <text x="25" y="15" fill="#333" font-size="14">DMA零拷贝传输</text> </g>
<g transform="translate(700, 60)"> <circle cx="10" cy="10" r="5" fill="#9C27B0"/> <text x="25" y="15" fill="#333" font-size="14">自动故障恢复</text> </g>
<g transform="translate(50, 100)"> <circle cx="10" cy="10" r="5" fill="#E91E63"/> <text x="25" y="15" fill="#333" font-size="14">状态缓存10ms更新</text> </g>
<g transform="translate(250, 100)"> <circle cx="10" cy="10" r="5" fill="#00BCD4"/> <text x="25" y="15" fill="#333" font-size="14">CRC16数据校验</text> </g>
<g transform="translate(480, 100)"> <circle cx="10" cy="10" r="5" fill="#795548"/> <text x="25" y="15" fill="#333" font-size="14">紧急停止0延迟</text> </g>
<g transform="translate(700, 100)"> <circle cx="10" cy="10" r="5" fill="#607D8B"/> <text x="25" y="15" fill="#333" font-size="14">报警主动上报</text> </g>
<text x="50" y="160" fill="#666" font-size="12">• 支持最多32条命令缓冲</text> <text x="350" y="160" fill="#666" font-size="12">• 批量寄存器读写优化</text> <text x="650" y="160" fill="#666" font-size="12">• 看门狗超时保护</text> </g> </svg> </div>
<!-- 通信架构 --> <div id="communication" class="tab-content"> <svg viewBox="0 0 1200 800"> <defs> <marker id="arrow" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth"> <path d="M0,0 L0,6 L9,3 z" fill="#333" /> </marker> </defs>
<!-- OSI模型层次 --> <text x="600" y="40" text-anchor="middle" fill="#333" font-size="24" font-weight="bold">通信协议栈架构</text>
<!-- 应用层 --> <g transform="translate(100, 80)"> <rect x="0" y="0" width="1000" height="80" rx="10" fill="#4CAF50" opacity="0.8"/> <text x="50" y="45" fill="white" font-size="18" font-weight="bold">应用层</text>
<rect x="200" y="15" width="180" height="50" rx="5" fill="white"/> <text x="290" y="35" text-anchor="middle" fill="#333" font-size="14">上位机控制软件</text> <text x="290" y="55" text-anchor="middle" fill="#666" font-size="12">命令生成/响应处理</text>
<rect x="450" y="15" width="180" height="50" rx="5" fill="white"/> <text x="540" y="35" text-anchor="middle" fill="#333" font-size="14">STM32业务逻辑</text> <text x="540" y="55" text-anchor="middle" fill="#666" font-size="12">命令解析/执行</text>
<rect x="700" y="15" width="180" height="50" rx="5" fill="white"/> <text x="790" y="35" text-anchor="middle" fill="#333" font-size="14">伺服控制逻辑</text> <text x="790" y="55" text-anchor="middle" fill="#666" font-size="12">运动控制算法</text> </g>
<!-- 协议层 --> <g transform="translate(100, 180)"> <rect x="0" y="0" width="1000" height="80" rx="10" fill="#2196F3" opacity="0.8"/> <text x="50" y="45" fill="white" font-size="18" font-weight="bold">协议层</text>
<rect x="200" y="15" width="180" height="50" rx="5" fill="white"/> <text x="290" y="35" text-anchor="middle" fill="#333" font-size="14">自定义协议</text> <text x="290" y="55" text-anchor="middle" fill="#666" font-size="12">Header+CMD+Data+CRC</text>
<rect x="450" y="15" width="180" height="50" rx="5" fill="white"/> <text x="540" y="35" text-anchor="middle" fill="#333" font-size="14">协议转换器</text> <text x="540" y="55" text-anchor="middle" fill="#666" font-size="12">双向协议转换</text>
<rect x="700" y="15" width="180" height="50" rx="5" fill="white"/> <text x="790" y="35" text-anchor="middle" fill="#333" font-size="14">Modbus协议</text> <text x="790" y="55" text-anchor="middle" fill="#666" font-size="12">功能码03/06/10</text> </g>
<!-- 传输层 --> <g transform="translate(100, 280)"> <rect x="0" y="0" width="1000" height="80" rx="10" fill="#FF9800" opacity="0.8"/> <text x="50" y="45" fill="white" font-size="18" font-weight="bold">传输层</text>
<rect x="200" y="15" width="180" height="50" rx="5" fill="white"/> <text x="290" y="35" text-anchor="middle" fill="#333" font-size="14">串口缓冲管理</text> <text x="290" y="55" text-anchor="middle" fill="#666" font-size="12">环形缓冲区</text>
<rect x="450" y="15" width="180" height="50" rx="5" fill="white"/> <text x="540" y="35" text-anchor="middle" fill="#333" font-size="14">DMA控制器</text> <text x="540" y="55" text-anchor="middle" fill="#666" font-size="12">双缓冲/中断管理</text>
<rect x="700" y="15" width="180" height="50" rx="5" fill="white"/> <text x="790" y="35" text-anchor="middle" fill="#333" font-size="14">RTU帧管理</text> <text x="790" y="55" text-anchor="middle" fill="#666" font-size="12">3.5字符超时检测</text> </g>
<!-- 物理层 --> <g transform="translate(100, 380)"> <rect x="0" y="0" width="1000" height="80" rx="10" fill="#9C27B0" opacity="0.8"/> <text x="50" y="45" fill="white" font-size="18" font-weight="bold">物理层</text>
<rect x="200" y="15" width="180" height="50" rx="5" fill="white"/> <text x="290" y="35" text-anchor="middle" fill="#333" font-size="14">RS232接口</text> <text x="290" y="55" text-anchor="middle" fill="#666" font-size="12">115200bps 8N1</text>
<rect x="450" y="15" width="180" height="50" rx="5" fill="white"/> <text x="540" y="35" text-anchor="middle" fill="#333" font-size="14">UART1 + UART2</text> <text x="540" y="55" text-anchor="middle" fill="#666" font-size="12">硬件流控可选</text>
<rect x="700" y="15" width="180" height="50" rx="5" fill="white"/> <text x="790" y="35" text-anchor="middle" fill="#333" font-size="14">RS485接口</text> <text x="790" y="55" text-anchor="middle" fill="#666" font-size="12">19200bps RTU</text> </g>
<!-- 数据包格式展示 --> <g transform="translate(100, 500)"> <rect x="0" y="0" width="1000" height="250" rx="15" fill="#f5f5f5" stroke="#ddd" stroke-width="2"/> <text x="500" y="30" text-anchor="middle" fill="#333" font-size="18" font-weight="bold">数据包格式详解</text>
<!-- 上位机协议包 --> <text x="50" y="70" fill="#333" font-size="14" font-weight="bold">上位机→STM32 命令包:</text> <g transform="translate(50, 80)"> <rect x="0" y="0" width="80" height="40" fill="#4CAF50" stroke="#333"/> <text x="40" y="25" text-anchor="middle" fill="white" font-size="12">Header</text> <text x="40" y="55" text-anchor="middle" fill="#666" font-size="10">0xAA55</text>
<rect x="80" y="0" width="60" height="40" fill="#2196F3" stroke="#333"/> <text x="110" y="25" text-anchor="middle" fill="white" font-size="12">CMD</text> <text x="110" y="55" text-anchor="middle" fill="#666" font-size="10">1 Byte</text>
<rect x="140" y="0" width="60" height="40" fill="#FF9800" stroke="#333"/> <text x="170" y="25" text-anchor="middle" fill="white" font-size="12">Priority</text> <text x="170" y="55" text-anchor="middle" fill="#666" font-size="10">1 Byte</text>
<rect x="200" y="0" width="80" height="40" fill="#9C27B0" stroke="#333"/> <text x="240" y="25" text-anchor="middle" fill="white" font-size="12">Sequence</text> <text x="240" y="55" text-anchor="middle" fill="#666" font-size="10">2 Bytes</text>
<rect x="280" y="0" width="200" height="40" fill="#607D8B" stroke="#333"/> <text x="380" y="25" text-anchor="middle" fill="white" font-size="12">Data[0-16]</text> <text x="380" y="55" text-anchor="middle" fill="#666" font-size="10">0-32 Bytes</text>
<rect x="480" y="0" width="80" height="40" fill="#E91E63" stroke="#333"/> <text x="520" y="25" text-anchor="middle" fill="white" font-size="12">CRC16</text> <text x="520" y="55" text-anchor="middle" fill="#666" font-size="10">2 Bytes</text> </g>
<!-- Modbus协议包 --> <text x="50" y="160" fill="#333" font-size="14" font-weight="bold">STM32→伺服 Modbus包:</text> <g transform="translate(50, 170)"> <rect x="0" y="0" width="80" height="40" fill="#FF5722" stroke="#333"/> <text x="40" y="25" text-anchor="middle" fill="white" font-size="12">Address</text> <text x="40" y="55" text-anchor="middle" fill="#666" font-size="10">1 Byte</text>
<rect x="80" y="0" width="80" height="40" fill="#3F51B5" stroke="#333"/> <text x="120" y="25" text-anchor="middle" fill="white" font-size="12">Function</text> <text x="120" y="55" text-anchor="middle" fill="#666" font-size="10">1 Byte</text>
<rect x="160" y="0" width="100" height="40" fill="#009688" stroke="#333"/> <text x="210" y="25" text-anchor="middle" fill="white" font-size="12">Reg Addr</text> <text x="210" y="55" text-anchor="middle" fill="#666" font-size="10">2 Bytes</text>
<rect x="260" y="0" width="100" height="40" fill="#CDDC39" stroke="#333"/> <text x="310" y="25" text-anchor="middle" fill="black" font-size="12">Reg Count</text> <text x="310" y="55" text-anchor="middle" fill="#666" font-size="10">2 Bytes</text>
<rect x="360" y="0" width="120" height="40" fill="#795548" stroke="#333"/> <text x="420" y="25" text-anchor="middle" fill="white" font-size="12">Data</text> <text x="420" y="55" text-anchor="middle" fill="#666" font-size="10">N Bytes</text>
<rect x="480" y="0" width="80" height="40" fill="#FF9800" stroke="#333"/> <text x="520" y="25" text-anchor="middle" fill="white" font-size="12">CRC16</text> <text x="520" y="55" text-anchor="middle" fill="#666" font-size="10">2 Bytes</text> </g> </g> </svg> </div>
<!-- 数据流 --> <div id="dataflow" class="tab-content"> <svg viewBox="0 0 1200 900"> <defs> <marker id="arrow2" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth"> <path d="M0,0 L0,6 L9,3 z" fill="#2196F3" /> </marker> </defs>
<text x="600" y="40" text-anchor="middle" fill="#333" font-size="24" font-weight="bold">系统数据流向图</text>
<!-- 正常命令流 --> <g transform="translate(50, 80)"> <rect x="0" y="0" width="500" height="350" rx="10" fill="#E3F2FD" stroke="#2196F3" stroke-width="2"/> <text x="250" y="30" text-anchor="middle" fill="#1976D2" font-size="18" font-weight="bold">正常命令流</text>
<!-- 流程步骤 --> <rect x="30" y="50" width="150" height="40" rx="5" fill="#4CAF50"/> <text x="105" y="75" text-anchor="middle" fill="white" font-size="14">1. 上位机发送命令</text>
<path d="M 105 90 L 105 120" stroke="#333" stroke-width="2" marker-end="url(#arrow2)"/>
<rect x="30" y="130" width="150" height="40" rx="5" fill="#2196F3"/> <text x="105" y="155" text-anchor="middle" fill="white" font-size="14">2. RS232接收中断</text>
<path d="M 105 170 L 105 200" stroke="#333" stroke-width="2" marker-end="url(#arrow2)"/>
<rect x="30" y="210" width="150" height="40" rx="5" fill="#FF9800"/> <text x="105" y="235" text-anchor="middle" fill="white" font-size="14">3. 命令入队(优先级)</text>
<path d="M 180 230 L 250 230" stroke="#333" stroke-width="2" marker-end="url(#arrow2)"/>
<rect x="250" y="210" width="150" height="40" rx="5" fill="#9C27B0"/> <text x="325" y="235" text-anchor="middle" fill="white" font-size="14">4. 命令解析执行</text>
<path d="M 325 250 L 325 280" stroke="#333" stroke-width="2" marker-end="url(#arrow2)"/>
<rect x="250" y="290" width="150" height="40" rx="5" fill="#E91E63"/> <text x="325" y="315" text-anchor="middle" fill="white" font-size="14">5. Modbus请求</text>
<path d="M 325 330 L 325 360" stroke="#333" stroke-width="2" marker-end="url(#arrow2)"/>
<rect x="250" y="370" width="150" height="40" rx="5" fill="#00BCD4"/> <text x="325" y="395" text-anchor="middle" fill="white" font-size="14">6. 响应返回上位机</text> </g>
<!-- 紧急停止流 --> <g transform="translate(600, 80)"> <rect x="0" y="0" width="500" height="350" rx="10" fill="#FFEBEE" stroke="#F44336" stroke-width="2"/> <text x="250" y="30" text-anchor="middle" fill="#D32F2F" font-size="18" font-weight="bold">紧急停止流</text>
<rect x="150" y="50" width="200" height="40" rx="5" fill="#F44336"/> <text x="250" y="75" text-anchor="middle" fill="white" font-size="14">紧急停止命令(Priority=0)</text>
<path d="M 250 90 L 250 120" stroke="#F44336" stroke-width="3" marker-end="url(#arrow2)"/>
<rect x="150" y="130" width="200" height="40" rx="5" fill="#FF5722"/> <text x="250" y="155" text-anchor="middle" fill="white" font-size="14">直接执行(跳过队列)</text>
<path d="M 250 170 L 250 200" stroke="#F44336" stroke-width="3" marker-end="url(#arrow2)"/>
<rect x="150" y="210" width="200" height="40" rx="5" fill="#FF7043"/> <text x="250" y="235" text-anchor="middle" fill="white" font-size="14">立即发送停止命令到伺服</text>
<path d="M 250 250 L 250 280" stroke="#F44336" stroke-width="3" marker-end="url(#arrow2)"/>
<rect x="150" y="290" width="200" height="40" rx="5" fill="#FF8A65"/> <text x="250" y="315" text-anchor="middle" fill="white" font-size="14">清空所有待执行队列</text>
<text x="250" y="370" text-anchor="middle" fill="#F44336" font-size="16" font-weight="bold">⚡ 总耗时 < 1ms</text> </g>
<!-- 状态更新流 --> <g transform="translate(50, 480)"> <rect x="0" y="0" width="1100" height="350" rx="10" fill="#F3E5F5" stroke="#9C27B0" stroke-width="2"/> <text x="550" y="30" text-anchor="middle" fill="#7B1FA2" font-size="18" font-weight="bold">周期性状态更新流 (10ms Timer)</text>
<!-- 轮询序列 --> <g transform="translate(50, 60)"> <rect x="0" y="0" width="120" height="60" rx="5" fill="#9C27B0"/> <text x="60" y="25" text-anchor="middle" fill="white" font-size="12">T=0ms</text> <text x="60" y="45" text-anchor="middle" fill="white" font-size="12">读状态字</text>
<path d="M 120 30 L 180 30" stroke="#9C27B0" stroke-width="2" marker-end="url(#arrow2)"/>
<rect x="180" y="0" width="120" height="60" rx="5" fill="#AB47BC"/> <text x="240" y="25" text-anchor="middle" fill="white" font-size="12">T=10ms</text> <text x="240" y="45" text-anchor="middle" fill="white" font-size="12">读报警代码</text>
<path d="M 300 30 L 360 30" stroke="#9C27B0" stroke-width="2" marker-end="url(#arrow2)"/>
<rect x="360" y="0" width="120" height="60" rx="5" fill="#BA68C8"/> <text x="420" y="25" text-anchor="middle" fill="white" font-size="12">T=20ms</text> <text x="420" y="45" text-anchor="middle" fill="white" font-size="12">读实际位置</text>
<path d="M 480 30 L 540 30" stroke="#9C27B0" stroke-width="2" marker-end="url(#arrow2)"/>
<rect x="540" y="0" width="120" height="60" rx="5" fill="#CE93D8"/> <text x="600" y="25" text-anchor="middle" fill="white" font-size="12">T=30ms</text> <text x="600" y="45" text-anchor="middle" fill="white" font-size="12">读实际速度</text>
<path d="M 660 30 L 720 30" stroke="#9C27B0" stroke-width="2" marker-end="url(#arrow2)"/>
<rect x="720" y="0" width="120" height="60" rx="5" fill="#E1BEE7"/> <text x="780" y="25" text-anchor="middle" fill="#333" font-size="12">T=40ms</text> <text x="780" y="45" text-anchor="middle" fill="#333" font-size="12">循环重启</text> </g>
<!-- 缓存更新 --> <g transform="translate(200, 150)"> <rect x="0" y="0" width="700" height="100" rx="10" fill="white" stroke="#9C27B0" stroke-width="2"/> <text x="350" y="30" text-anchor="middle" fill="#9C27B0" font-size="16" font-weight="bold">状态缓存区</text>
<g transform="translate(50, 40)"> <rect x="0" y="0" width="120" height="40" rx="5" fill="#E8EAF6"/> <text x="60" y="25" text-anchor="middle" fill="#333" font-size="12">status_word</text>
<rect x="140" y="0" width="120" height="40" rx="5" fill="#E8EAF6"/> <text x="200" y="25" text-anchor="middle" fill="#333" font-size="12">alarm_code</text>
<rect x="280" y="0" width="120" height="40" rx="5" fill="#E8EAF6"/> <text x="340" y="25" text-anchor="middle" fill="#333" font-size="12">actual_position</text>
<rect x="420" y="0" width="120" height="40" rx="5" fill="#E8EAF6"/> <text x="480" y="25" text-anchor="middle" fill="#333" font-size="12">actual_speed</text> </g> </g>
<!-- 报警通知 --> <g transform="translate(350, 270)"> <rect x="0" y="0" width="400" height="50" rx="25" fill="#FF5722" class="pulse"/> <text x="200" y="32" text-anchor="middle" fill="white" font-size="14" font-weight="bold"> ⚠️ 检测到报警时,立即推送通知给上位机 </text> </g> </g> </svg> </div>
<!-- 实时机制 --> <div id="realtime" class="tab-content"> <svg viewBox="0 0 1200 900"> <text x="600" y="40" text-anchor="middle" fill="#333" font-size="24" font-weight="bold">实时性保证机制</text>
<!-- 中断优先级 --> <g transform="translate(50, 80)"> <rect x="0" y="0" width="500" height="300" rx="10" fill="#E8F5E9" stroke="#4CAF50" stroke-width="2"/> <text x="250" y="30" text-anchor="middle" fill="#2E7D32" font-size="18" font-weight="bold">中断优先级配置</text>
<g transform="translate(30, 60)"> <rect x="0" y="0" width="440" height="40" rx="5" fill="#F44336"/> <text x="20" y="25" fill="white" font-size="14">优先级0: 紧急停止外部中断 (EXTI)</text> <text x="420" y="25" text-anchor="end" fill="white" font-size="12">最高</text> </g>
<g transform="translate(30, 110)"> <rect x="0" y="0" width="400" height="40" rx="5" fill="#FF9800"/> <text x="20" y="25" fill="white" font-size="14">优先级1: UART1 RX DMA (上位机命令)</text> </g>
<g transform="translate(30, 160)"> <rect x="0" y="0" width="360" height="40" rx="5" fill="#2196F3"/> <text x="20" y="25" fill="white" font-size="14">优先级2: UART2 RX DMA (Modbus响应)</text> </g>
<g transform="translate(30, 210)"> <rect x="0" y="0" width="320" height="40" rx="5" fill="#9C27B0"/> <text x="20" y="25" fill="white" font-size="14">优先级3: TIM3 定时器 (状态更新)</text> <text x="300" y="25" text-anchor="end" fill="white" font-size="12">最低</text> </g> </g>
<!-- DMA配置 --> <g transform="translate(600, 80)"> <rect x="0" y="0" width="500" height="300" rx="10" fill="#E1F5FE" stroke="#03A9F4" stroke-width="2"/> <text x="250" y="30" text-anchor="middle" fill="#0277BD" font-size="18" font-weight="bold">DMA优化配置</text>
<g transform="translate(50, 60)"> <rect x="0" y="0" width="400" height="60" rx="5" fill="white" stroke="#03A9F4"/> <text x="200" y="25" text-anchor="middle" fill="#333" font-size="14" font-weight="bold">UART1 RX DMA (通道4)</text> <text x="200" y="45" text-anchor="middle" fill="#666" font-size="12">• 循环模式 • 半传输/全传输中断</text> </g>
<g transform="translate(50, 130)"> <rect x="0" y="0" width="400" height="60" rx="5" fill="white" stroke="#03A9F4"/> <text x="200" y="25" text-anchor="middle" fill="#333" font-size="14" font-weight="bold">UART2 TX/RX DMA (通道5/6)</text> <text x="200" y="45" text-anchor="middle" fill="#666" font-size="12">• 正常模式 • 传输完成中断</text> </g>
<g transform="translate(50, 200)"> <rect x="0" y="0" width="400" height="60" rx="5" fill="white" stroke="#03A9F4"/> <text x="200" y="25" text-anchor="middle" fill="#333" font-size="14" font-weight="bold">双缓冲机制</text> <text x="200" y="45" text-anchor="middle" fill="#666" font-size="12">• 乒乓缓冲 • 零拷贝切换</text> </g> </g>
<!-- 时序图 --> <g transform="translate(50, 420)"> <rect x="0" y="0" width="1100" height="400" rx="10" fill="#FFF3E0" stroke="#FF9800" stroke-width="2"/> <text x="550" y="30" text-anchor="middle" fill="#E65100" font-size="18" font-weight="bold">典型命令执行时序</text>
<!-- 时间轴 --> <line x1="100" y1="100" x2="1000" y2="100" stroke="#333" stroke-width="2"/>
<!-- 时间刻度 --> <g transform="translate(100, 100)"> <circle cx="0" cy="0" r="5" fill="#333"/> <text x="0" y="-10" text-anchor="middle" fill="#333" font-size="12">0ms</text> <text x="0" y="25" text-anchor="middle" fill="#666" font-size="11">命令接收</text>
<circle cx="150" cy="0" r="5" fill="#4CAF50"/> <text x="150" y="-10" text-anchor="middle" fill="#333" font-size="12">0.1ms</text> <text x="150" y="25" text-anchor="middle" fill="#666" font-size="11">DMA传输</text> <rect x="130" y="40" width="40" height="30" rx="5" fill="#4CAF50" opacity="0.3"/>
<circle cx="300" cy="0" r="5" fill="#2196F3"/> <text x="300" y="-10" text-anchor="middle" fill="#333" font-size="12">0.5ms</text> <text x="300" y="25" text-anchor="middle" fill="#666" font-size="11">命令解析</text> <rect x="280" y="40" width="40" height="30" rx="5" fill="#2196F3" opacity="0.3"/>
<circle cx="450" cy="0" r="5" fill="#FF9800"/> <text x="450" y="-10" text-anchor="middle" fill="#333" font-size="12">1ms</text> <text x="450" y="25" text-anchor="middle" fill="#666" font-size="11">Modbus发送</text> <rect x="430" y="40" width="40" height="30" rx="5" fill="#FF9800" opacity="0.3"/>
<circle cx="600" cy="0" r="5" fill="#9C27B0"/> <text x="600" y="-10" text-anchor="middle" fill="#333" font-size="12">3ms</text> <text x="600" y="25" text-anchor="middle" fill="#666" font-size="11">伺服响应</text> <rect x="580" y="40" width="40" height="30" rx="5" fill="#9C27B0" opacity="0.3"/>
<circle cx="750" cy="0" r="5" fill="#E91E63"/> <text x="750" y="-10" text-anchor="middle" fill="#333" font-size="12">3.5ms</text> <text x="750" y="25" text-anchor="middle" fill="#666" font-size="11">结果处理</text> <rect x="730" y="40" width="40" height="30" rx="5" fill="#E91E63" opacity="0.3"/>
<circle cx="900" cy="0" r="5" fill="#00BCD4"/> <text x="900" y="-10" text-anchor="middle" fill="#333" font-size="12">4ms</text> <text x="900" y="25" text-anchor="middle" fill="#666" font-size="11">响应上位机</text> <rect x="880" y="40" width="40" height="30" rx="5" fill="#00BCD4" opacity="0.3"/> </g>
<!-- 性能指标 --> <g transform="translate(100, 250)"> <rect x="0" y="0" width="900" height="120" rx="10" fill="white" stroke="#FF9800"/> <text x="450" y="30" text-anchor="middle" fill="#FF9800" font-size="16" font-weight="bold">🎯 关键性能指标</text>
<g transform="translate(50, 50)"> <text x="0" y="20" fill="#333" font-size="14">• 命令响应延迟: <5ms (典型值4ms)</text> <text x="0" y="45" fill="#333" font-size="14">• 紧急停止延迟: <1ms (硬件中断)</text> <text x="0" y="70" fill="#333" font-size="14">• 状态更新周期: 10ms (可调)</text>
<text x="400" y="20" fill="#333" font-size="14">• DMA传输效率: 100% (零CPU占用)</text> <text x="400" y="45" fill="#333" font-size="14">• 命令吞吐量: >200条/秒</text> <text x="400" y="70" fill="#333" font-size="14">• 通信成功率: >99.95%</text> </g> </g> </g> </svg> </div> </div>
<div class="legend"> <h3>📋 系统说明</h3> <div class="legend-item"> <div class="legend-color"></div> 上位机相关 </div> <div class="legend-item"> <div class="legend-color"></div> STM32处理 </div> <div class="legend-item"> <div class="legend-color"></div> 伺服通信 </div> <div class="legend-item"> <div class="legend-color"></div> 实时机制 </div> </div></div>
<script> function showTab(tabName) { // 隐藏所有标签页 const tabs = document.querySelectorAll('.tab-content'); tabs.forEach(tab => { tab.classList.remove('active'); });
// 显示选中的标签页 document.getElementById(tabName).classList.add('active');
// 更新按钮状态 const btns = document.querySelectorAll('.tab-btn'); btns.forEach(btn => { btn.classList.remove('active'); }); event.target.classList.add('active'); }
// 添加动画效果 document.addEventListener('DOMContentLoaded', function() { // SVG元素渐入效果 const svgElements = document.querySelectorAll('svg rect, svg circle, svg text'); svgElements.forEach((element, index) => { element.style.opacity = '0'; element.style.animation = `fadeIn 0.5s ease-out ${index * 0.01}s forwards`; }); });</script>根据你的需求,有几种方案可以实现上位机通过单片机控制伺服驱动器。我推荐Modbus透传方案,这是最优雅和实用的解决方案:
推荐方案:Modbus透传 + 仲裁控制
跳转到“推荐方案:Modbus透传 + 仲裁控制”架构设计
跳转到“架构设计”上位机 <--RS232--> STM32 <--Modbus RTU--> 伺服驱动器实现策略
跳转到“实现策略”1. 透传模式
跳转到“1. 透传模式”单片机作为Modbus网关,将上位机的Modbus命令透传给伺服驱动器:
// HAL实现示例typedef enum { CONTROL_MODE_LOCAL, // 单片机控制 CONTROL_MODE_REMOTE, // 上位机控制 CONTROL_MODE_MIXED // 混合控制} control_mode_t;
typedef struct { control_mode_t mode; uint32_t last_pc_command_time; // 上位机最后命令时间 uint8_t pc_timeout_ms; // 超时时间} modbus_gateway_t;
// 透传处理函数void modbus_gateway_process(void) { uint8_t rs232_buffer[256]; uint8_t modbus_response[256];
// 接收上位机命令 if (HAL_UART_Receive(&huart_rs232, rs232_buffer, sizeof(rs232_buffer), 10) == HAL_OK) { gateway.last_pc_command_time = HAL_GetTick(); gateway.mode = CONTROL_MODE_REMOTE;
// 验证并转发Modbus命令 if (validate_modbus_frame(rs232_buffer)) { // 发送到伺服驱动器 HAL_UART_Transmit(&huart_modbus, rs232_buffer, get_frame_length(rs232_buffer), 100);
// 接收伺服响应 if (HAL_UART_Receive(&huart_modbus, modbus_response, sizeof(modbus_response), 100) == HAL_OK) { // 回传给上位机 HAL_UART_Transmit(&huart_rs232, modbus_response, get_frame_length(modbus_response), 100); } } }
// 检查上位机通信超时 if (HAL_GetTick() - gateway.last_pc_command_time > gateway.pc_timeout_ms) { gateway.mode = CONTROL_MODE_LOCAL; }}2. 仲裁控制机制
跳转到“2. 仲裁控制机制”typedef struct { uint16_t address; uint16_t local_value; uint16_t remote_value; uint8_t priority; // 0=local, 1=remote} register_control_t;
// 关键寄存器仲裁register_control_t critical_registers[] = { {0x60E, 0, 0, 0}, // 使能信号 - 本地优先 {0x61A, 0, 0, 1}, // G-初始角速度 - 远程优先 {0x626, 0, 0, 1}, // C-电子位置 - 远程优先};
void arbitrate_control(void) { for (int i = 0; i < sizeof(critical_registers)/sizeof(register_control_t); i++) { if (gateway.mode == CONTROL_MODE_REMOTE && critical_registers[i].priority == 1) { // 使用远程值 write_servo_register(critical_registers[i].address, critical_registers[i].remote_value); } else { // 使用本地值 write_servo_register(critical_registers[i].address, critical_registers[i].local_value); } }}3. 安全保护机制
跳转到“3. 安全保护机制”// 安全检查函数HAL_StatusTypeDef validate_modbus_command(uint8_t* frame) { uint16_t address = (frame[2] << 8) | frame[3]; uint16_t value = (frame[4] << 8) | frame[5];
// 检查关键安全参数 switch(address) { case 0x60E: // 使能控制 if (get_emergency_stop_status()) { return HAL_ERROR; // 急停状态禁止使能 } break;
case 0x61A: // 速度控制 if (value > MAX_SAFE_SPEED) { return HAL_ERROR; // 超速保护 } break;
case 0x626: // 位置控制 if (value < MIN_POSITION || value > MAX_POSITION) { return HAL_ERROR; // 位置限制 } break; }
return HAL_OK;}实现要点
跳转到“实现要点”4. UART配置
跳转到“4. UART配置”// RS232配置 (与上位机通信)huart1.Init.BaudRate = 115200;huart1.Init.WordLength = UART_WORDLENGTH_8B;huart1.Init.StopBits = UART_STOPBITS_1;huart1.Init.Parity = UART_PARITY_NONE;
// Modbus RTU配置 (与伺服通信)huart2.Init.BaudRate = 9600; // 根据伺服设定huart2.Init.WordLength = UART_WORDLENGTH_8B;huart2.Init.StopBits = UART_STOPBITS_1;huart2.Init.Parity = UART_PARITY_EVEN; // Modbus常用偶校验优势
跳转到“优势”- 标准化:上位机可以直接使用标准Modbus协议
- 灵活性:支持本地/远程/混合控制模式
- 安全性:单片机可以实施安全检查和保护
- 监控能力:单片机可以监控所有通信
- 容错性:上位机断线后自动切换到本地控制
替代方案
跳转到“替代方案”如果需要更复杂的控制,也可以考虑:
- 自定义协议:上位机发送高级命令,单片机解析后控制伺服
- 双通道:添加第二个通信接口让上位机直接连接伺服(需要硬件支持)
#ifndef SERVO_CONTROL_H#define SERVO_CONTROL_H
#include "main.h"#include "modbus.h"
// 伺服驱动器寄存器地址定义(根据您的表格)#define REG_CONTROL_WORD 0x6006 // P6-07 控制字#define REG_STATUS_WORD 0x6008 // P6-09 状态字#define REG_TARGET_POSITION 0x60A // P6-11 目标位置#define REG_TARGET_SPEED 0x60E // P6-15 目标速度#define REG_ACCELERATION 0x612 // P6-19 加速度#define REG_DECELERATION 0x614 // P6-21 减速度#define REG_ALARM_CODE 0x622 // P6-35 报警代码#define REG_ACTUAL_POSITION 0x63A // P6-59 实际位置#define REG_ACTUAL_SPEED 0x63C // P6-61 实际速度
// 上位机命令类型typedef enum { CMD_EMERGENCY_STOP = 0x01, // 紧急停止(最高优先级) CMD_START_STOP = 0x02, // 启停控制 CMD_SET_POSITION = 0x03, // 位置设定 CMD_SET_SPEED = 0x04, // 速度设定 CMD_SET_ACCEL = 0x05, // 加减速设定 CMD_READ_STATUS = 0x06, // 读取状态 CMD_READ_ALARM = 0x07, // 读取报警 CMD_CLEAR_ALARM = 0x08, // 清除报警 CMD_READ_ACTUAL_POS = 0x09, // 读取实际位置 CMD_READ_ACTUAL_SPD = 0x0A, // 读取实际速度 CMD_BATCH_READ = 0x0B, // 批量读取 CMD_DIRECT_MODBUS = 0xFF // 直接透传Modbus命令} CommandType;
// 命令包结构typedef struct { uint8_t header[2]; // 0xAA, 0x55 uint8_t cmd_type; // 命令类型 uint8_t priority; // 优先级 (0-最高, 255-最低) uint16_t sequence; // 序列号 uint16_t reg_addr; // 寄存器地址 uint16_t reg_count; // 寄存器数量 uint16_t data[16]; // 数据区(最多16个字) uint16_t crc16; // CRC校验} ServoCommand;
// 响应包结构typedef struct { uint8_t header[2]; // 0xBB, 0x66 uint8_t cmd_type; // 命令类型(回显) uint8_t status; // 执行状态 uint16_t sequence; // 序列号(回显) uint16_t data_len; // 数据长度 uint16_t data[32]; // 返回数据 uint16_t crc16; // CRC校验} ServoResponse;
// 伺服状态缓存结构typedef struct { uint16_t status_word; uint16_t alarm_code; int32_t actual_position; int16_t actual_speed; uint16_t control_word; uint32_t last_update_tick; uint8_t is_valid;} ServoStatusCache;
// 命令队列结构#define CMD_QUEUE_SIZE 32typedef struct { ServoCommand commands[CMD_QUEUE_SIZE]; uint8_t head; uint8_t tail; uint8_t count;} CommandQueue;
// 函数声明void Servo_Init(void);HAL_StatusTypeDef Servo_ProcessCommand(ServoCommand* cmd, ServoResponse* resp);void Servo_UpdateStatusCache(void);HAL_StatusTypeDef Servo_EmergencyStop(void);void Servo_RS232_RxCallback(uint8_t* data, uint16_t size);void Servo_MainLoop(void);
#endif
// servo_control.c#include "servo_control.h"#include "usart.h"#include "tim.h"#include <string.h>
// 全局变量static ServoStatusCache g_servo_status;static CommandQueue g_cmd_queue;static CommandQueue g_priority_queue; // 高优先级队列static uint8_t g_rs232_rx_buffer[256];static uint8_t g_rs232_tx_buffer[256];static ModbusHandle g_modbus_handle;
// 初始化函数void Servo_Init(void) { // 初始化命令队列 memset(&g_cmd_queue, 0, sizeof(CommandQueue)); memset(&g_priority_queue, 0, sizeof(CommandQueue));
// 初始化状态缓存 memset(&g_servo_status, 0, sizeof(ServoStatusCache));
// 初始化Modbus Modbus_Init(&g_modbus_handle, &huart2, 1); // 假设使用UART2,从站地址1
// 启动定时器用于周期性状态更新 HAL_TIM_Base_Start_IT(&htim3); // 10ms周期}
// 命令入队(支持优先级)static HAL_StatusTypeDef Enqueue_Command(ServoCommand* cmd) { CommandQueue* queue;
// 紧急停止命令直接执行 if (cmd->cmd_type == CMD_EMERGENCY_STOP) { return Servo_EmergencyStop(); }
// 根据优先级选择队列 if (cmd->priority < 10) { // 高优先级阈值 queue = &g_priority_queue; } else { queue = &g_cmd_queue; }
// 检查队列是否已满 if (queue->count >= CMD_QUEUE_SIZE) { return HAL_ERROR; }
// 入队 memcpy(&queue->commands[queue->tail], cmd, sizeof(ServoCommand)); queue->tail = (queue->tail + 1) % CMD_QUEUE_SIZE; queue->count++;
return HAL_OK;}
// 命令出队并执行static HAL_StatusTypeDef Dequeue_And_Execute(void) { CommandQueue* queue; ServoCommand* cmd; ServoResponse resp;
// 优先处理高优先级队列 if (g_priority_queue.count > 0) { queue = &g_priority_queue; } else if (g_cmd_queue.count > 0) { queue = &g_cmd_queue; } else { return HAL_OK; // 队列为空 }
// 取出命令 cmd = &queue->commands[queue->head];
// 执行命令 HAL_StatusTypeDef result = Servo_ProcessCommand(cmd, &resp);
// 发送响应给上位机 HAL_UART_Transmit_DMA(&huart1, (uint8_t*)&resp, sizeof(resp));
// 出队 queue->head = (queue->head + 1) % CMD_QUEUE_SIZE; queue->count--;
return result;}
// 处理具体命令HAL_StatusTypeDef Servo_ProcessCommand(ServoCommand* cmd, ServoResponse* resp) { HAL_StatusTypeDef result = HAL_OK; uint16_t modbus_data[32];
// 初始化响应包 resp->header[0] = 0xBB; resp->header[1] = 0x66; resp->cmd_type = cmd->cmd_type; resp->sequence = cmd->sequence; resp->status = 0; // 0=成功
switch (cmd->cmd_type) { case CMD_START_STOP: // 写入控制字 result = Modbus_WriteRegister(&g_modbus_handle, REG_CONTROL_WORD, cmd->data[0]); if (result == HAL_OK) { g_servo_status.control_word = cmd->data[0]; } break;
case CMD_SET_POSITION: // 设置目标位置(32位,需要写2个寄存器) modbus_data[0] = (uint16_t)(cmd->data[0]); // 低16位 modbus_data[1] = (uint16_t)(cmd->data[1]); // 高16位 result = Modbus_WriteMultipleRegisters(&g_modbus_handle, REG_TARGET_POSITION, 2, modbus_data); break;
case CMD_SET_SPEED: // 设置目标速度 result = Modbus_WriteRegister(&g_modbus_handle, REG_TARGET_SPEED, cmd->data[0]); break;
case CMD_SET_ACCEL: // 设置加速度和减速度 result = Modbus_WriteRegister(&g_modbus_handle, REG_ACCELERATION, cmd->data[0]); if (result == HAL_OK) { result = Modbus_WriteRegister(&g_modbus_handle, REG_DECELERATION, cmd->data[1]); } break;
case CMD_READ_STATUS: // 读取状态字 result = Modbus_ReadRegisters(&g_modbus_handle, REG_STATUS_WORD, 1, modbus_data); if (result == HAL_OK) { resp->data[0] = modbus_data[0]; resp->data_len = 2; g_servo_status.status_word = modbus_data[0]; } break;
case CMD_READ_ALARM: // 读取报警代码 result = Modbus_ReadRegisters(&g_modbus_handle, REG_ALARM_CODE, 1, modbus_data); if (result == HAL_OK) { resp->data[0] = modbus_data[0]; resp->data_len = 2; g_servo_status.alarm_code = modbus_data[0]; } break;
case CMD_CLEAR_ALARM: // 清除报警(写特定值到控制字) result = Modbus_WriteRegister(&g_modbus_handle, REG_CONTROL_WORD, 0x0080); // 复位位 HAL_Delay(100); result = Modbus_WriteRegister(&g_modbus_handle, REG_CONTROL_WORD, 0x0000); break;
case CMD_READ_ACTUAL_POS: // 读取实际位置(32位) result = Modbus_ReadRegisters(&g_modbus_handle, REG_ACTUAL_POSITION, 2, modbus_data); if (result == HAL_OK) { resp->data[0] = modbus_data[0]; resp->data[1] = modbus_data[1]; resp->data_len = 4; g_servo_status.actual_position = ((int32_t)modbus_data[1] << 16) | modbus_data[0]; } break;
case CMD_BATCH_READ: // 批量读取多个寄存器 result = Modbus_ReadRegisters(&g_modbus_handle, cmd->reg_addr, cmd->reg_count, resp->data); if (result == HAL_OK) { resp->data_len = cmd->reg_count * 2; } break;
case CMD_DIRECT_MODBUS: // 直接透传Modbus命令 if (cmd->data[0] == 0x03) { // 读保持寄存器 result = Modbus_ReadRegisters(&g_modbus_handle, cmd->reg_addr, cmd->reg_count, resp->data); resp->data_len = cmd->reg_count * 2; } else if (cmd->data[0] == 0x10) { // 写多个寄存器 result = Modbus_WriteMultipleRegisters(&g_modbus_handle, cmd->reg_addr, cmd->reg_count, &cmd->data[1]); } break;
default: result = HAL_ERROR; resp->status = 0xFF; // 未知命令 break; }
if (result != HAL_OK) { resp->status = 0x01; // 执行失败 }
// 计算CRC resp->crc16 = Calculate_CRC16((uint8_t*)resp, sizeof(ServoResponse) - 2);
return result;}
// 紧急停止函数HAL_StatusTypeDef Servo_EmergencyStop(void) { // 立即发送停止命令到伺服 uint16_t stop_cmd = 0x0000; // 停止位 HAL_StatusTypeDef result = Modbus_WriteRegister(&g_modbus_handle, REG_CONTROL_WORD, stop_cmd);
// 清空所有命令队列 g_cmd_queue.count = 0; g_cmd_queue.head = 0; g_cmd_queue.tail = 0; g_priority_queue.count = 0; g_priority_queue.head = 0; g_priority_queue.tail = 0;
return result;}
// 周期性更新状态缓存(在定时器中断中调用)void Servo_UpdateStatusCache(void) { static uint8_t update_index = 0; uint16_t data[2];
// 轮询更新不同的状态信息 switch (update_index) { case 0: // 读取状态字 if (Modbus_ReadRegisters(&g_modbus_handle, REG_STATUS_WORD, 1, data) == HAL_OK) { g_servo_status.status_word = data[0]; } break; case 1: // 读取报警代码 if (Modbus_ReadRegisters(&g_modbus_handle, REG_ALARM_CODE, 1, data) == HAL_OK) { g_servo_status.alarm_code = data[0]; // 如果有报警,立即通知上位机 if (data[0] != 0) { ServoResponse alarm_notify; alarm_notify.header[0] = 0xCC; alarm_notify.header[1] = 0x77; alarm_notify.cmd_type = 0xF0; // 报警通知 alarm_notify.data[0] = data[0]; alarm_notify.data_len = 2; HAL_UART_Transmit_IT(&huart1, (uint8_t*)&alarm_notify, sizeof(alarm_notify)); } } break; case 2: // 读取实际位置 if (Modbus_ReadRegisters(&g_modbus_handle, REG_ACTUAL_POSITION, 2, data) == HAL_OK) { g_servo_status.actual_position = ((int32_t)data[1] << 16) | data[0]; } break; case 3: // 读取实际速度 if (Modbus_ReadRegisters(&g_modbus_handle, REG_ACTUAL_SPEED, 1, data) == HAL_OK) { g_servo_status.actual_speed = (int16_t)data[0]; } break; }
update_index = (update_index + 1) % 4; g_servo_status.last_update_tick = HAL_GetTick(); g_servo_status.is_valid = 1;}
// RS232接收回调void Servo_RS232_RxCallback(uint8_t* data, uint16_t size) { // 检查数据包完整性 if (size < sizeof(ServoCommand)) { return; }
ServoCommand* cmd = (ServoCommand*)data;
// 验证包头 if (cmd->header[0] != 0xAA || cmd->header[1] != 0x55) { return; }
// 验证CRC uint16_t calc_crc = Calculate_CRC16(data, size - 2); if (calc_crc != cmd->crc16) { return; }
// 命令入队 Enqueue_Command(cmd);}
// 主循环处理函数void Servo_MainLoop(void) { static uint32_t last_process_tick = 0;
// 每5ms处理一次命令队列 if (HAL_GetTick() - last_process_tick >= 5) { last_process_tick = HAL_GetTick(); Dequeue_And_Execute(); }
// 检查Modbus超时 Modbus_CheckTimeout(&g_modbus_handle);}
// 定时器中断回调(10ms)void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM3) { // 更新状态缓存 Servo_UpdateStatusCache(); }}
// CRC16计算函数uint16_t Calculate_CRC16(uint8_t* data, uint16_t length) { uint16_t crc = 0xFFFF; for (uint16_t i = 0; i < length; i++) { crc ^= data[i]; for (uint8_t j = 0; j < 8; j++) { if (crc & 0x0001) { crc = (crc >> 1) ^ 0xA001; } else { crc >>= 1; } } } return crc;}来源:CSDN 原文,首次发布于 2025-09-19。