| @@ -100,8 +100,6 @@ static void AppTaskStart(void *pArg) | |||
| // (void)ModbusSlaveSetHoldingRegister( // 非法数据值计数 | |||
| // D6, (uint16_t)ModbusSlaveStatistics.illegalValueCount); | |||
| // HistorySaveToRegisters(); | |||
| /* | |||
| * 每1ms轮询一次, | |||
| */ | |||
| @@ -270,6 +270,10 @@ void Plsr_IsrPost(uint8_t axis, uint32_t event_mask); | |||
| #### 3.1.3 流程图 | |||
|  | |||
| **图 3-1 PLSR单轴完整执行流程图** | |||
| ```text | |||
| 系统启动 | |||
| -> 输出置安全状态 | |||
| @@ -476,6 +480,10 @@ void PlsrProfile_SetTarget(PlsrProfile *profile, uint32_t target_hz); | |||
| 高速输出模块使用硬件定时器维持脉冲边沿,OS不参与逐脉冲翻转。 | |||
|  | |||
| **图 3-2 PLSR高速脉冲与硬件计数架构图** | |||
| | 逻辑点 | MCU引脚 | 定时器通道 | 定时器时钟 | | |||
| | --- | --- | --- | ---: | | |||
| | Q0/Y0 | PF6 | TIM10_CH1 | 168 MHz | | |||
| @@ -655,6 +663,10 @@ UNINITIALIZED、IDLE、ACCEL、RUN、DECEL、WAIT、PAUSED、 | |||
| COMPLETED、STOPPED、ERROR | |||
| ``` | |||
|  | |||
| **图 3-3 PLSR单轴状态机与命令优先级图** | |||
| 主要合法转换如下: | |||
| | 当前状态 | 事件 | 下一状态 | | |||
| @@ -1128,6 +1140,13 @@ typedef struct { | |||
| #### 4.2.1 数据流关系 | |||
|  | |||
| **图 4-1 PLSR数据流关系图** | |||
| 图中蓝色表示配置和运动数据,橙色表示硬实时数据及中断事件,绿色表示持久化数据,紫色表示命令与 | |||
| 状态接口。为方便纯文本环境阅读,保留如下简化关系。 | |||
| ```text | |||
| S0/S1/S2/D逻辑软元件 | |||
| | | |||
| @@ -0,0 +1,216 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <svg xmlns="http://www.w3.org/2000/svg" width="1800" height="1100" viewBox="0 0 1800 1100" role="img" aria-labelledby="title desc"> | |||
| <title id="title">PLSR单轴状态机与命令优先级图</title> | |||
| <desc id="desc">展示单轴从初始化、空闲、运动、等待、暂停到正常完成、停止和错误的状态转换,以及软件急停到正常运行的命令事件优先级。</desc> | |||
| <defs> | |||
| <linearGradient id="bg" x1="0" y1="0" x2="0" y2="1"> | |||
| <stop offset="0" stop-color="#F8FBFF"/> | |||
| <stop offset="1" stop-color="#EEF3F9"/> | |||
| </linearGradient> | |||
| <filter id="shadow" x="-15%" y="-15%" width="130%" height="140%"> | |||
| <feDropShadow dx="0" dy="5" stdDeviation="7" flood-color="#183A61" flood-opacity="0.14"/> | |||
| </filter> | |||
| <marker id="blueArrow" markerWidth="11" markerHeight="11" refX="9" refY="5.5" orient="auto"> | |||
| <path d="M0,0 L11,5.5 L0,11 z" fill="#3478B9"/> | |||
| </marker> | |||
| <marker id="orangeArrow" markerWidth="11" markerHeight="11" refX="9" refY="5.5" orient="auto"> | |||
| <path d="M0,0 L11,5.5 L0,11 z" fill="#D96F1E"/> | |||
| </marker> | |||
| <marker id="redArrow" markerWidth="11" markerHeight="11" refX="9" refY="5.5" orient="auto"> | |||
| <path d="M0,0 L11,5.5 L0,11 z" fill="#C64545"/> | |||
| </marker> | |||
| <marker id="greenArrow" markerWidth="11" markerHeight="11" refX="9" refY="5.5" orient="auto"> | |||
| <path d="M0,0 L11,5.5 L0,11 z" fill="#398760"/> | |||
| </marker> | |||
| <style> | |||
| .title { font: 700 34px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #17385F; } | |||
| .subtitle { font: 400 16px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #5E7187; } | |||
| .layer { font: 700 21px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #17385F; } | |||
| .state-title { font: 700 19px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #17385F; } | |||
| .state-text { font: 400 14px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #485E75; } | |||
| .white-title { font: 700 19px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #FFFFFF; } | |||
| .white-text { font: 400 14px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #F4F8FD; } | |||
| .label { font: 600 13px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #405A74; } | |||
| .small { font: 400 13px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #5A6E83; } | |||
| .blue { fill: none; stroke: #3478B9; stroke-width: 3; marker-end: url(#blueArrow); } | |||
| .orange { fill: none; stroke: #D96F1E; stroke-width: 3; marker-end: url(#orangeArrow); } | |||
| .red { fill: none; stroke: #C64545; stroke-width: 3; marker-end: url(#redArrow); } | |||
| .green { fill: none; stroke: #398760; stroke-width: 3; marker-end: url(#greenArrow); } | |||
| .dash { stroke-dasharray: 8 7; } | |||
| </style> | |||
| </defs> | |||
| <rect width="1800" height="1100" fill="url(#bg)"/> | |||
| <text x="900" y="50" text-anchor="middle" class="title">PLSR 单轴状态机与命令优先级图</text> | |||
| <text x="900" y="78" text-anchor="middle" class="subtitle">每轴独立状态 · 唯一转换入口 · 高优先级事件不可被低优先级命令覆盖</text> | |||
| <!-- 状态机区域 --> | |||
| <g filter="url(#shadow)"> | |||
| <rect x="65" y="105" width="1270" height="900" rx="22" fill="#FFFFFF" stroke="#A8C4E0" stroke-width="2"/> | |||
| </g> | |||
| <text x="100" y="143" class="layer">单轴状态转换</text> | |||
| <!-- 状态节点 --> | |||
| <g filter="url(#shadow)"> | |||
| <rect x="115" y="180" width="240" height="95" rx="18" fill="#E9EEF4" stroke="#9AABBC" stroke-width="2"/> | |||
| <rect x="470" y="180" width="240" height="95" rx="18" fill="#EAF4FE" stroke="#78A8D6" stroke-width="2"/> | |||
| <rect x="205" y="385" width="240" height="105" rx="18" fill="#2F78B7"/> | |||
| <rect x="555" y="385" width="240" height="105" rx="18" fill="#2F78B7"/> | |||
| <rect x="905" y="385" width="240" height="105" rx="18" fill="#2F78B7"/> | |||
| <rect x="205" y="595" width="240" height="105" rx="18" fill="#FFF4E8" stroke="#DFA06D" stroke-width="2"/> | |||
| <rect x="555" y="595" width="240" height="105" rx="18" fill="#FFF4E8" stroke="#DFA06D" stroke-width="2"/> | |||
| <rect x="130" y="825" width="250" height="105" rx="18" fill="#EAF8F0" stroke="#73B18F" stroke-width="2"/> | |||
| <rect x="490" y="825" width="250" height="105" rx="18" fill="#F5F1EB" stroke="#B0A18C" stroke-width="2"/> | |||
| <rect x="850" y="825" width="250" height="105" rx="18" fill="#FDEDED" stroke="#D68181" stroke-width="2"/> | |||
| </g> | |||
| <text x="235" y="216" text-anchor="middle" class="state-title">UNINITIALIZED</text> | |||
| <text x="235" y="245" text-anchor="middle" class="state-text">输出安全 · 模块未就绪</text> | |||
| <text x="590" y="216" text-anchor="middle" class="state-title">IDLE</text> | |||
| <text x="590" y="245" text-anchor="middle" class="state-text">BUSY=0 · PULSE_ACTIVE=0</text> | |||
| <text x="325" y="424" text-anchor="middle" class="white-title">ACCEL</text> | |||
| <text x="325" y="453" text-anchor="middle" class="white-text">加速输出</text> | |||
| <text x="325" y="476" text-anchor="middle" class="white-text">BUSY=1 · PULSE_ACTIVE=1</text> | |||
| <text x="675" y="424" text-anchor="middle" class="white-title">RUN</text> | |||
| <text x="675" y="453" text-anchor="middle" class="white-text">目标频率运行/段内调频</text> | |||
| <text x="675" y="476" text-anchor="middle" class="white-text">BUSY=1 · PULSE_ACTIVE=1</text> | |||
| <text x="1025" y="424" text-anchor="middle" class="white-title">DECEL</text> | |||
| <text x="1025" y="453" text-anchor="middle" class="white-text">减速或反向前停车</text> | |||
| <text x="1025" y="476" text-anchor="middle" class="white-text">BUSY=1 · PULSE_ACTIVE=1</text> | |||
| <text x="325" y="635" text-anchor="middle" class="state-title">WAIT</text> | |||
| <text x="325" y="664" text-anchor="middle" class="state-text">等待时间/信号/EXT</text> | |||
| <text x="325" y="687" text-anchor="middle" class="state-text">BUSY=1 · PULSE_ACTIVE=0</text> | |||
| <text x="675" y="635" text-anchor="middle" class="state-title">PAUSED</text> | |||
| <text x="675" y="664" text-anchor="middle" class="state-text">保留快照、段号和剩余量</text> | |||
| <text x="675" y="687" text-anchor="middle" class="state-text">BUSY=1 · PULSE_ACTIVE=0</text> | |||
| <text x="255" y="864" text-anchor="middle" class="state-title">COMPLETED</text> | |||
| <text x="255" y="893" text-anchor="middle" class="state-text">正常结束 · DONE=1并锁存</text> | |||
| <text x="255" y="916" text-anchor="middle" class="state-text">BUSY=0 · PULSE_ACTIVE=0</text> | |||
| <text x="615" y="864" text-anchor="middle" class="state-title">STOPPED</text> | |||
| <text x="615" y="893" text-anchor="middle" class="state-text">命令/限位停止 · DONE=0</text> | |||
| <text x="615" y="916" text-anchor="middle" class="state-text">停止原因锁存</text> | |||
| <text x="975" y="864" text-anchor="middle" class="state-title">ERROR</text> | |||
| <text x="975" y="893" text-anchor="middle" class="state-text">不可恢复运行错误 · DONE=0</text> | |||
| <text x="975" y="916" text-anchor="middle" class="state-text">禁止新脉冲并锁存错误</text> | |||
| <!-- 主运行链:线条只穿过状态框之间的留白区 --> | |||
| <path d="M355 227 L462 227" class="blue"/> | |||
| <text x="408" y="215" text-anchor="middle" class="label">初始化成功</text> | |||
| <path d="M590 275 L590 320 L325 320 L325 377" class="blue"/> | |||
| <text x="455" y="309" text-anchor="middle" class="label">START成功并建立运行快照</text> | |||
| <path d="M445 437 L547 437" class="blue"/> | |||
| <text x="496" y="425" text-anchor="middle" class="label">达到运行频率</text> | |||
| <path d="M795 437 L897 437" class="blue"/> | |||
| <text x="846" y="425" text-anchor="middle" class="label">需减速或反向</text> | |||
| <path d="M1025 385 L1025 348 L675 348 L675 377" class="blue dash"/> | |||
| <text x="850" y="338" text-anchor="middle" class="label">下一同向段或继续运行</text> | |||
| <!-- WAIT:进入与退出使用状态框两侧的独立通道 --> | |||
| <path d="M555 465 L500 465 L500 647 L453 647" class="orange"/> | |||
| <text x="492" y="526" text-anchor="end" class="label">段完成且条件未满足</text> | |||
| <path d="M325 595 L325 498" class="orange dash"/> | |||
| <text x="310" y="554" text-anchor="end" class="label">条件满足,进入下一段</text> | |||
| <!-- PAUSE / RESUME --> | |||
| <path d="M1025 490 L1025 550 L675 550 L675 587" class="orange"/> | |||
| <text x="850" y="539" text-anchor="middle" class="label">PAUSE:减速到0</text> | |||
| <path d="M555 647 L520 647 L520 565 L420 565 L420 498" class="orange dash"/> | |||
| <text x="472" y="581" text-anchor="middle" class="label">RESUME</text> | |||
| <!-- 正常完成 --> | |||
| <path d="M325 700 L325 770 L255 770 L255 817" class="green"/> | |||
| <text x="255" y="758" text-anchor="middle" class="label">等待后正常结束</text> | |||
| <path d="M605 490 L605 520 L540 520 L540 720 L180 720 L180 817" class="green"/> | |||
| <text x="190" y="710" class="label">最后一段正常完成</text> | |||
| <!-- 零位移可直接正常完成,不要求先进入脉冲活动态 --> | |||
| <path d="M520 275 L520 290 L72 290 L72 790 L180 790 L180 817" class="green dash"/> | |||
| <text x="82" y="282" class="label">零位移/零脉冲末段</text> | |||
| <!-- 抢占与异常采用汇总入口,避免把每个活动态重复连线 --> | |||
| <rect x="410" y="752" width="410" height="54" rx="12" fill="#FFF3E7" stroke="#D8843F"/> | |||
| <text x="615" y="775" text-anchor="middle" class="label">任意BUSY状态:STOP_DECEL减速到0</text> | |||
| <text x="615" y="797" text-anchor="middle" class="label">STOP_IMMEDIATE完成当前有效脉冲后关闭</text> | |||
| <path d="M615 806 L615 817" class="orange"/> | |||
| <rect x="850" y="752" width="390" height="54" rx="12" fill="#FDEDEE" stroke="#C64545"/> | |||
| <text x="1045" y="775" text-anchor="middle" class="label">任意状态:初始化、参数、定时器</text> | |||
| <text x="1045" y="797" text-anchor="middle" class="label">或硬件计数发生不可恢复错误</text> | |||
| <path d="M975 806 L975 817" class="red"/> | |||
| <path d="M1100 877 L1280 877 L1280 145 L710 145 L710 227" class="red dash"/> | |||
| <text x="995" y="134" text-anchor="middle" class="label">RESET_ERROR成功:仅回IDLE,不自动续跑</text> | |||
| <!-- 终态后的下一次启动规则用注记表达,避免回路线穿越状态图 --> | |||
| <rect x="130" y="955" width="610" height="43" rx="11" fill="#EEF5FC" stroke="#7FA9CF"/> | |||
| <text x="435" y="982" text-anchor="middle" class="label">COMPLETED / STOPPED收到合法START:清DONE,建立新快照后重新运行</text> | |||
| <!-- 优先级区域 --> | |||
| <g filter="url(#shadow)"> | |||
| <rect x="1370" y="105" width="365" height="900" rx="22" fill="#FFFFFF" stroke="#D4B0A0" stroke-width="2"/> | |||
| </g> | |||
| <text x="1552" y="143" text-anchor="middle" class="layer">命令与事件优先级</text> | |||
| <text x="1552" y="174" text-anchor="middle" class="small">越靠上优先级越高</text> | |||
| <rect x="1415" y="215" width="275" height="80" rx="15" fill="#A92F38"/> | |||
| <text x="1552" y="248" text-anchor="middle" class="white-title">1 软件急停</text> | |||
| <text x="1552" y="274" text-anchor="middle" class="white-text">立即禁止新脉冲并锁存</text> | |||
| <rect x="1415" y="315" width="275" height="80" rx="15" fill="#C3463F"/> | |||
| <text x="1552" y="348" text-anchor="middle" class="white-title">2 硬限位</text> | |||
| <text x="1552" y="374" text-anchor="middle" class="white-text">禁止越界方向,默认减速停止</text> | |||
| <rect x="1415" y="415" width="275" height="80" rx="15" fill="#D6653F"/> | |||
| <text x="1552" y="448" text-anchor="middle" class="white-title">3 STOP_IMMEDIATE</text> | |||
| <text x="1552" y="474" text-anchor="middle" class="white-text">完成当前有效脉冲后关闭</text> | |||
| <rect x="1415" y="515" width="275" height="80" rx="15" fill="#E28B45"/> | |||
| <text x="1552" y="548" text-anchor="middle" class="white-title">4 STOP_DECEL</text> | |||
| <text x="1552" y="574" text-anchor="middle" class="white-text">按当前减速曲线停止</text> | |||
| <rect x="1415" y="615" width="275" height="80" rx="15" fill="#E5AE57"/> | |||
| <text x="1552" y="648" text-anchor="middle" class="white-title">5 PAUSE</text> | |||
| <text x="1552" y="674" text-anchor="middle" class="white-text">减速到0并保留运行快照</text> | |||
| <rect x="1415" y="715" width="275" height="80" rx="15" fill="#6E9EC8"/> | |||
| <text x="1552" y="748" text-anchor="middle" class="white-title">6 正常运行事件</text> | |||
| <text x="1552" y="774" text-anchor="middle" class="white-text">段完成、等待、跳转、调频</text> | |||
| <path d="M1552 295 L1552 307" class="red"/> | |||
| <path d="M1552 395 L1552 407" class="red"/> | |||
| <path d="M1552 495 L1552 507" class="orange"/> | |||
| <path d="M1552 595 L1552 607" class="orange"/> | |||
| <path d="M1552 695 L1552 707" class="blue"/> | |||
| <rect x="1415" y="835" width="275" height="115" rx="15" fill="#F5F7FA" stroke="#B7C2CE"/> | |||
| <text x="1552" y="866" text-anchor="middle" class="state-title">锁存规则</text> | |||
| <text x="1552" y="895" text-anchor="middle" class="state-text">高优先级原因一旦锁存</text> | |||
| <text x="1552" y="919" text-anchor="middle" class="state-text">低优先级命令不得覆盖</text> | |||
| <text x="1552" y="943" text-anchor="middle" class="state-text">重复立即停止按幂等处理</text> | |||
| <!-- 图例 --> | |||
| <circle cx="95" cy="1065" r="6" fill="#2F78B7"/> | |||
| <text x="110" y="1070" class="small">脉冲活动状态</text> | |||
| <circle cx="260" cy="1065" r="6" fill="#E0A064"/> | |||
| <text x="275" y="1070" class="small">BUSY但不发脉冲</text> | |||
| <circle cx="455" cy="1065" r="6" fill="#398760"/> | |||
| <text x="470" y="1070" class="small">正常完成</text> | |||
| <circle cx="585" cy="1065" r="6" fill="#C64545"/> | |||
| <text x="600" y="1070" class="small">异常/抢占转换</text> | |||
| </svg> | |||
| @@ -0,0 +1,170 @@ | |||
| <svg xmlns="http://www.w3.org/2000/svg" width="2000" height="1250" viewBox="0 0 2000 1250"> | |||
| <defs> | |||
| <filter id="shadow" x="-20%" y="-20%" width="140%" height="150%"> | |||
| <feDropShadow dx="0" dy="7" stdDeviation="7" flood-color="#24445F" flood-opacity="0.16"/> | |||
| </filter> | |||
| <marker id="arrowBlue" markerWidth="13" markerHeight="13" refX="11" refY="6.5" orient="auto"> | |||
| <path d="M0,0 L13,6.5 L0,13 Z" fill="#2F78B7"/> | |||
| </marker> | |||
| <marker id="arrowOrange" markerWidth="13" markerHeight="13" refX="11" refY="6.5" orient="auto"> | |||
| <path d="M0,0 L13,6.5 L0,13 Z" fill="#D97925"/> | |||
| </marker> | |||
| <marker id="arrowGreen" markerWidth="13" markerHeight="13" refX="11" refY="6.5" orient="auto"> | |||
| <path d="M0,0 L13,6.5 L0,13 Z" fill="#31845E"/> | |||
| </marker> | |||
| <marker id="arrowRed" markerWidth="13" markerHeight="13" refX="11" refY="6.5" orient="auto"> | |||
| <path d="M0,0 L13,6.5 L0,13 Z" fill="#C64545"/> | |||
| </marker> | |||
| <style> | |||
| text { font-family: "Microsoft YaHei", "Noto Sans CJK SC", Arial, sans-serif; fill: #123A60; } | |||
| .title { font-size: 42px; font-weight: 700; fill: #123A60; } | |||
| .subtitle { font-size: 20px; fill: #61778C; } | |||
| .lane { font-size: 20px; font-weight: 700; fill: #50708D; letter-spacing: 1px; } | |||
| .state-title { font-size: 29px; font-weight: 700; } | |||
| .state-desc { font-size: 19px; } | |||
| .flag { font-size: 17px; fill: #345B7D; } | |||
| .label { font-size: 18px; font-weight: 600; paint-order: stroke; stroke: #FFFFFF; stroke-width: 7px; stroke-linejoin: round; } | |||
| .note-title { font-size: 18px; font-weight: 700; } | |||
| .note-text { font-size: 17px; } | |||
| .blue { fill: none; stroke: #2F78B7; stroke-width: 4; marker-end: url(#arrowBlue); } | |||
| .orange { fill: none; stroke: #D97925; stroke-width: 4; marker-end: url(#arrowOrange); } | |||
| .green { fill: none; stroke: #31845E; stroke-width: 4; marker-end: url(#arrowGreen); } | |||
| .red { fill: none; stroke: #C64545; stroke-width: 4; marker-end: url(#arrowRed); } | |||
| .dash { stroke-dasharray: 11 9; } | |||
| </style> | |||
| </defs> | |||
| <rect width="2000" height="1250" fill="#F4F8FC"/> | |||
| <text x="1000" y="62" text-anchor="middle" class="title">PLSR 单轴状态机</text> | |||
| <text x="1000" y="94" text-anchor="middle" class="subtitle">每轴独立运行 · 唯一状态入口 · 硬件脉冲发生与状态控制解耦</text> | |||
| <rect x="65" y="125" width="1870" height="1050" rx="25" fill="#FFFFFF" stroke="#A7C8E5" stroke-width="2.5" filter="url(#shadow)"/> | |||
| <!-- 分层背景 --> | |||
| <rect x="95" y="155" width="1810" height="185" rx="18" fill="#F4F7FA"/> | |||
| <rect x="95" y="365" width="1810" height="245" rx="18" fill="#F0F6FC"/> | |||
| <rect x="95" y="635" width="1810" height="210" rx="18" fill="#FFF8F0"/> | |||
| <rect x="95" y="870" width="1810" height="245" rx="18" fill="#F6FAF7"/> | |||
| <text x="115" y="184" class="lane">准备状态</text> | |||
| <text x="115" y="394" class="lane">脉冲活动状态</text> | |||
| <text x="115" y="664" class="lane">保持状态</text> | |||
| <text x="115" y="899" class="lane">终止状态</text> | |||
| <!-- 准备状态 --> | |||
| <g filter="url(#shadow)"> | |||
| <rect x="190" y="205" width="330" height="105" rx="20" fill="#E8EDF3" stroke="#93A9BD" stroke-width="2.5"/> | |||
| <rect x="700" y="205" width="330" height="105" rx="20" fill="#EAF4FD" stroke="#66A4DC" stroke-width="2.5"/> | |||
| </g> | |||
| <text x="355" y="246" text-anchor="middle" class="state-title">UNINITIALIZED</text> | |||
| <text x="355" y="278" text-anchor="middle" class="state-desc">输出安全,模块尚未就绪</text> | |||
| <text x="865" y="246" text-anchor="middle" class="state-title">IDLE</text> | |||
| <text x="865" y="278" text-anchor="middle" class="flag">BUSY=0 · PULSE_ACTIVE=0</text> | |||
| <!-- 活动状态 --> | |||
| <g filter="url(#shadow)"> | |||
| <rect x="190" y="430" width="330" height="120" rx="20" fill="#347FBD"/> | |||
| <rect x="700" y="430" width="330" height="120" rx="20" fill="#2876B5"/> | |||
| <rect x="1210" y="430" width="330" height="120" rx="20" fill="#347FBD"/> | |||
| </g> | |||
| <text x="355" y="474" text-anchor="middle" class="state-title" fill="#FFFFFF" style="fill:#FFFFFF">ACCEL</text> | |||
| <text x="355" y="504" text-anchor="middle" class="state-desc" fill="#FFFFFF" style="fill:#FFFFFF">按加速参数提升频率</text> | |||
| <text x="355" y="532" text-anchor="middle" class="flag" fill="#FFFFFF" style="fill:#FFFFFF">BUSY=1 · PULSE_ACTIVE=1</text> | |||
| <text x="865" y="474" text-anchor="middle" class="state-title" fill="#FFFFFF" style="fill:#FFFFFF">RUN</text> | |||
| <text x="865" y="504" text-anchor="middle" class="state-desc" fill="#FFFFFF" style="fill:#FFFFFF">恒频或段内动态调频</text> | |||
| <text x="865" y="532" text-anchor="middle" class="flag" fill="#FFFFFF" style="fill:#FFFFFF">BUSY=1 · PULSE_ACTIVE=1</text> | |||
| <text x="1375" y="474" text-anchor="middle" class="state-title" fill="#FFFFFF" style="fill:#FFFFFF">DECEL</text> | |||
| <text x="1375" y="504" text-anchor="middle" class="state-desc" fill="#FFFFFF" style="fill:#FFFFFF">减速、反向或受控停止</text> | |||
| <text x="1375" y="532" text-anchor="middle" class="flag" fill="#FFFFFF" style="fill:#FFFFFF">BUSY=1 · PULSE_ACTIVE=1</text> | |||
| <!-- 保持状态 --> | |||
| <g filter="url(#shadow)"> | |||
| <rect x="190" y="690" width="330" height="115" rx="20" fill="#FFF3E7" stroke="#EA9B55" stroke-width="2.5"/> | |||
| <rect x="700" y="690" width="330" height="115" rx="20" fill="#FFF3E7" stroke="#EA9B55" stroke-width="2.5"/> | |||
| </g> | |||
| <text x="355" y="734" text-anchor="middle" class="state-title">WAIT</text> | |||
| <text x="355" y="765" text-anchor="middle" class="state-desc">等待时间、信号或EXT条件</text> | |||
| <text x="355" y="791" text-anchor="middle" class="flag">BUSY=1 · PULSE_ACTIVE=0</text> | |||
| <text x="865" y="734" text-anchor="middle" class="state-title">PAUSED</text> | |||
| <text x="865" y="765" text-anchor="middle" class="state-desc">保留快照、段号与剩余量</text> | |||
| <text x="865" y="791" text-anchor="middle" class="flag">BUSY=1 · PULSE_ACTIVE=0</text> | |||
| <!-- 终止状态 --> | |||
| <g filter="url(#shadow)"> | |||
| <rect x="175" y="950" width="350" height="120" rx="20" fill="#ECF8F1" stroke="#56A67D" stroke-width="2.5"/> | |||
| <rect x="690" y="950" width="350" height="120" rx="20" fill="#F5F2ED" stroke="#AF9A7C" stroke-width="2.5"/> | |||
| <rect x="1205" y="950" width="350" height="120" rx="20" fill="#FDEEEF" stroke="#E06469" stroke-width="2.5"/> | |||
| </g> | |||
| <text x="350" y="994" text-anchor="middle" class="state-title">COMPLETED</text> | |||
| <text x="350" y="1025" text-anchor="middle" class="state-desc">正常结束,DONE=1并锁存</text> | |||
| <text x="350" y="1053" text-anchor="middle" class="flag">BUSY=0 · PULSE_ACTIVE=0</text> | |||
| <text x="865" y="994" text-anchor="middle" class="state-title">STOPPED</text> | |||
| <text x="865" y="1025" text-anchor="middle" class="state-desc">命令或限位停止,DONE=0</text> | |||
| <text x="865" y="1053" text-anchor="middle" class="flag">停止原因锁存</text> | |||
| <text x="1380" y="994" text-anchor="middle" class="state-title">ERROR</text> | |||
| <text x="1380" y="1025" text-anchor="middle" class="state-desc">不可恢复运行错误,DONE=0</text> | |||
| <text x="1380" y="1053" text-anchor="middle" class="flag">禁止新脉冲并锁存错误</text> | |||
| <!-- 主运行链 --> | |||
| <path d="M520 257 L692 257" class="blue"/> | |||
| <text x="606" y="244" text-anchor="middle" class="label">初始化成功</text> | |||
| <path d="M865 310 L865 355 L355 355 L355 422" class="blue"/> | |||
| <text x="610" y="345" text-anchor="middle" class="label">合法START:校验并建立快照</text> | |||
| <path d="M520 490 L692 490" class="blue"/> | |||
| <text x="606" y="478" text-anchor="middle" class="label">达到目标频率</text> | |||
| <path d="M1030 490 L1202 490" class="blue"/> | |||
| <text x="1116" y="478" text-anchor="middle" class="label">需要减速或反向</text> | |||
| <path d="M1375 430 L1375 405 L865 405 L865 422" class="blue dash"/> | |||
| <text x="1120" y="395" text-anchor="middle" class="label">下一同向段或继续运行</text> | |||
| <!-- WAIT --> | |||
| <path d="M700 500 L610 500 L610 748 L528 748" class="orange"/> | |||
| <text x="625" y="675" class="label">段完成且等待条件未满足</text> | |||
| <path d="M355 690 L355 558" class="orange dash"/> | |||
| <text x="340" y="632" text-anchor="end" class="label">条件满足,进入下一段</text> | |||
| <!-- PAUSE --> | |||
| <path d="M1375 550 L1375 640 L865 640 L865 682" class="orange"/> | |||
| <text x="1120" y="629" text-anchor="middle" class="label">PAUSE减速到0</text> | |||
| <path d="M700 748 L620 748 L620 615 L470 615 L470 558" class="orange dash"/> | |||
| <text x="635" y="735" text-anchor="middle" class="label">RESUME</text> | |||
| <!-- 正常完成 --> | |||
| <path d="M355 805 L355 942" class="green"/> | |||
| <text x="340" y="880" text-anchor="end" class="label">等待后正常结束</text> | |||
| <path d="M700 535 L650 535 L650 830 L350 830 L350 942" class="green"/> | |||
| <text x="500" y="820" text-anchor="middle" class="label">最后一段正常完成</text> | |||
| <!-- 停止汇总入口 --> | |||
| <rect x="650" y="850" width="430" height="68" rx="14" fill="#FFF3E7" stroke="#D97925" stroke-width="2"/> | |||
| <text x="865" y="877" text-anchor="middle" class="note-title">停止入口</text> | |||
| <text x="865" y="902" text-anchor="middle" class="note-text">STOP_DECEL到0 / STOP_IMMEDIATE收尾</text> | |||
| <path d="M865 918 L865 942" class="orange"/> | |||
| <!-- 错误与复位 --> | |||
| <rect x="1210" y="850" width="340" height="68" rx="14" fill="#FDEEEF" stroke="#C64545" stroke-width="2"/> | |||
| <text x="1380" y="877" text-anchor="middle" class="note-title">任意状态不可恢复错误</text> | |||
| <text x="1380" y="902" text-anchor="middle" class="note-text">参数、定时器、计数或非法转换</text> | |||
| <path d="M1380 918 L1380 942" class="red"/> | |||
| <path d="M1555 1010 L1850 1010 L1850 170 L1030 170 L1030 245" class="red dash"/> | |||
| <text x="1450" y="158" text-anchor="middle" class="label">RESET_ERROR成功:仅回IDLE,不自动续跑</text> | |||
| <!-- 说明与图例 --> | |||
| <rect x="1580" y="205" width="285" height="335" rx="18" fill="#F8FAFC" stroke="#B9C8D5" stroke-width="2"/> | |||
| <text x="1722" y="242" text-anchor="middle" class="state-title" style="font-size:23px">转换原则</text> | |||
| <text x="1605" y="282" class="note-text">1. 每个周期只执行一次转换</text> | |||
| <text x="1605" y="320" class="note-text">2. 高优先级事件先于普通事件</text> | |||
| <text x="1605" y="358" class="note-text">3. 状态切换不直接产生脉冲</text> | |||
| <text x="1605" y="396" class="note-text">4. 硬件安全收尾后确认终态</text> | |||
| <text x="1605" y="434" class="note-text">5. 终态原因和错误均需锁存</text> | |||
| <text x="1605" y="472" class="note-text">6. 零任务直接进入完成态</text> | |||
| <line x1="1605" y1="505" x2="1655" y2="505" stroke="#2F78B7" stroke-width="4"/> | |||
| <text x="1670" y="512" class="note-text">正常运行</text> | |||
| <line x1="1605" y1="535" x2="1655" y2="535" stroke="#D97925" stroke-width="4"/> | |||
| <text x="1670" y="542" class="note-text">等待/暂停/停止</text> | |||
| <text x="1000" y="1145" text-anchor="middle" class="subtitle">COMPLETED 或 STOPPED 收到下一次合法 START:清除旧完成标志,重新校验并建立运行快照</text> | |||
| </svg> | |||
| @@ -0,0 +1,179 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <svg xmlns="http://www.w3.org/2000/svg" width="1800" height="1100" viewBox="0 0 1800 1100" role="img" aria-labelledby="title desc"> | |||
| <title id="title">PLSR 4.2.1 数据流关系图</title> | |||
| <desc id="desc">展示PLSR逻辑软元件、参数快照、路径和速度处理、轴状态机、硬件与软件计数、状态发布及持久化之间的数据关系。</desc> | |||
| <defs> | |||
| <linearGradient id="bg" x1="0" y1="0" x2="0" y2="1"> | |||
| <stop offset="0" stop-color="#F8FBFF"/> | |||
| <stop offset="1" stop-color="#EEF3F9"/> | |||
| </linearGradient> | |||
| <filter id="shadow" x="-12%" y="-12%" width="124%" height="135%"> | |||
| <feDropShadow dx="0" dy="5" stdDeviation="7" flood-color="#183A61" flood-opacity="0.14"/> | |||
| </filter> | |||
| <marker id="blueArrow" markerWidth="11" markerHeight="11" refX="9" refY="5.5" orient="auto"> | |||
| <path d="M0,0 L11,5.5 L0,11 z" fill="#3478B9"/> | |||
| </marker> | |||
| <marker id="orangeArrow" markerWidth="11" markerHeight="11" refX="9" refY="5.5" orient="auto"> | |||
| <path d="M0,0 L11,5.5 L0,11 z" fill="#D96F1E"/> | |||
| </marker> | |||
| <marker id="greenArrow" markerWidth="11" markerHeight="11" refX="9" refY="5.5" orient="auto"> | |||
| <path d="M0,0 L11,5.5 L0,11 z" fill="#398760"/> | |||
| </marker> | |||
| <marker id="purpleArrow" markerWidth="11" markerHeight="11" refX="9" refY="5.5" orient="auto"> | |||
| <path d="M0,0 L11,5.5 L0,11 z" fill="#7759A7"/> | |||
| </marker> | |||
| <style> | |||
| .title { font: 700 34px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #17385F; } | |||
| .subtitle { font: 400 16px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #5E7187; } | |||
| .layer { font: 700 21px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #17385F; } | |||
| .box-title { font: 700 18px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #17385F; } | |||
| .box-text { font: 400 14px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #445C75; } | |||
| .small { font: 400 13px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #5A6E83; } | |||
| .white-title { font: 700 19px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #FFFFFF; } | |||
| .white-text { font: 400 14px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #F4F8FD; } | |||
| .blue { fill: none; stroke: #3478B9; stroke-width: 3; marker-end: url(#blueArrow); } | |||
| .orange { fill: none; stroke: #D96F1E; stroke-width: 3; marker-end: url(#orangeArrow); } | |||
| .green { fill: none; stroke: #398760; stroke-width: 3; marker-end: url(#greenArrow); } | |||
| .purple { fill: none; stroke: #7759A7; stroke-width: 3; marker-end: url(#purpleArrow); } | |||
| .dash { stroke-dasharray: 8 7; } | |||
| </style> | |||
| </defs> | |||
| <rect width="1800" height="1100" fill="url(#bg)"/> | |||
| <text x="900" y="50" text-anchor="middle" class="title">PLSR 数据流关系图</text> | |||
| <text x="900" y="78" text-anchor="middle" class="subtitle">参数数据、运行快照、运动计算、实时计数、状态发布与持久化</text> | |||
| <!-- 输入数据层 --> | |||
| <g filter="url(#shadow)"> | |||
| <rect x="70" y="105" width="1660" height="145" rx="20" fill="#FFFFFF" stroke="#A9C5E2" stroke-width="2"/> | |||
| </g> | |||
| <text x="105" y="140" class="layer">输入数据层</text> | |||
| <rect x="260" y="135" width="310" height="83" rx="14" fill="#EDF5FE" stroke="#9CBFE1"/> | |||
| <text x="415" y="167" text-anchor="middle" class="box-title">S0 / S1 动态数据</text> | |||
| <text x="415" y="194" text-anchor="middle" class="box-text">路径段 · 定位模式 · 起始段</text> | |||
| <rect x="600" y="135" width="310" height="83" rx="14" fill="#EDF5FE" stroke="#9CBFE1"/> | |||
| <text x="755" y="167" text-anchor="middle" class="box-title">S2 与固定配置</text> | |||
| <text x="755" y="194" text-anchor="middle" class="box-text">HSD460~539 · SFD900~1419</text> | |||
| <rect x="940" y="135" width="310" height="83" rx="14" fill="#EDF5FE" stroke="#9CBFE1"/> | |||
| <text x="1095" y="167" text-anchor="middle" class="box-title">D 与输出配置</text> | |||
| <text x="1095" y="194" text-anchor="middle" class="box-text">模式 · 高速点 · DIR点 · 极性</text> | |||
| <rect x="1280" y="135" width="310" height="83" rx="14" fill="#EDF5FE" stroke="#9CBFE1"/> | |||
| <text x="1435" y="167" text-anchor="middle" class="box-title">命令与运行变量</text> | |||
| <text x="1435" y="194" text-anchor="middle" class="box-text">START/STOP · Wait/EXT · 动态频率</text> | |||
| <!-- 参数提交 --> | |||
| <path d="M415 218 L415 276 L675 276" class="blue"/> | |||
| <path d="M755 218 L755 276" class="blue"/> | |||
| <path d="M1095 218 L1095 276 L825 276" class="blue"/> | |||
| <g filter="url(#shadow)"> | |||
| <rect x="610" y="285" width="580" height="96" rx="17" fill="#FFFFFF" stroke="#6E9FD0" stroke-width="2"/> | |||
| </g> | |||
| <text x="900" y="320" text-anchor="middle" class="box-title">PlsrParser_BuildSnapshot</text> | |||
| <text x="900" y="349" text-anchor="middle" class="box-text">边界与组合校验 · 原子申请资源 · 生成不可变执行副本</text> | |||
| <path d="M900 381 L900 409" class="blue"/> | |||
| <g filter="url(#shadow)"> | |||
| <rect x="650" y="418" width="500" height="84" rx="16" fill="#2F70AF"/> | |||
| </g> | |||
| <text x="900" y="451" text-anchor="middle" class="white-title">PlsrJobSnapshot</text> | |||
| <text x="900" y="479" text-anchor="middle" class="white-text">S0段快照 · S1模式 · S2参数 · D绑定 · 计数器租约</text> | |||
| <!-- 运动处理层 --> | |||
| <text x="80" y="548" class="layer">运动处理层</text> | |||
| <path d="M900 502 L900 530 L320 530 L320 565" class="blue"/> | |||
| <path d="M900 502 L900 565" class="blue"/> | |||
| <path d="M900 502 L900 530 L1480 530 L1480 565" class="blue"/> | |||
| <rect x="140" y="575" width="360" height="112" rx="16" fill="#FFFFFF" stroke="#86AFD6" stroke-width="2"/> | |||
| <text x="320" y="610" text-anchor="middle" class="box-title">PlsrPath 路径解释</text> | |||
| <text x="320" y="638" text-anchor="middle" class="box-text">段进入 · Wait/ACT/EXT · 动态跳转</text> | |||
| <text x="320" y="663" text-anchor="middle" class="box-text">输出:段目标、方向、结束与事件</text> | |||
| <rect x="720" y="575" width="360" height="112" rx="16" fill="#FFFFFF" stroke="#86AFD6" stroke-width="2"/> | |||
| <text x="900" y="610" text-anchor="middle" class="box-title">PlsrProfile 速度规划</text> | |||
| <text x="900" y="638" text-anchor="middle" class="box-text">直线/S形/正弦 · 短距离 · 动态调频</text> | |||
| <text x="900" y="663" text-anchor="middle" class="box-text">输出:命令频率与PSC/ARR/CCR</text> | |||
| <rect x="1300" y="575" width="360" height="112" rx="16" fill="#FFFFFF" stroke="#86AFD6" stroke-width="2"/> | |||
| <text x="1480" y="610" text-anchor="middle" class="box-title">PlsrResource 资源管理</text> | |||
| <text x="1480" y="638" text-anchor="middle" class="box-text">Q0~Q3 · DIR · TIM9/TIM12</text> | |||
| <text x="1480" y="663" text-anchor="middle" class="box-text">输出:输出租约与计数模式</text> | |||
| <!-- 轴运行对象 --> | |||
| <path d="M320 687 L320 702 L575 702 L575 760 L597 760" class="blue"/> | |||
| <path d="M900 687 L900 710" class="blue"/> | |||
| <path d="M1480 687 L1480 700 L1215 700 L1215 760 L1203 760" class="blue"/> | |||
| <g filter="url(#shadow)"> | |||
| <rect x="605" y="718" width="590" height="132" rx="20" fill="#173F69"/> | |||
| </g> | |||
| <text x="900" y="757" text-anchor="middle" class="white-title">PlsrAxis 四轴运行对象</text> | |||
| <text x="900" y="787" text-anchor="middle" class="white-text">状态机 · 当前段 · 运行快照 · 计数模式 · 停止原因</text> | |||
| <text x="900" y="815" text-anchor="middle" class="white-text">64位逻辑位置 · 任务累计 · 总累计 · 事件序号</text> | |||
| <!-- 实时硬件数据路径 --> | |||
| <path d="M1080 650 L1120 650 L1120 690 L1210 690 L1210 762 L1217 762" class="orange"/> | |||
| <rect x="1225" y="718" width="245" height="88" rx="14" fill="#FFF2E8" stroke="#E29A65" stroke-width="2"/> | |||
| <text x="1347" y="750" text-anchor="middle" class="box-title">TIM6 控制刷新</text> | |||
| <text x="1347" y="777" text-anchor="middle" class="box-text">0.1 ms / 1 ms</text> | |||
| <path d="M1347 718 L1347 704 L1575 704 L1575 718" class="orange"/> | |||
| <rect x="1480" y="718" width="250" height="88" rx="14" fill="#FFF2E8" stroke="#E29A65" stroke-width="2"/> | |||
| <text x="1605" y="750" text-anchor="middle" class="box-title">PWM输出定时器</text> | |||
| <text x="1605" y="777" text-anchor="middle" class="box-text">TIM10/13/11/14</text> | |||
| <path d="M1605 806 L1605 821" class="orange"/> | |||
| <rect x="1435" y="830" width="295" height="90" rx="14" fill="#FFF2E8" stroke="#E29A65" stroke-width="2"/> | |||
| <text x="1582" y="859" text-anchor="middle" class="box-title">脉冲累计与ISR事件</text> | |||
| <text x="1582" y="885" text-anchor="middle" class="box-text">TIM9/TIM12硬件计数优先</text> | |||
| <text x="1582" y="908" text-anchor="middle" class="box-text">输出定时器软件计数兜底</text> | |||
| <path d="M1435 875 L1220 875 L1220 784 L1203 784" class="orange"/> | |||
| <text x="1260" y="864" class="small">块累计 / 最终比较 / 故障事件</text> | |||
| <!-- 状态与持久化 --> | |||
| <path d="M780 850 L780 927" class="green"/> | |||
| <rect x="80" y="936" width="390" height="105" rx="16" fill="#F0FAF4" stroke="#7DB899" stroke-width="2"/> | |||
| <text x="275" y="970" text-anchor="middle" class="box-title">Flash / Backup SRAM</text> | |||
| <text x="275" y="998" text-anchor="middle" class="box-text">固定配置 · K0 · 位置与累计检查点</text> | |||
| <text x="275" y="1023" text-anchor="middle" class="box-text">复位后不自动恢复运动</text> | |||
| <path d="M650 850 L650 900 L275 900 L275 927" class="green"/> | |||
| <rect x="600" y="936" width="360" height="105" rx="16" fill="#F0FAF4" stroke="#7DB899" stroke-width="2"/> | |||
| <text x="780" y="970" text-anchor="middle" class="box-title">PlsrStatusPublish</text> | |||
| <text x="780" y="998" text-anchor="middle" class="box-text">版本化一致快照</text> | |||
| <text x="780" y="1023" text-anchor="middle" class="box-text">状态 · 频率 · 位置 · 计数 · 错误</text> | |||
| <path d="M960 988 L1000 988" class="purple"/> | |||
| <rect x="1010" y="936" width="350" height="105" rx="16" fill="#F5F0FC" stroke="#A995C7" stroke-width="2"/> | |||
| <text x="1185" y="970" text-anchor="middle" class="box-title">固定运行状态</text> | |||
| <text x="1185" y="998" text-anchor="middle" class="box-text">HSD0~15 · SM · SD</text> | |||
| <text x="1185" y="1023" text-anchor="middle" class="box-text">I6000~6399段事件</text> | |||
| <path d="M1360 988 L1400 988" class="purple"/> | |||
| <rect x="1410" y="936" width="320" height="105" rx="16" fill="#F5F0FC" stroke="#A995C7" stroke-width="2"/> | |||
| <text x="1570" y="970" text-anchor="middle" class="box-title">外部读取</text> | |||
| <text x="1570" y="998" text-anchor="middle" class="box-text">Modbus RTU · 内部API</text> | |||
| <text x="1570" y="1023" text-anchor="middle" class="box-text">读取一致状态,不直接写运行对象</text> | |||
| <!-- 动态命令到状态机 --> | |||
| <path d="M1435 218 L1435 270 L1240 270 L1240 755 L1203 755" class="purple dash"/> | |||
| <text x="1255" y="293" class="small">运行命令与条件变量</text> | |||
| <!-- 图例 --> | |||
| <circle cx="90" cy="1073" r="6" fill="#3478B9"/> | |||
| <text x="105" y="1078" class="small">配置与运动数据</text> | |||
| <circle cx="265" cy="1073" r="6" fill="#D96F1E"/> | |||
| <text x="280" y="1078" class="small">硬实时数据与事件</text> | |||
| <circle cx="470" cy="1073" r="6" fill="#398760"/> | |||
| <text x="485" y="1078" class="small">持久化数据</text> | |||
| <circle cx="615" cy="1073" r="6" fill="#7759A7"/> | |||
| <text x="630" y="1078" class="small">状态/命令接口</text> | |||
| </svg> | |||
| @@ -0,0 +1,166 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <svg xmlns="http://www.w3.org/2000/svg" width="1800" height="1100" viewBox="0 0 1800 1100" role="img" aria-labelledby="title desc"> | |||
| <title id="title">PLSR高速脉冲与硬件计数架构图</title> | |||
| <desc id="desc">展示TIM6控制刷新、四路PWM输出、TIM9和TIM12硬件计数二选一连接、软件计数兜底以及最终完整边沿关闭流程。</desc> | |||
| <defs> | |||
| <linearGradient id="bg" x1="0" y1="0" x2="0" y2="1"> | |||
| <stop offset="0" stop-color="#F8FBFF"/> | |||
| <stop offset="1" stop-color="#EEF3F9"/> | |||
| </linearGradient> | |||
| <filter id="shadow" x="-12%" y="-12%" width="124%" height="135%"> | |||
| <feDropShadow dx="0" dy="5" stdDeviation="7" flood-color="#183A61" flood-opacity="0.14"/> | |||
| </filter> | |||
| <marker id="blueArrow" markerWidth="11" markerHeight="11" refX="9" refY="5.5" orient="auto"> | |||
| <path d="M0,0 L11,5.5 L0,11 z" fill="#3478B9"/> | |||
| </marker> | |||
| <marker id="orangeArrow" markerWidth="11" markerHeight="11" refX="9" refY="5.5" orient="auto"> | |||
| <path d="M0,0 L11,5.5 L0,11 z" fill="#D96F1E"/> | |||
| </marker> | |||
| <marker id="purpleArrow" markerWidth="11" markerHeight="11" refX="9" refY="5.5" orient="auto"> | |||
| <path d="M0,0 L11,5.5 L0,11 z" fill="#7357A5"/> | |||
| </marker> | |||
| <marker id="greenArrow" markerWidth="11" markerHeight="11" refX="9" refY="5.5" orient="auto"> | |||
| <path d="M0,0 L11,5.5 L0,11 z" fill="#398760"/> | |||
| </marker> | |||
| <style> | |||
| .title { font: 700 34px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #17385F; } | |||
| .subtitle { font: 400 16px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #5E7187; } | |||
| .layer { font: 700 21px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #17385F; } | |||
| .box-title { font: 700 18px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #17385F; } | |||
| .box-text { font: 400 14px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #445C75; } | |||
| .small { font: 400 13px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #5A6E83; } | |||
| .white-title { font: 700 19px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #FFFFFF; } | |||
| .white-text { font: 400 14px "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; fill: #F4F8FD; } | |||
| .blue { fill: none; stroke: #3478B9; stroke-width: 3; marker-end: url(#blueArrow); } | |||
| .orange { fill: none; stroke: #D96F1E; stroke-width: 3; marker-end: url(#orangeArrow); } | |||
| .purple { fill: none; stroke: #7357A5; stroke-width: 3; marker-end: url(#purpleArrow); } | |||
| .green { fill: none; stroke: #398760; stroke-width: 3; marker-end: url(#greenArrow); } | |||
| .dash { stroke-dasharray: 8 7; } | |||
| </style> | |||
| </defs> | |||
| <rect width="1800" height="1100" fill="url(#bg)"/> | |||
| <text x="900" y="50" text-anchor="middle" class="title">PLSR 高速脉冲与硬件计数架构图</text> | |||
| <text x="900" y="78" text-anchor="middle" class="subtitle">硬件PWM维持边沿 · TIM9/TIM12硬件计数优先 · 软件周期计数兜底</text> | |||
| <!-- 控制配置 --> | |||
| <g filter="url(#shadow)"> | |||
| <rect x="90" y="105" width="1620" height="120" rx="20" fill="#FFFFFF" stroke="#A8C4E0" stroke-width="2"/> | |||
| </g> | |||
| <text x="125" y="140" class="layer">控制与配置</text> | |||
| <rect x="330" y="130" width="330" height="70" rx="14" fill="#EAF4FE" stroke="#94B9DD"/> | |||
| <text x="495" y="158" text-anchor="middle" class="box-title">PlsrProfile 速度规划</text> | |||
| <text x="495" y="183" text-anchor="middle" class="box-text">命令频率 · PSC/ARR/CCR</text> | |||
| <rect x="735" y="130" width="330" height="70" rx="14" fill="#EAF4FE" stroke="#94B9DD"/> | |||
| <text x="900" y="158" text-anchor="middle" class="box-title">TIM6 控制刷新</text> | |||
| <text x="900" y="183" text-anchor="middle" class="box-text">0.1 ms / 1 ms · 安全预装载</text> | |||
| <rect x="1140" y="130" width="330" height="70" rx="14" fill="#EAF4FE" stroke="#94B9DD"/> | |||
| <text x="1305" y="158" text-anchor="middle" class="box-title">PlsrResource 资源管理</text> | |||
| <text x="1305" y="183" text-anchor="middle" class="box-text">输出租约 · 计数器租约 · 计数模式</text> | |||
| <path d="M660 165 L727 165" class="blue"/> | |||
| <path d="M1065 165 L1132 165" class="blue"/> | |||
| <!-- PWM输出层 --> | |||
| <text x="95" y="285" class="layer">硬件PWM输出层</text> | |||
| <path d="M900 200 L900 258" class="blue"/> | |||
| <path d="M900 258 L265 258 L265 315" class="blue"/> | |||
| <path d="M900 258 L690 258 L690 315" class="blue"/> | |||
| <path d="M900 258 L1110 258 L1110 315" class="blue"/> | |||
| <path d="M900 258 L1535 258 L1535 315" class="blue"/> | |||
| <g filter="url(#shadow)"> | |||
| <rect x="100" y="325" width="330" height="125" rx="17" fill="#FFF3E9" stroke="#E29A64" stroke-width="2"/> | |||
| <rect x="525" y="325" width="330" height="125" rx="17" fill="#FFF3E9" stroke="#E29A64" stroke-width="2"/> | |||
| <rect x="945" y="325" width="330" height="125" rx="17" fill="#FFF3E9" stroke="#E29A64" stroke-width="2"/> | |||
| <rect x="1370" y="325" width="330" height="125" rx="17" fill="#FFF3E9" stroke="#E29A64" stroke-width="2"/> | |||
| </g> | |||
| <text x="265" y="360" text-anchor="middle" class="box-title">Q0 · PF6</text> | |||
| <text x="265" y="389" text-anchor="middle" class="box-text">TIM10_CH1 · 168 MHz</text> | |||
| <text x="265" y="417" text-anchor="middle" class="box-text">PWM / A相 / CW或CCW</text> | |||
| <text x="690" y="360" text-anchor="middle" class="box-title">Q1 · PF8</text> | |||
| <text x="690" y="389" text-anchor="middle" class="box-text">TIM13_CH1 · 84 MHz</text> | |||
| <text x="690" y="417" text-anchor="middle" class="box-text">PWM / B相 / CW或CCW</text> | |||
| <text x="1110" y="360" text-anchor="middle" class="box-title">Q2 · PF7</text> | |||
| <text x="1110" y="389" text-anchor="middle" class="box-text">TIM11_CH1 · 168 MHz</text> | |||
| <text x="1110" y="417" text-anchor="middle" class="box-text">PWM / A相 / CW或CCW</text> | |||
| <text x="1535" y="360" text-anchor="middle" class="box-title">Q3 · PF9</text> | |||
| <text x="1535" y="389" text-anchor="middle" class="box-text">TIM14_CH1 · 84 MHz</text> | |||
| <text x="1535" y="417" text-anchor="middle" class="box-text">PWM / B相 / CW或CCW</text> | |||
| <!-- 物理输出 --> | |||
| <path d="M265 450 L265 490" class="orange"/> | |||
| <path d="M690 450 L690 490" class="orange"/> | |||
| <path d="M1110 450 L1110 490" class="orange"/> | |||
| <path d="M1535 450 L1535 490" class="orange"/> | |||
| <rect x="100" y="500" width="1600" height="60" rx="15" fill="#273D55"/> | |||
| <text x="900" y="526" text-anchor="middle" class="white-title">物理高速输出 Q0~Q3</text> | |||
| <text x="900" y="550" text-anchor="middle" class="white-text">PULSE/DIR:上升沿计数 · AB相:完整四状态周期计数 · CW/CCW:当前方向通道计数</text> | |||
| <!-- 内部计数连接 --> | |||
| <text x="95" y="620" class="layer">内部硬件计数与软件兜底</text> | |||
| <path d="M265 450 L265 590 L440 590 L440 665" class="purple"/> | |||
| <path d="M1110 450 L1110 590 L620 590 L620 665" class="purple"/> | |||
| <path d="M690 450 L690 610 L1120 610 L1120 665" class="purple"/> | |||
| <path d="M1535 450 L1535 610 L1300 610 L1300 665" class="purple"/> | |||
| <text x="305" y="582" class="small">TIM10_OC · ITR2</text> | |||
| <text x="935" y="582" class="small">TIM11_OC · ITR3</text> | |||
| <text x="735" y="603" class="small">TIM13_OC · ITR2</text> | |||
| <text x="1390" y="603" class="small">TIM14_OC · ITR3</text> | |||
| <g filter="url(#shadow)"> | |||
| <rect x="280" y="675" width="500" height="145" rx="19" fill="#F3EEFC" stroke="#9F88C4" stroke-width="2"/> | |||
| <rect x="1020" y="675" width="500" height="145" rx="19" fill="#F3EEFC" stroke="#9F88C4" stroke-width="2"/> | |||
| </g> | |||
| <text x="530" y="712" text-anchor="middle" class="box-title">TIM9 · 16位硬件累计</text> | |||
| <text x="530" y="741" text-anchor="middle" class="box-text">Q0/TIM10 或 Q2/TIM11 二选一</text> | |||
| <text x="530" y="768" text-anchor="middle" class="box-text">External Clock Mode 1 · 65536脉冲块</text> | |||
| <text x="530" y="795" text-anchor="middle" class="box-text">100 kHz时块中断约1.53次/秒</text> | |||
| <text x="1270" y="712" text-anchor="middle" class="box-title">TIM12 · 16位硬件累计</text> | |||
| <text x="1270" y="741" text-anchor="middle" class="box-text">Q1/TIM13 或 Q3/TIM14 二选一</text> | |||
| <text x="1270" y="768" text-anchor="middle" class="box-text">External Clock Mode 1 · 65536脉冲块</text> | |||
| <text x="1270" y="795" text-anchor="middle" class="box-text">100 kHz时块中断约1.53次/秒</text> | |||
| <rect x="660" y="850" width="480" height="88" rx="16" fill="#FFF8E9" stroke="#D9AD50" stroke-width="2"/> | |||
| <text x="900" y="882" text-anchor="middle" class="box-title">软件周期计数兜底</text> | |||
| <text x="900" y="909" text-anchor="middle" class="box-text">计数器不可达或被占用时,启用对应PWM定时器更新中断</text> | |||
| <path d="M530 820 L530 840 L652 840 L652 872" class="purple dash"/> | |||
| <path d="M1270 820 L1270 840 L1148 840 L1148 872" class="purple dash"/> | |||
| <text x="900" y="837" text-anchor="middle" class="small">未获得租约的轴自动回退,不拒绝合法任务</text> | |||
| <!-- 计数结果与尾停 --> | |||
| <path d="M530 820 L530 962 L680 962" class="green"/> | |||
| <path d="M1270 820 L1270 962 L1120 962" class="green"/> | |||
| <path d="M900 938 L900 952" class="green"/> | |||
| <g filter="url(#shadow)"> | |||
| <rect x="690" y="958" width="420" height="88" rx="17" fill="#EAF8F0" stroke="#73B18F" stroke-width="2"/> | |||
| </g> | |||
| <text x="900" y="990" text-anchor="middle" class="box-title">目标数量到达与最终完整边沿</text> | |||
| <text x="900" y="1017" text-anchor="middle" class="box-text">PULSE/CW/CCW:末脉冲下降沿 · AB:第四次状态跳变</text> | |||
| <path d="M1110 1002 L1280 1002" class="green"/> | |||
| <rect x="1290" y="958" width="400" height="88" rx="17" fill="#EAF8F0" stroke="#73B18F" stroke-width="2"/> | |||
| <text x="1490" y="990" text-anchor="middle" class="box-title">安全关闭并发布完成事件</text> | |||
| <text x="1490" y="1017" text-anchor="middle" class="box-text">关闭通道 · 释放租约 · 更新位置和段状态</text> | |||
| <!-- 图例 --> | |||
| <circle cx="100" cy="1075" r="6" fill="#3478B9"/> | |||
| <text x="115" y="1080" class="small">控制配置</text> | |||
| <circle cx="230" cy="1075" r="6" fill="#D96F1E"/> | |||
| <text x="245" y="1080" class="small">物理脉冲</text> | |||
| <circle cx="355" cy="1075" r="6" fill="#7357A5"/> | |||
| <text x="370" y="1080" class="small">计数连接/回退</text> | |||
| <circle cx="535" cy="1075" r="6" fill="#398760"/> | |||
| <text x="550" y="1080" class="small">完成与状态事件</text> | |||
| </svg> | |||
| @@ -66,17 +66,6 @@ typedef struct | |||
| uint32_t droppedFrameCount; ///< 接收槽占用或长度错误导致的丢帧数 | |||
| uint32_t uartErrorCount; ///< HAL 串口错误计数 | |||
| } MODBUS_SLAVE_STATS; | |||
| /** | |||
| * @brief 一次Modbus RTU通信的历史记录 | |||
| */ | |||
| typedef struct | |||
| { | |||
| uint32_t tick; ///< 通信发生时的系统时间 | |||
| uint16_t requestLength; ///< 请求ADU长度 | |||
| uint16_t responseLength; ///< 响应ADU长度 | |||
| uint8_t request[256]; ///< 请求ADU数据 | |||
| uint8_t response[256]; ///< 响应ADU数据 | |||
| } MODBUS_HISTORY_ITEM; | |||
| /** @brief Modbus 从站通信统计数据 */ | |||
| extern volatile MODBUS_SLAVE_STATS ModbusSlaveStatistics; | |||
| /** | |||
| @@ -153,7 +142,6 @@ uint8_t ModbusSlaveGetCoil(uint16_t address, uint8_t *state); | |||
| uint8_t ModbusSlaveIsConnected(uint32_t timeoutMs); | |||
| void ModbusRetainedRegistersLoad(void); | |||
| void ModbusRetainedRegistersPoll(void); | |||
| void HistorySaveToRegisters(void); | |||
| #ifdef __cplusplus | |||
| } | |||
| @@ -1,5 +1,4 @@ | |||
| #include "modbus_rtu_slave.h" | |||
| #include "ucos_ii.h" | |||
| #include <string.h> | |||
| #define MODBUS_RTU_ADU_SIZE_MAX (256U) // Modbus RTU 最大 ADU 长度,单位为字节 | |||
| @@ -25,12 +24,7 @@ | |||
| static UART_HandleTypeDef *ModbusUart; | |||
| static uint8_t ModbusSlaveAddress; | |||
| static void ModbusHistorySave(uint32_t tick, const uint8_t *request, | |||
| uint16_t requestLength, const uint8_t *response, | |||
| uint16_t responseLength); | |||
| static HAL_StatusTypeDef ModbusStartReceive(void); | |||
| static MODBUS_HISTORY_ITEM ModbusHistory[16]; | |||
| static uint16_t ModbusHistoryWriteIndex; | |||
| /*最近一次字节尾到字节头的间隔周期数*/ | |||
| static volatile uint32_t ModbusLastInterFrameGapCycles; | |||
| /** | |||
| @@ -457,73 +451,6 @@ tx: | |||
| return (uint16_t)(5U + quantity * 2U); | |||
| } | |||
| /* | |||
| * 返回帧 | |||
| *从站地址 | 14 | 总字节数 | 子响应长度 | 06 | 寄存器数据 | CRC | |||
| * | |||
| * 01 从站地址 | |||
| * 14 功能码 | |||
| * 06 后续响应数据共6字节 | |||
| * 05 当前子响应长度,共5字节 | |||
| * 06 引用类型 | |||
| * 12 34 第一个寄存器 | |||
| * 56 78 第二个寄存器 | |||
| */ | |||
| static uint16_t ModbusProcessFile(const uint8_t *request, | |||
| uint16_t requestLength) | |||
| { | |||
| uint16_t fileNumber; | |||
| uint16_t recordNumber; | |||
| uint16_t quantity; | |||
| uint16_t index; | |||
| uint16_t value; | |||
| uint16_t *historyRegister; | |||
| if (requestLength != 12U) | |||
| { | |||
| ModbusSlaveStatistics.illegalValueCount++; | |||
| return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE); | |||
| } | |||
| if ((request[2] != 7U) || (request[3] != 0x06U)) | |||
| { | |||
| ModbusSlaveStatistics.illegalValueCount++; | |||
| return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE); | |||
| } | |||
| quantity = ModbusGetU16Be(&request[8]); | |||
| fileNumber = ModbusGetU16Be(&request[4]); | |||
| recordNumber = ModbusGetU16Be(&request[6]); | |||
| if ((quantity == 0U) || (quantity > 124U)) // 数量0或者数量大于最大值 | |||
| { | |||
| ModbusSlaveStatistics.illegalValueCount++; | |||
| return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE); | |||
| } | |||
| /* 检查文件号、记录号和读取范围 */ | |||
| if ((fileNumber > 15U) || (recordNumber >= 260U) | |||
| || (((uint32_t)recordNumber + quantity) > 260UL)) | |||
| { | |||
| ModbusSlaveStatistics.illegalAddressCount++; | |||
| return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS); | |||
| } | |||
| ModbusTxFrame[0] = ModbusSlaveAddress; | |||
| ModbusTxFrame[1] = 0X14; | |||
| ModbusTxFrame[2] = (uint8_t)(2U + quantity * 2U); | |||
| ModbusTxFrame[3] = (uint8_t)(1U + quantity * 2U); | |||
| ModbusTxFrame[4] = 0X06; | |||
| historyRegister = (uint16_t *)ModbusHistory; | |||
| for (index = 0U; index < quantity; index++) | |||
| { | |||
| uint16_t address = fileNumber * 260 + recordNumber + index; | |||
| value = *(historyRegister + address); | |||
| ModbusTxFrame[5U + index * 2U] = (uint8_t)(value & 0x00FFU); | |||
| ModbusTxFrame[6U + index * 2U] = (uint8_t)(value >> 8U); | |||
| } | |||
| ModbusAppendCrc(ModbusTxFrame, (uint16_t)(5U + quantity * 2U)); | |||
| return (uint16_t)(7U + quantity * 2U); | |||
| } | |||
| /** | |||
| * @brief 处理写单个保持寄存器功能码 0x06 | |||
| * @param[in] request RTU 请求帧 | |||
| @@ -893,11 +820,6 @@ static uint16_t ModbusProcessRequest(const uint8_t *request, | |||
| return (isBroadcast != 0U) | |||
| ? 0U | |||
| : ModbusProcessReadHolding(request, requestLength); | |||
| case 0x14U: | |||
| return (isBroadcast != 0U) | |||
| ? 0U | |||
| : ModbusProcessFile(request, requestLength); | |||
| case 0x05U: // 写单个线圈 | |||
| return ModbusProcessWriteSingleCoil(request, requestLength, | |||
| isBroadcast); | |||
| @@ -959,9 +881,6 @@ void ModbusSlavePoll(void) | |||
| } | |||
| responseLength = ModbusProcessRequest(ModbusRxFrame, ModbusRxFrameLength); | |||
| ModbusHistorySave(OSTimeGet() / 1000, ModbusRxFrame, ModbusRxFrameLength, | |||
| ModbusTxFrame, responseLength); | |||
| if (responseLength > 0U) | |||
| { | |||
| ModbusTxBusy = 1U; | |||
| @@ -1192,57 +1111,3 @@ void ModbusRetainedRegistersPoll(void) | |||
| ModbusRetainedSnapshot[index] = value; | |||
| } | |||
| } | |||
| static void ModbusHistorySave(uint32_t tick, const uint8_t *request, | |||
| uint16_t requestLength, const uint8_t *response, | |||
| uint16_t responseLength) | |||
| { | |||
| MODBUS_HISTORY_ITEM *item; | |||
| if (requestLength > 256U) | |||
| { | |||
| requestLength = 256U; | |||
| } | |||
| if (responseLength > 256U) | |||
| { | |||
| responseLength = 256U; | |||
| } | |||
| item = &ModbusHistory[ModbusHistoryWriteIndex]; | |||
| item->tick = tick; | |||
| item->requestLength = requestLength; | |||
| item->responseLength = responseLength; | |||
| (void)memcpy(item->request, request, requestLength); | |||
| if (responseLength > 0U) | |||
| { | |||
| (void)memcpy(item->response, response, responseLength); | |||
| } | |||
| ModbusHistoryWriteIndex++; | |||
| if (ModbusHistoryWriteIndex >= 16U) | |||
| { | |||
| ModbusHistoryWriteIndex = 0U; | |||
| } | |||
| } | |||
| void HistorySaveToRegisters(void) | |||
| { | |||
| uint16_t index; | |||
| if (ModbusHistoryWriteIndex == 0U) | |||
| { | |||
| index = 15U; | |||
| } | |||
| else | |||
| { | |||
| index = ModbusHistoryWriteIndex - 1U; | |||
| } | |||
| (void)memcpy(&ModbusHoldingRegisters[1000U + index * 260U], | |||
| &ModbusHistory[index], sizeof(ModbusHistory[index])); | |||
| } | |||