198 lines
11 KiB
Python
198 lines
11 KiB
Python
import xml.etree.ElementTree as ET
|
||
|
||
def make_mxfile():
|
||
# 基础参数
|
||
y_cs_high, y_cs_low = 80, 110
|
||
y_sclk_high, y_sclk_low = 180, 210
|
||
y_mosi_high, y_mosi_low = 280, 310
|
||
y_miso_high, y_miso_low = 380, 410
|
||
|
||
# 8个bit周期,每个100px,从x=100开始,到x=900结束
|
||
rise_edges = [150, 250, 350, 450, 550, 650, 750, 850]
|
||
fall_edges = [190, 290, 390, 490, 590, 690, 790, 890]
|
||
|
||
# MOSI: 0x5A = 0b01011010, MSB first
|
||
mosi_bits = [0, 1, 0, 1, 1, 0, 1, 0]
|
||
# MISO: 0xA5 = 0b10100101, MSB first
|
||
miso_bits = [1, 0, 1, 0, 0, 1, 0, 1]
|
||
|
||
lines = []
|
||
lines.append('<?xml version="1.0" encoding="UTF-8"?>')
|
||
lines.append('<mxfile host="app.diagrams.net" modified="2024-05-25T00:00:00.000Z" agent="AI" version="21.0.0" type="device">')
|
||
lines.append(' <diagram name="SPI Waveform" id="spi_waveform">')
|
||
lines.append(' <mxGraphModel dx="1000" dy="600" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1000" pageHeight="600" math="0" shadow="0">')
|
||
lines.append(' <root>')
|
||
lines.append(' <mxCell id="0"/>')
|
||
lines.append(' <mxCell id="1" parent="0"/>')
|
||
|
||
# 背景网格
|
||
lines.append(' <mxCell id="grid" value="" style="points=[];gridColor=#e0e0e0;gridSize=20;spacingTop=20;spacingLeft=20;spacingBottom=20;spacingRight=20;html=1;" vertex="1" parent="1">')
|
||
lines.append(' <mxGeometry x="0" y="0" width="1000" height="600" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# 标题
|
||
lines.append(' <mxCell id="title" value="SPI 全双工传输 (模式 0: CPOL=0, CPHA=0)" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=16;fontStyle=1;" vertex="1" parent="1">')
|
||
lines.append(' <mxGeometry x="300" y="20" width="400" height="30" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# MSB / LSB
|
||
lines.append(' <mxCell id="msb" value="MSB" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=12;fontColor=#666666;fontStyle=1;" vertex="1" parent="1">')
|
||
lines.append(' <mxGeometry x="130" y="60" width="40" height="20" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
lines.append(' <mxCell id="lsb" value="LSB" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=12;fontColor=#666666;fontStyle=1;" vertex="1" parent="1">')
|
||
lines.append(' <mxGeometry x="880" y="60" width="40" height="20" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# 信号标签
|
||
for label, y, eid in [("CS", 85, "label_cs"), ("SCLK", 185, "label_sclk"),
|
||
("MOSI", 275, "label_mosi"), ("MISO", 365, "label_miso")]:
|
||
lines.append(f' <mxCell id="{eid}" value="{label}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontStyle=1;" vertex="1" parent="1">')
|
||
lines.append(f' <mxGeometry x="20" y="{y}" width="50" height="20" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# 垂直参考虚线(放在每个上升沿采样点)
|
||
for i, x in enumerate(rise_edges):
|
||
lines.append(f' <mxCell id="vl_{x}" value="" style="endArrow=none;html=1;strokeWidth=1;strokeColor=#CCCCCC;dashed=1;" edge="1" parent="1">')
|
||
lines.append(' <mxGeometry width="50" height="50" relative="1" as="geometry">')
|
||
lines.append(f' <mxPoint x="{x}" y="180" as="sourcePoint"/>')
|
||
lines.append(f' <mxPoint x="{x}" y="420" as="targetPoint"/>')
|
||
lines.append(' </mxGeometry>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# CS 波形
|
||
cs_points = [
|
||
(80, y_cs_high), (90, y_cs_high), (90, y_cs_low), (910, y_cs_low),
|
||
(910, y_cs_high), (920, y_cs_high)
|
||
]
|
||
lines.append(' <mxCell id="cs" value="" style="edgeStyle=none;html=1;strokeWidth=2;strokeColor=#FF5722;rounded=0;endArrow=none;" edge="1" parent="1">')
|
||
lines.append(' <mxGeometry relative="0" as="geometry">')
|
||
lines.append(f' <mxPoint x="{cs_points[0][0]}" y="{cs_points[0][1]}" as="sourcePoint"/>')
|
||
lines.append(f' <mxPoint x="{cs_points[-1][0]}" y="{cs_points[-1][1]}" as="targetPoint"/>')
|
||
lines.append(' <Array as="points">')
|
||
for x, y in cs_points[1:-1]:
|
||
lines.append(f' <mxPoint x="{x}" y="{y}"/>')
|
||
lines.append(' </Array>')
|
||
lines.append(' </mxGeometry>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# SCLK 波形
|
||
sclk_pts = [(80, y_sclk_low), (100, y_sclk_low)]
|
||
for rise, fall in zip(rise_edges, fall_edges):
|
||
sclk_pts.extend([
|
||
(rise, y_sclk_low),
|
||
(rise, y_sclk_high),
|
||
(fall, y_sclk_high),
|
||
(fall, y_sclk_low),
|
||
])
|
||
sclk_pts.append((920, y_sclk_low))
|
||
|
||
lines.append(' <mxCell id="sclk" value="" style="edgeStyle=none;html=1;strokeWidth=2;strokeColor=#2196F3;rounded=0;endArrow=none;" edge="1" parent="1">')
|
||
lines.append(' <mxGeometry relative="0" as="geometry">')
|
||
lines.append(f' <mxPoint x="{sclk_pts[0][0]}" y="{sclk_pts[0][1]}" as="sourcePoint"/>')
|
||
lines.append(f' <mxPoint x="{sclk_pts[-1][0]}" y="{sclk_pts[-1][1]}" as="targetPoint"/>')
|
||
lines.append(' <Array as="points">')
|
||
for x, y in sclk_pts[1:-1]:
|
||
lines.append(f' <mxPoint x="{x}" y="{y}"/>')
|
||
lines.append(' </Array>')
|
||
lines.append(' </mxGeometry>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# MOSI 波形
|
||
mosi_start_y = y_mosi_high if mosi_bits[0] else y_mosi_low
|
||
mosi_pts = [(80, mosi_start_y), (100, mosi_start_y)]
|
||
for i, bit in enumerate(mosi_bits):
|
||
y_level = y_mosi_high if bit else y_mosi_low
|
||
end_x = fall_edges[i] if i < len(fall_edges) else 900
|
||
mosi_pts.append((end_x, y_level))
|
||
if i + 1 < len(mosi_bits):
|
||
y_next = y_mosi_high if mosi_bits[i+1] else y_mosi_low
|
||
if y_next != y_level:
|
||
mosi_pts.append((end_x, y_next))
|
||
mosi_pts.append((920, mosi_pts[-1][1]))
|
||
|
||
lines.append(' <mxCell id="mosi" value="" style="edgeStyle=none;html=1;strokeWidth=2;strokeColor=#4CAF50;rounded=0;endArrow=none;" edge="1" parent="1">')
|
||
lines.append(' <mxGeometry relative="0" as="geometry">')
|
||
lines.append(f' <mxPoint x="{mosi_pts[0][0]}" y="{mosi_pts[0][1]}" as="sourcePoint"/>')
|
||
lines.append(f' <mxPoint x="{mosi_pts[-1][0]}" y="{mosi_pts[-1][1]}" as="targetPoint"/>')
|
||
lines.append(' <Array as="points">')
|
||
for x, y in mosi_pts[1:-1]:
|
||
lines.append(f' <mxPoint x="{x}" y="{y}"/>')
|
||
lines.append(' </Array>')
|
||
lines.append(' </mxGeometry>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# MISO 波形 — 修复:sourcePoint 与第一个中间点同 y,避免斜线
|
||
miso_start_y = y_miso_high if miso_bits[0] else y_miso_low
|
||
miso_pts = [(80, miso_start_y), (100, miso_start_y)]
|
||
for i, bit in enumerate(miso_bits):
|
||
y_level = y_miso_high if bit else y_miso_low
|
||
end_x = fall_edges[i] if i < len(fall_edges) else 900
|
||
miso_pts.append((end_x, y_level))
|
||
if i + 1 < len(miso_bits):
|
||
y_next = y_miso_high if miso_bits[i+1] else y_miso_low
|
||
if y_next != y_level:
|
||
miso_pts.append((end_x, y_next))
|
||
miso_pts.append((920, miso_pts[-1][1]))
|
||
|
||
lines.append(' <mxCell id="miso" value="" style="edgeStyle=none;html=1;strokeWidth=2;strokeColor=#9C27B0;rounded=0;endArrow=none;" edge="1" parent="1">')
|
||
lines.append(' <mxGeometry relative="0" as="geometry">')
|
||
lines.append(f' <mxPoint x="{miso_pts[0][0]}" y="{miso_pts[0][1]}" as="sourcePoint"/>')
|
||
lines.append(f' <mxPoint x="{miso_pts[-1][0]}" y="{miso_pts[-1][1]}" as="targetPoint"/>')
|
||
lines.append(' <Array as="points">')
|
||
for x, y in miso_pts[1:-1]:
|
||
lines.append(f' <mxPoint x="{x}" y="{y}"/>')
|
||
lines.append(' </Array>')
|
||
lines.append(' </mxGeometry>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# MOSI 数据位标签
|
||
mosi_centers = [145, 240, 340, 440, 540, 640, 740, 840]
|
||
for i, (bit, cx) in enumerate(zip(mosi_bits, mosi_centers)):
|
||
lines.append(f' <mxCell id="m{i}" value="{bit}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=12;fontColor=#4CAF50;" vertex="1" parent="1">')
|
||
lines.append(f' <mxGeometry x="{cx}" y="255" width="20" height="20" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# MISO 数据位标签
|
||
miso_centers = [145, 240, 340, 440, 540, 640, 740, 840]
|
||
for i, (bit, cx) in enumerate(zip(miso_bits, miso_centers)):
|
||
lines.append(f' <mxCell id="s{i}" value="{bit}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=12;fontColor=#9C27B0;" vertex="1" parent="1">')
|
||
lines.append(f' <mxGeometry x="{cx}" y="355" width="20" height="20" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# 数据包标注
|
||
lines.append(' <mxCell id="hex_mosi" value="0x5A" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontStyle=1;fontColor=#4CAF50;" vertex="1" parent="1">')
|
||
lines.append(' <mxGeometry x="500" y="235" width="80" height="20" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
lines.append(' <mxCell id="hex_miso" value="0xA5" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontStyle=1;fontColor=#9C27B0;" vertex="1" parent="1">')
|
||
lines.append(' <mxGeometry x="500" y="425" width="80" height="20" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
|
||
lines.append(' </root>')
|
||
lines.append(' </mxGraphModel>')
|
||
lines.append(' </diagram>')
|
||
lines.append('</mxfile>')
|
||
|
||
return "\n".join(lines)
|
||
|
||
xml_content = make_mxfile()
|
||
output_path = "SPI_Waveform_CPHA0_Corrected.drawio"
|
||
with open(output_path, "w", encoding="utf-8") as f:
|
||
f.write(xml_content)
|
||
|
||
# 验证 MISO 起始点
|
||
import re
|
||
with open(output_path, "r", encoding="utf-8") as f:
|
||
content = f.read()
|
||
|
||
match = re.search(r'id="miso".*?<Array as="points">(.*?)</Array>', content, re.DOTALL)
|
||
if match:
|
||
pts = re.findall(r'<mxPoint x="(\d+)" y="(\d+)"/>', match.group(1))
|
||
print("MISO 点:")
|
||
for x, y in pts[:4]:
|
||
print(f" ({x}, {y})")
|
||
src = re.search(r'id="miso".*?<mxPoint x="(\d+)" y="(\d+)" as="sourcePoint"/>', content)
|
||
if src:
|
||
print(f"sourcePoint: ({src.group(1)}, {src.group(2)})")
|
||
|
||
print("\n文件已保存:", output_path)
|