2601_95983181 2026-05-03 21:05 采纳率: 0%
浏览 6

i2c通信协议的信号一直传输不进板子

module exam #(
parameter [6:0] ADXL_ADDR = 7'h53 // SDO=0->0x53, SDO=1->0x1D
)(
input wire clk_12m,
input wire rst_n,

output reg  scl,
inout  wire sda,

// 数码管1(共阴极,段选高有效)
output reg seg_a1,
output reg seg_b1,
output reg seg_c1,
output reg seg_d1,
output reg seg_e1,
output reg seg_f1,
output reg seg_g1,
output reg seg_dp1,
output reg seg_dig1          // 组选通,低有效

);

// ============================================================
// 数码管:固定显示 "1",DP 表示 i2c_ok
// ============================================================
reg i2c_ok;

always @(*) begin
    seg_dig1 = 1'b0;  // 使能数码管1(低有效)

    // "1":b、c 亮(共阴极:1=亮)
    seg_a1 = 1'b0;
    seg_b1 = 1'b1;
    seg_c1 = 1'b1;
    seg_d1 = 1'b0;
    seg_e1 = 1'b0;
    seg_f1 = 1'b0;
    seg_g1 = 1'b0;

    seg_dp1 = i2c_ok; // 成功点亮小数点
end

// ============================================================
// I2C half tick: 12MHz -> 100kHz (half period = 60 cycles)
// ============================================================
reg [6:0] div_cnt;  // 0..59
reg tick;

always @(posedge clk_12m or negedge rst_n) begin
    if (!rst_n) begin
        div_cnt <= 7'd0;
        tick    <= 1'b0;
    end else if (div_cnt == 7'd59) begin
        div_cnt <= 7'd0;
        tick    <= 1'b1;
    end else begin
        div_cnt <= div_cnt + 7'd1;
        tick    <= 1'b0;
    end
end

// ============================================================
// SDA open-drain
// ============================================================
reg sda_oe, sda_out;
assign sda = sda_oe ? sda_out : 1'bz;
wire sda_in = sda;

// ============================================================
// I2C state machine: read DEVID(0x00)
// ============================================================
localparam ST_IDLE     = 4'd0;
localparam ST_START    = 4'd1;
localparam ST_SEND     = 4'd2;
localparam ST_ACK      = 4'd3;
localparam ST_RESTART  = 4'd4;
localparam ST_READ     = 4'd5;
localparam ST_READNACK = 4'd6;
localparam ST_STOP     = 4'd7;
localparam ST_CHECK    = 4'd8;

reg [3:0] st;
reg [7:0] tx;
reg [7:0] rx;
reg [2:0] bitn;      // 7..0
reg phase;           // 0:SCL low, 1:SCL high
reg nack_seen;
reg [1:0] seq;       // 0:ADDRW, 1:REG, 2:ADDRR

wire [7:0] ADDR_W = {ADXL_ADDR, 1'b0};
wire [7:0] ADDR_R = {ADXL_ADDR, 1'b1};

always @(posedge clk_12m or negedge rst_n) begin
    if (!rst_n) begin
        scl       <= 1'b1;
        sda_oe    <= 1'b0;
        sda_out   <= 1'b1;

        st        <= ST_IDLE;
        tx        <= 8'h00;
        rx        <= 8'h00;
        bitn      <= 3'd7;
        phase     <= 1'b0;
        nack_seen <= 1'b0;
        seq       <= 2'd0;
        i2c_ok    <= 1'b0;
    end else if (tick) begin
        case (st)
            ST_IDLE: begin
                scl       <= 1'b1;
                sda_oe    <= 1'b0;   // release SDA
                sda_out   <= 1'b1;
                phase     <= 1'b0;
                bitn      <= 3'd7;
                rx        <= 8'h00;
                nack_seen <= 1'b0;
                seq       <= 2'd0;
                st        <= ST_START;
            end

            ST_START: begin
                // START: SDA 1->0 while SCL=1
                scl     <= 1'b1;
                sda_oe  <= 1'b1;
                sda_out <= 1'b0;

                tx    <= ADDR_W;
                bitn  <= 3'd7;
                phase <= 1'b0;
                st    <= ST_SEND;
            end

            ST_SEND: begin
                if (!phase) begin
                    // SCL low: set data bit
                    scl     <= 1'b0;
                    sda_oe  <= 1'b1;
                    sda_out <= tx[bitn];
                    phase   <= 1'b1;
                end else begin
                    // SCL high: slave samples
                    scl   <= 1'b1;
                    phase <= 1'b0;
                    if (bitn == 3'd0)
                        st <= ST_ACK;
                    else
                        bitn <= bitn - 3'd1;
                end
            end

            ST_ACK: begin
                if (!phase) begin
                    // SCL low: release SDA
                    scl     <= 1'b0;
                    sda_oe  <= 1'b0;
                    sda_out <= 1'b1;
                    phase   <= 1'b1;
                end else begin
                    // SCL high: sample ACK (0=ACK, 1=NACK)
                    scl <= 1'b1;
                    if (sda_in) nack_seen <= 1'b1;
                    phase <= 1'b0;

                    if (seq == 2'd0) begin
                        // after ADDR_W
                        tx   <= 8'h00;   // DEVID reg
                        bitn <= 3'd7;
                        seq  <= 2'd1;
                        st   <= ST_SEND;
                    end else if (seq == 2'd1) begin
                        // after reg
                        st <= ST_RESTART;
                    end else begin
                        // after ADDR_R
                        bitn <= 3'd7;
                        rx   <= 8'h00;
                        st   <= ST_READ;
                    end
                end
            end

            ST_RESTART: begin
                // repeated START in 2 ticks
                if (!phase) begin
                    scl     <= 1'b1;
                    sda_oe  <= 1'b0; // release SDA -> high
                    sda_out <= 1'b1;
                    phase   <= 1'b1;
                end else begin
                    scl     <= 1'b1;
                    sda_oe  <= 1'b1;
                    sda_out <= 1'b0; // pull low -> repeated START
                    phase   <= 1'b0;

                    tx   <= ADDR_R;
                    bitn <= 3'd7;
                    seq  <= 2'd2;
                    st   <= ST_SEND;
                end
            end

            ST_READ: begin
                if (!phase) begin
                    // SCL low: release SDA
                    scl     <= 1'b0;
                    sda_oe  <= 1'b0;
                    sda_out <= 1'b1;
                    phase   <= 1'b1;
                end else begin
                    // SCL high: sample data
                    scl <= 1'b1;
                    rx[bitn] <= sda_in;
                    phase <= 1'b0;
                    if (bitn == 3'd0)
                        st <= ST_READNACK;
                    else
                        bitn <= bitn - 3'd1;
                end
            end

            ST_READNACK: begin
                // master NACK: drive SDA=1 on 9th clock
                if (!phase) begin
                    scl     <= 1'b0;
                    sda_oe  <= 1'b1;
                    sda_out <= 1'b1; // NACK
                    phase   <= 1'b1;
                end else begin
                    scl   <= 1'b1;
                    phase <= 1'b0;
                    st    <= ST_STOP;
                end
            end

            ST_STOP: begin
                // STOP: SDA 0->1 while SCL=1
                if (!phase) begin
                    scl     <= 1'b0;
                    sda_oe  <= 1'b1;
                    sda_out <= 1'b0;
                    phase   <= 1'b1;
                end else begin
                    scl     <= 1'b1;
                    sda_oe  <= 1'b0; // release SDA -> rises due to pull-up
                    sda_out <= 1'b1;
                    phase   <= 1'b0;
                    st      <= ST_CHECK;
                end
            end

            /*ST_CHECK: begin
                i2c_ok <= (!nack_seen) && (rx == 8'hE5);
                st     <= ST_IDLE;
            end*/
                 ST_CHECK: begin
                // 暂时只测试 ACK,不管读回来的数据对错
                    i2c_ok <= !nack_seen;  // 只要收到ACK就亮小数点
                    st <= ST_IDLE;
                end

            default: st <= ST_IDLE;
        endcase
    end
end

endmodule

以上为代码部分,用板子上的数码管是否显示小数点判断信号是否有传输,但是一直都没能看到小数点显示

img


图片里是板子外接了一个传感器,管脚分配和接线应该都没问题,硬件应该也都是好的

希望能帮忙找找问题,帮忙解答谢谢

  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2026-05-03 21:06
    关注

    晚上好🌙🌙🌙
    本答案参考通义千问

    你提到的 I2C通信协议的信号一直传输不进板子,这可能是由多种原因引起的。以下是一个详细的分析和解决方案。


    一、可能的原因分析

    1. 硬件连接问题

      • SDA 和 SCL 线是否正确连接?
      • 是否有上拉电阻?I2C 是开漏总线,需要外部上拉电阻(通常为 4.7kΩ)。
      • 检查是否有短路或断路。
    2. 时钟同步问题

      • I2C 通信依赖于主设备发送的时钟信号(SCL)。如果时钟频率设置错误,可能导致数据无法正确传输。
      • 在你的代码中,clk_12m 被用来生成 I2C 的半周期(60 个时钟周期),但需要确保该时钟是稳定的。
    3. 状态机逻辑错误

      • 你的 I2C 状态机可能存在逻辑错误,导致无法正确发送起始位、地址、数据等。
      • 检查 ST_SENDST_ACKST_READ 等状态是否正确处理。
    4. SDA 控制逻辑问题

      • 你的 sda_oesda_out 控制是否合理?特别是 sda = sda_oe ? sda_out : 1'bz; 这一行是否正确?
    5. ACK/NACK 处理问题

      • 如果从设备没有正确响应 ACK,主设备会认为通信失败。检查 nack_seen 是否被正确设置。
    6. 寄存器地址或读写模式错误

      • 你使用的 ADDR_WADDR_R 是否正确?例如,ADXL345 的地址是 0x1D0x53,取决于 SDO 引脚的状态。

    二、详细解决方案

    1. 硬件检查

    • 确认 SDA/SCL 接线正确

      • 确保 SDA 和 SCL 正确连接到 ADXL345 的相应引脚。
      • 如果使用开发板,检查是否已经启用 I2C 接口。
    • 添加上拉电阻

      • 在 SDA 和 SCL 上添加 4.7kΩ 的上拉电阻,确保信号稳定。

    2. 修正 I2C 状态机逻辑

    修改后的代码片段(重点部分已加粗)

    // I2C state machine: read DEVID(0x00)
    localparam ST_IDLE     = 4'd0;
    localparam ST_START    = 4'd1;
    localparam ST_SEND     = 4'd2;
    localparam ST_ACK      = 4'd3;
    localparam ST_RESTART  = 4'd4;
    localparam ST_READ     = 4'd5;
    localparam ST_READNACK = 4'd6;
    localparam ST_STOP     = 4'd7;
    localparam ST_CHECK    = 4'd8;
    
    reg [3:0] st;
    reg [7:0] tx;
    reg [7:0] rx;
    reg [2:0] bitn;      // 7..0
    reg phase;           // 0:SCL low, 1:SCL high
    reg nack_seen;
    reg [1:0] seq;       // 0:ADDRW, 1:REG, 2:ADDRR
    
    wire [7:0] ADDR_W = {ADXL_ADDR, 1'b0};
    wire [7:0] ADDR_R = {ADXL_ADDR, 1'b1};
    
    always @(posedge clk_12m or negedge rst_n) begin
        if (!rst_n) begin
            scl       <= 1'b1;
            sda_oe    <= 1'b0;   // release SDA
            sda_out   <= 1'b1;
    
            st        <= ST_IDLE;
            tx        <= 8'h00;
            rx        <= 8'h00;
            bitn      <= 3'd7;
            phase     <= 1'b0;
            nack_seen <= 1'b0;
            seq       <= 2'd0;
            i2c_ok    <= 1'b0;
        end else if (tick) begin
            case (st)
                ST_IDLE: begin
                    scl       <= 1'b1;
                    sda_oe    <= 1'b0;   // release SDA
                    sda_out   <= 1'b1;
                    phase     <= 1'b0;
                    bitn      <= 3'd7;
                    rx        <= 8'h00;
                    nack_seen <= 1'b0;
                    seq       <= 2'd0;
                    st        <= ST_START;
                end
    
                ST_START: begin
                    // START: SDA 1->0 while SCL=1
                    scl     <= 1'b1;
                    sda_oe  <= 1'b1;
                    sda_out <= 1'b0;
    
                    tx    <= ADDR_W;
                    bitn  <= 3'd7;
                    phase <= 1'b0;
                    st    <= ST_SEND;
                end
    
                ST_SEND: begin
                    if (!phase) begin
                        // SCL low: set data bit
                        scl     <= 1'b0;
                        sda_oe  <= 1'b1;
                        sda_out <= tx[bitn];
                        phase   <= 1'b1;
                    end else begin
                        // SCL high: slave samples
                        scl   <= 1'b1;
                        phase <= 1'b0;
                        if (bitn == 3'd0) begin
                            st <= ST_ACK;
                            // Wait for ACK
                        end else begin
                            bitn <= bitn - 3'd1;
                        end
                    end
                end
    
                ST_ACK: begin
                    if (!phase) begin
                        // SCL low: check ACK
                        scl     <= 1'b0;
                        sda_oe  <= 1'b0; // Release SDA to read
                        phase   <= 1'b1;
                    end else begin
                        // SCL high: read ACK
                        scl   <= 1'b1;
                        phase <= 1'b0;
                        if (sda_in == 1'b0) begin
                            // ACK received
                            nack_seen <= 1'b0;
                            st <= ST_RESTART;
                        end else begin
                            // NACK received
                            nack_seen <= 1'b1;
                            st <= ST_STOP;
                        end
                    end
                end
    
                // 其他状态保持不变...
    

    关键修改点:

    • ST_ACK 状态中增加了对 sda_in 的读取,以判断是否收到 ACK。
    • 确保 sda_oe 在读取 ACK 时释放 SDA,以便从设备可以驱动 SDA。

    三、调试建议

    1. 使用示波器或逻辑分析仪

      • 检查 SCL 和 SDA 的波形,确认是否出现正确的起始位、数据位、ACK/NAK 等。
    2. 打印调试信息

      • 在状态转移时输出 st 的值,观察是否进入预期状态。
    3. 验证寄存器地址

      • 确认 ADDR_WADDR_R 是否正确。例如,对于 ADXL345,地址应为 0x1D(SDO=1)或 0x53(SDO=0)。
    4. 测试简单读写操作

      • 尝试读取一个简单的寄存器(如 DEVID,地址 0x00),看是否能返回正确的值。

    四、总结

    | 问题 | 解决方案 | |------|----------| | 硬件连接错误 | 检查 SDA/SCL 接线,添加上拉电阻 | | 状态机逻辑错误 | 修正状态机逻辑,增加 ACK/NACK 判断 | | SDA 控制逻辑错误 | 确保 sda_oe 在读取时释放 SDA | | 寄存器地址错误 | 验证 ADDR_WADDR_R 是否正确 |


    如有更多具体现象(如波形、错误提示等),可进一步定位问题。希望以上内容对你有所帮助!

    评论

报告相同问题?

问题事件

  • 创建了问题 5月3日