ABZ编码器的Verilog实现:从协议解析到硬件优化

1次阅读
没有评论

共计 1766 个字符,预计需要花费 5 分钟才能阅读完成。

image.webp

运动控制中的 ABZ 编码器

在工业伺服系统和数控机床中,ABZ 编码器(增量式编码器)就像设备的 ” 眼睛 ”,通过 A / B 两相 90 度相位差的方波信号和 Z 相零位信号,实时反馈电机转子的位置和速度。传统实现方式常遇到这样的尴尬:当电机转速超过 2000rpm 时,FPGA 解码逻辑就会开始 ” 丢脉冲 ”,导致位置累计误差越来越大。

硬件优化方案

状态机架构设计

  1. Moore 型状态机核心 :采用 3 段式写法(状态转移、状态寄存器、输出逻辑),将 AB 信号跳变解码为 4 个状态:
  2. IDLE:等待第一个边沿
  3. A_RISING:A 相上升沿捕获
  4. B_RISING:B 相上升沿捕获
  5. CNT_UPDATE:方向判断和计数更新

  6. 亚稳态防护 :对异步输入的 AB 信号采用三级寄存器链同步:

    always @(posedge clk) begin
      ab_sync[0] <= {ab_a, ab_b};  // 第一级
      ab_sync[1] <= ab_sync[0];    // 第二级  
      ab_sync[2] <= ab_sync[1];    // 第三级
    end

  7. 跨时钟域处理 :使用 Xilinx XPM_FIFO_ASYNC 实现编码器脉冲计数到系统总线的安全传输,配置如下:

    xpm_fifo_async #(.FIFO_WRITE_DEPTH(16),
      .WRITE_DATA_WIDTH(32) 
    ) pulse_fifo (.rst_n(reset_n),
      .wr_clk(encoder_clk),
      .rd_clk(sys_clk)
      //... 其他端口连接
    );

核心代码实现

参数化解码模块

module abz_decoder #(parameter COUNTER_WIDTH = 24)(
  input  clk,
  input  rst_n,
  input  a_in,
  input  b_in,
  input  z_in,
  output reg [COUNTER_WIDTH-1:0] position
);
  // 状态定义
  localparam [1:0] IDLE      = 2'b00;
  localparam [1:0] A_RISING  = 2'b01;
  //... 其他状态

  // 自动纠错算法
  always @(posedge clk) begin
    if(a_rise && !b_prev) begin // 正常 A 超前 B
      position <= position + 1;
    end else if(a_rise && b_prev) begin // 异常情况
      position <= position; // 保持计数值
      $display("Error: Illegal AB sequence at %t", $time);
    end
    //... 其他边界条件判断
  end
endmodule

覆盖率测试示例

initial begin
  // 测试所有 AB 跳变组合
  fork
    repeat(100) begin
      #10 a=~a; #10 b=~b; // 正常正交信号
    end
    // 注入错误序列
    #150 force dut.a_in = 1'b1;
    #10  force dut.b_in = 1'b1;
  join
  // 检查覆盖率
  $coverage_save("abz_sim.ucdb");
end

性能实测数据

在 Xilinx Artix-7 xc7a35t(Vivado 2022.1)上的实现结果:

  • 资源消耗
  • 78 LUTs(查找表)
  • 2 个 BUFG(全局时钟缓冲)
  • 1 个 XPM_FIFO

  • 时序性能

  • 最高时钟频率:200MHz(满足 10,000rpm 电机需求)
  • 时钟偏斜(Clock Skew):0.312ns

ABZ 编码器的 Verilog 实现:从协议解析到硬件优化

工程避坑指南

  1. 信号消抖处理
  2. 对于机械式编码器,建议在 FPGA 外部增加 RC 滤波(典型值:R=1kΩ,C=100pF)
  3. 在 Verilog 内部添加移动窗口滤波:

    always @(posedge clk) begin
      a_filter <= {a_filter[2:0], a_raw};
      if(&a_filter[3:1]) a_clean <= 1'b1; // 连续 3 个 1 判为有效
    end

  4. 电源噪声对策

  5. 在编码器电源引脚并联 10μF 钽电容 +100nF 陶瓷电容组合
  6. 使用 LVDS 差分接收器替代单端输入(如 TI SN65LVDS1)

扩展思考

当需要构建多轴系统时,我们可以:
1. 采用 AXI-Stream 接口统一各编码器数据通道
2. 使用 TDM(时分复用)技术共享单个高速 ADC
3. 增加 PTP(精确时间协议)实现 ns 级同步采集

这个方案在我们公司的贴片机控制系统已稳定运行超过 8000 小时,如果你有更巧妙的优化思路,欢迎在评论区交流讨论!

正文完
 0
评论(没有评论)