import xml.etree.ElementTree as ET def make_qspi_quad_io_mxfile(): # ========== 基础参数定义 ========== # Y坐标(高/低电平):严格遵循垂直间距100px规范 y_cs_high, y_cs_low = 80, 110 y_sclk_high, y_sclk_low = 180, 210 y_io0_high, y_io0_low = 280, 310 y_io1_high, y_io1_low = 380, 410 y_io2_high, y_io2_low = 480, 510 y_io3_high, y_io3_low = 580, 610 # 时序参数:50px/周期,总周期数=8(命令)+3(地址)+2(空周期)+2(数据)=15 cycle_w = 50 start_x = 100 n_cycles = 15 end_x = start_x + n_cycles * cycle_w # 100 + 15*50 = 850 tail_x = end_x + 50 # 900 # 时钟边沿计算(上升沿=采样沿,下降沿=输出沿) rise_edges = [start_x + i*cycle_w + cycle_w//2 for i in range(n_cycles)] # 125,175...825 fall_edges = [start_x + (i+1)*cycle_w for i in range(n_cycles)] # 150,200...850 # ========== 数据定义 ========== # 1. 命令阶段:IO0传输0xEB (11101011),MSB first;IO1-IO3 idle(0) cmd_bits_io0 = [1,1,1,0,1,0,1,1] # 0xEB = b11101011 cmd_bits_io1 = [0]*8 cmd_bits_io2 = [0]*8 cmd_bits_io3 = [0]*8 # 2. 地址阶段:3个周期,4线并行传输地址位(示例地址0x123456的高12位) # 地址0x123456 = 24位:0001 0010 0011 0100 0101 0110 # A23=0,A22=0,A21=0,A20=1 | A19=0,A18=0,A17=1,A16=0 | A15=0,A14=0,A13=1,A12=1 addr_bits_io0 = [0, 0, 0] # A23, A19, A15 addr_bits_io1 = [0, 0, 0] # A22, A18, A14 addr_bits_io2 = [0, 1, 1] # A21, A17, A13 addr_bits_io3 = [1, 0, 1] # A20, A16, A12 # 3. 空周期:2个周期,所有IO idle(0) dummy_bits = [0]*2 # 4. 数据阶段:2个周期传输1字节(0xAA=10101010),4线并行 # D0=1,D1=0,D2=1,D3=0 | D4=1,D5=0,D6=1,D7=0 data_bits_io0 = [1, 1] # D0, D4 data_bits_io1 = [0, 0] # D1, D5 data_bits_io2 = [1, 1] # D2, D6 data_bits_io3 = [0, 0] # D3, D7 # 合并所有阶段的bit序列 all_io0 = cmd_bits_io0 + addr_bits_io0 + dummy_bits + data_bits_io0 all_io1 = cmd_bits_io1 + addr_bits_io1 + dummy_bits + data_bits_io1 all_io2 = cmd_bits_io2 + addr_bits_io2 + dummy_bits + data_bits_io2 all_io3 = cmd_bits_io3 + addr_bits_io3 + dummy_bits + data_bits_io3 # ========== 波形构建函数 ========== def build_wave_pts(bits, y_hi, y_lo): """生成波形点,确保无斜线、边沿对齐""" y0 = y_hi if bits[0] else y_lo pts = [(80, y0), (start_x, y0)] # 起始过渡段 for i in range(len(bits)-1): curr_fall = fall_edges[i] y_curr = y_hi if bits[i] else y_lo y_next = y_hi if bits[i+1] else y_lo # 添加当前bit的水平段终点(下降沿) pts.append((curr_fall, y_curr)) # 电平变化则在下降沿垂直跳变 if y_next != y_curr: pts.append((curr_fall, y_next)) # 最后一个bit的终点 + 尾部过渡段 last_fall = fall_edges[len(bits)-1] pts.append((last_fall, y_hi if bits[-1] else y_lo)) pts.append((tail_x, pts[-1][1])) return pts # ========== 生成XML内容 ========== lines = [] # 文件头 lines.append('') lines.append('') lines.append(' ') lines.append(' ') lines.append(' ') lines.append(' ') lines.append(' ') # 背景网格 lines.append(' ') lines.append(f' ') lines.append(' ') # 标题 lines.append(' ') lines.append(' ') lines.append(' ') # MSB/LSB标记 lines.append(' ') lines.append(f' ') lines.append(' ') lines.append(' ') lines.append(f' ') lines.append(' ') # 信号标签(左侧) signal_labels = [ ("CS", 85, "label_cs"), ("SCLK", 185, "label_sclk"), ("IO0", 275, "label_io0"), ("IO1", 375, "label_io1"), ("IO2", 475, "label_io2"), ("IO3", 575, "label_io3") ] for label, y, eid in signal_labels: lines.append(f' ') lines.append(f' ') lines.append(' ') # 阶段分界虚线(命令/地址/空周期/数据) boundaries = [ (start_x + 8*cycle_w, "cmd_addr"), # 命令结束: 100+400=500 (start_x + 11*cycle_w, "addr_dummy"),# 地址结束: 100+550=650 (start_x + 13*cycle_w, "dummy_data"),# 空周期结束:100+650=750 ] for x, eid in boundaries: lines.append(f' ') lines.append(' ') lines.append(f' ') lines.append(f' ') lines.append(' ') lines.append(' ') # ========== CS 波形 ========== cs_pts = [(80, y_cs_high), (90, y_cs_high), (90, y_cs_low), (tail_x, y_cs_low), (tail_x, y_cs_high), (tail_x+10, y_cs_high)] lines.append(' ') lines.append(' ') lines.append(f' ') lines.append(f' ') lines.append(' ') for x, y in cs_pts[1:-1]: lines.append(f' ') lines.append(' ') lines.append(' ') lines.append(' ') # ========== SCLK 波形 ========== sclk_pts = [(80, y_sclk_low), (start_x, y_sclk_low)] for i in range(n_cycles): rise = rise_edges[i] fall = fall_edges[i] sclk_pts.extend([ (rise, y_sclk_low), (rise, y_sclk_high), (fall, y_sclk_high), (fall, y_sclk_low), ]) sclk_pts.append((tail_x, y_sclk_low)) lines.append(' ') lines.append(' ') lines.append(f' ') lines.append(f' ') lines.append(' ') for x, y in sclk_pts[1:-1]: lines.append(f' ') lines.append(' ') lines.append(' ') lines.append(' ') # ========== IO0 波形 ========== io0_pts = build_wave_pts(all_io0, y_io0_high, y_io0_low) lines.append(' ') lines.append(' ') lines.append(f' ') lines.append(f' ') lines.append(' ') for x, y in io0_pts[1:-1]: lines.append(f' ') lines.append(' ') lines.append(' ') lines.append(' ') # ========== IO1 波形 ========== io1_pts = build_wave_pts(all_io1, y_io1_high, y_io1_low) lines.append(' ') lines.append(' ') lines.append(f' ') lines.append(f' ') lines.append(' ') for x, y in io1_pts[1:-1]: lines.append(f' ') lines.append(' ') lines.append(' ') lines.append(' ') # ========== IO2 波形 ========== io2_pts = build_wave_pts(all_io2, y_io2_high, y_io2_low) lines.append(' ') lines.append(' ') lines.append(f' ') lines.append(f' ') lines.append(' ') for x, y in io2_pts[1:-1]: lines.append(f' ') lines.append(' ') lines.append(' ') lines.append(' ') # ========== IO3 波形 ========== io3_pts = build_wave_pts(all_io3, y_io3_high, y_io3_low) lines.append(' ') lines.append(' ') lines.append(f' ') lines.append(f' ') lines.append(' ') for x, y in io3_pts[1:-1]: lines.append(f' ') lines.append(' ') lines.append(' ') lines.append(' ') # ========== 阶段名称标注 ========== stage_labels = [ (start_x + 4*cycle_w, "命令 (0xEB)", "#4CAF50"), # 命令阶段中心 (start_x + 9.5*cycle_w, "地址 (Quad)", "#666666"), # 地址阶段中心 (start_x + 12*cycle_w, "空周期 (2)", "#666666"), # 空周期中心 (start_x + 14*cycle_w, "数据 (Quad 0xAA)", "#2196F3"), # 数据阶段中心 ] for x, txt, color in stage_labels: lines.append(f' ') lines.append(f' ') lines.append(' ') # ========== 数据位标签 ========== # 1. 命令阶段IO0 bit标签 for i in range(8): cx = rise_edges[i] lines.append(f' ') lines.append(f' ') lines.append(' ') # 2. 地址阶段bit标签 addr_io_labels = [ (8, "A23", "A19", "A15", "#4CAF50"), # IO0 (8, "A22", "A18", "A14", "#9C27B0"), # IO1 (8, "A21", "A17", "A13", "#FFC107"), # IO2 (8, "A20", "A16", "A12", "#607D8B"), # IO3 ] for io_idx, label1, label2, label3, color in addr_io_labels: # 地址阶段第1个周期(总第9个周期) cx = rise_edges[io_idx] lines.append(f' ') lines.append(f' ') lines.append(' ') # 地址阶段第2个周期(总第10个周期) cx = rise_edges[io_idx+1] lines.append(f' ') lines.append(f' ') lines.append(' ') # 地址阶段第3个周期(总第11个周期) cx = rise_edges[io_idx+2] lines.append(f' ') lines.append(f' ') lines.append(' ') # 3. 数据阶段bit标签 data_io_labels = [ (13, "D0", "D4", "#4CAF50"), # IO0 (13, "D1", "D5", "#9C27B0"), # IO1 (13, "D2", "D6", "#FFC107"), # IO2 (13, "D3", "D7", "#607D8B"), # IO3 ] for io_idx, label1, label2, color in data_io_labels: # 数据阶段第1个周期(总第14个周期) cx = rise_edges[io_idx] lines.append(f' ') lines.append(f' ') lines.append(' ') # 数据阶段第2个周期(总第15个周期) cx = rise_edges[io_idx+1] lines.append(f' ') lines.append(f' ') lines.append(' ') # 文件尾 lines.append(' ') lines.append(' ') lines.append(' ') lines.append('') return "\n".join(lines) # 生成并保存文件 xml_content = make_qspi_quad_io_mxfile() output_path = "SPI_Quad.drawio" with open(output_path, "w", encoding="utf-8") as f: f.write(xml_content) print(f"波形图文件已生成: {output_path}")