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



