Verilog IIC器件枚举工具 SI5341 SI5394等器件也可使用
程序如下:
`timescale 1ns / 1ps module i2c_address_scanner ( input wire SYSCLK2_P, // LVDS差分时钟正端 input wire SYSCLK2_N, // LVDS差分时钟负端 output wire SI_RSTBB, // Si5394复位(低有效) output wire I2C_SI5394_SCL, // I2C时钟 inout wire I2C_SI5394_SDA, // I2C数据(双向) output reg [7:0] LED // 扫描结果指示 ); // 内部时钟信号(差分转单端) wire clk; IBUFDS #( .DIFF_TERM("FALSE"), .IBUF_LOW_PWR("TRUE"), .IOSTANDARD("LVDS") ) clk_ibufds ( .O(clk), .I(SYSCLK2_P), .IB(SYSCLK2_N) ); // 参数定义 localparam CLK_FREQ = 100_000_000; // 100 MHz localparam I2C_FREQ = 100_000; // I2C时钟频率 (100 kHz) localparam DIVIDER_I2C = CLK_FREQ / (2 * I2C_FREQ) - 1; localparam COUNTER_ADDR = 500_000; // 500ms地址切换周期 // 状态机定义 typedef enum logic [2:0] { IDLE, // 空闲状态 START, // 发送起始条件 SEND_ADDR, // 发送设备地址 CHECK_ACK, // 检查ACK响应 STOP, // 发送停止条件 NEXT_ADDR // 准备下一个地址 } state_t; // 状态寄存器 reg [2:0] state = IDLE; reg [2:0] next_state = IDLE; // 定时控制 reg [31:0] timer_count = 0; reg addr_switch_pulse = 0; // I2C控制信号 reg [15:0] i2c_counter = 0; reg i2c_clk = 1; reg sda_out = 1; reg sda_oe = 0; wire sda_in = I2C_SI5394_SDA; // 地址扫描控制 reg [6:0] current_addr = 7'h00; // 当前尝试的地址(7位) reg [7:0] addr_response = 8'h00; // 地址响应位图 reg [3:0] bit_count = 0; reg found = 0; reg [7:0] found_count = 0; // I2C时钟边沿检测 reg i2c_clk_prev = 0; wire i2c_clk_rising = (i2c_clk_prev == 0 && i2c_clk == 1); wire i2c_clk_falling = (i2c_clk_prev == 1 && i2c_clk == 0); // 设备地址显示 always @(posedge clk) begin LED <= {found, current_addr}; end // 地址切换定时器(0.5秒) always @(posedge clk) begin if (timer_count >= COUNTER_ADDR) begin timer_count <= 0; addr_switch_pulse <= 1; end else begin timer_count <= timer_count + 1; addr_switch_pulse <= 0; end end // I2C时钟生成(100 kHz) always @(posedge clk) begin i2c_clk_prev <= i2c_clk; if (i2c_counter >= DIVIDER_I2C) begin i2c_counter <= 0; i2c_clk <= ~i2c_clk; end else begin i2c_counter <= i2c_counter + 1; end end // I2C总线控制 assign I2C_SI5394_SCL = (state != IDLE) ? i2c_clk : 1'b1; assign I2C_SI5394_SDA = sda_oe ? sda_out : 1'bz; // ILA信号 wire [7:0] current_addr_8bit = {1'b0, current_addr}; wire ack_detected = (state == CHECK_ACK && i2c_clk_rising && sda_in == 0); wire [7:0] response_map = addr_response; // 主状态机 always @(posedge clk) begin state <= next_state; case (state) IDLE: begin sda_oe <= 0; sda_out <= 1; found <= 0; if (addr_switch_pulse) begin next_state <= START; bit_count <= 0; end end START: begin sda_oe <= 1; // 确保START状态持续足够时间 if (i2c_clk_falling) begin sda_out <= 1; // SCL高时SDA高 end if (i2c_clk_rising) begin sda_out <= 0; // SCL高时拉低SDA(起始条件) next_state <= SEND_ADDR; end end SEND_ADDR: begin sda_oe <= 1; if (i2c_clk_falling) begin if (bit_count < 8) begin // 发送7位地址 + R/W位(0=写) if (bit_count < 7) begin sda_out <= current_addr[6 - bit_count]; end else begin sda_out <= 1'b0; // 写命令 end bit_count <= bit_count + 1; end else if (bit_count == 8) begin sda_oe <= 0; // 释放SDA用于ACK检测 next_state <= CHECK_ACK; end end end CHECK_ACK: begin sda_oe <= 0; // 主机释放SDA if (i2c_clk_rising) begin if (sda_in == 0) begin // ACK detected found <= 1; addr_response <= addr_response | (1 << current_addr); found_count <= found_count + 1; end next_state <= STOP; end end STOP: begin sda_oe <= 1; if (i2c_clk_falling) begin sda_out <= 0; // SCL低时SDA低 end if (i2c_clk_rising) begin sda_out <= 1; // SCL高时SDA高(停止条件) next_state <= NEXT_ADDR; end end NEXT_ADDR: begin // 移动到下一个地址 if (current_addr == 7'h7F) begin current_addr <= 7'h00; end else begin current_addr <= current_addr + 1; end next_state <= IDLE; end default: next_state <= IDLE; endcase end // 复位信号保持无效 assign SI_RSTBB = 1'b1; // ILA实例化 ila_inst ila ( .clk(clk), // 采样时钟 .probe0(I2C_SI5394_SCL), // I2C SCL .probe1(I2C_SI5394_SDA), // I2C SDA .probe2(state), // 状态机状态(3位) .probe3(current_addr_8bit), // 当前扫描地址(8位) .probe4({7'b0, ack_detected}), // ACK检测信号 .probe5(response_map), // 地址响应位图(8位) .probe6({7'b0, found}), // 当前地址发现标志 .probe7(found_count) // 发现设备计数 ); endmodule
运行结果如图所示:
能够找到器件地址为0x68