Verilog I2C 主模块似乎将 SDA 设置为双向,但在发送 cmd 后无法获得任何响应 [关闭]

问题描述 投票:0回答:0

我正在尝试通过 DE1-SOC 板与 HTU21D(F) 湿度和温度传感器通信。传感器位于分线板上,分线板上有用于 i2c 的上拉电阻。我可以快速找到的视觉上最接近的匹配项是旧的 sparkfun 教程页面here。根据传感器本身的数据表,它有两种通信模式。第一个是保持主模式,在该模式下传感器阻塞 SCK 线,同时传感器进行测量直到完成。第二种是 no hold master 模式,需要 master 轮询传感器直到传感器完成测量。

我已经尝试过保持主模式,因为我无法找到默认模式,我能找到的唯一指示是保持主模式首先在数据表中进行了详细说明。我试图找到我可以在线阅读的 i2c 模块示例,并最终使用 this 示例作为我的基础。

一旦我用 SDA、SCK、data_in(想要测量类型的命令)、data_out(测量)、go(按下 DE1-SOC 上的按键)、reset、系统时钟,我连接了适当的模块输入/输出和注册到各自的位置,并将其加载到板上。我主要使用信号抽头来监视内部信号,但也有 SDA 和 SCK 线。出现的是 SDA 线会输出地址(0x80),应该会收到一个确认,然后输出我硬设置为温度测量值(0xE3)的命令,而 SDA 线不会收到任何响应。发送命令后,我的模块似乎卡在了 ack 状态。

预期的行为是主机将在 SCK 为高电平时取消 SDA。发送起始位后,主机将发送地址 0x80 和写入位等待确认。地址确认后,主机将根据按下的键发送命令 0xE3 或 0xE5,并等待确认。一旦收到确认,主机将发送一个起始位并发送地址 0x81 和读取位并等待确认。收到读取确认后,主机将读取 8 位的 SDA 数据,然后发送确认。发送 ack 后,主机将读取另外 8 位,然后发送 nack。

我已经尝试根据上面显示的示例创建自己的模块

module i2c(
    input     clk, go, rst,
    input     [7:0]data_in,
    inout     ioport,
    output    ioclk,
    output    [13:0]data_out//,
//  output    [3:0]State,
//  output    [4:0]counter,
//  output    [7:0]command,
//  output    sdat
);
    
    // Declare state register
    reg        [3:0]        state;
    reg        [7:0]        addr;
    reg        [7:0]        cmd;
    reg        [4:0]        count;
    reg                     sdata;
    reg        [15:0]       io_in;
    reg                     dir;
    wire       [7:0]        icmd;
    wire       [4:0]        icount;
//  reg        [7:0]        rdata;
    reg                     exclk;
    wire                    idata, idir;
    
    // Declare states
    parameter Idle = 0, Start = 1, Addr = 2, Ack = 3, Cmd = 4, Ack2 = 5, Data1 = 6, Ack_s = 7, Data2 = 8, Stop = 9, Reset = 10, RAck = 11;
    
    initial begin
        dir      <= 1;
        sdata    <= 1;
        addr     <= 8'h80;
        cmd      <= 8'h00;
        exclk    <= 1;
        state    <= Idle;
    end
    
    assign ioport        = dir ? idata : 1'bz;
    assign ioclk        = exclk;
    assign data_out    = io_in[15:1];
    assign icmd = data_in;
    assign icount = count;
    assign idata = sdata;
//    assign irst = rdata;
//    assign State = state;
//    assign counter = count;
//    assign command = icmd;
//    assign idir = dir;
//    assign sdat = idir;
    //clk div by half for SCK
    always @ (posedge clk) begin
        exclk <= !exclk;
    end
    
    // direction control based on exclk and state
    always @ (negedge exclk) begin
        case(state)
            Idle://0
                dir <= 1;
            Start://1
                dir <= 1;
            Addr://2
                dir <= 1;
            Ack://3
                dir <= 0;
            Cmd://4
                dir <= 1;
            Ack2://5
                dir <= 0;
            Data1://6
                dir <= 0;
            Ack_s://7
                dir <= 1;
            Data2://8
                dir <= 0;
            Stop://9
                dir <= 1;
        endcase
    end
    //data output based on state using clk and exclk to determine when to change
    always @ (*) begin
        case (state)
            Idle: begin//0
                
            end
            Start: begin//1
                if(exclk && !clk) begin
                    sdata <= 0;
                end
            end
            Addr: begin//2
                if(!exclk) begin
                    sdata <= addr[icount];
                end
            end
            Ack: begin//3
                
            end
            Cmd: begin//4
                if(!exclk) begin
                    sdata <= cmd[icount];
                end
            end
            Ack2: begin//5
            
            end
            Data1: begin//6
                if(!exclk && !clk) begin
                    io_in[icount] <= ioport;
                end
            end
            Ack_s: begin//7
                if(!exclk && !clk) begin
                    sdata <= 0;
                end
            end
            Data2: begin//8
                if(!exclk && !clk) begin
                    io_in[icount] <= ioport;
                end
            end
            Stop: begin//9
                if((!exclk && !clk) && ioport) begin
                    sdata <= 0;
                end
                if(!ioport && (exclk && !clk)) begin
                    sdata <= 1;
                end
            end
            default: begin
            
            end    
        endcase
    end
    
    // Determine the next state based on exclk or rst
    always @ (posedge exclk or posedge rst) begin
        if (rst == 1) begin
            //state <= Reset;
            //count <= 7;
            state <= Idle;
        end
        else begin
            case (state)
                Idle: begin//0
                    if(go == 0) begin
                        state    <= Start;
                    end
                end
                Start: begin//1
                    if(!ioport && exclk) begin
                            state <= Addr;
                            count <= 7;
                    end
                    else begin
                        state <= Start;
                    end
                end
                Addr: begin//2
                    if (count == 4'b0) begin
                        state    <= Ack;
                    end
                    else begin
                        state <= Addr;
                        count <= count - 1'b1;
                    end
                end
                Ack: begin//3
                    if (!ioport && exclk) begin
                        if(!addr[0]) begin
                            state <= Cmd;
                            count <= 7;
                            cmd    <= icmd;
                        end
                        else begin
                            state <= Data1;
                            count <= 15;
                        end
                    end
                    else begin
                        state <= Ack;
                    end
                end
                Cmd: begin//4
                    if(count == 0) begin
                        state <= Ack2;
                    end
                    else begin
                        state <= Cmd;
                        count <= count - 1'b1;
                    end
                end
                Ack2: begin//5
                    if(!ioport && exclk) begin
                        state        <= Start;
                        addr[0]    <= 1'b1;
                        count <= 7;
                    end
                    else begin
                        state <= Ack2;
                    end
                end
                Data1: begin//6
                    if(count == 7) begin
                        state <= Ack_s;
                    end
                    else begin
                        state <= Data1;
                        count <= count - 1'b1;
                    end
                end
                Ack_s: begin//7
                    if(ioport) begin
                        state <= Data2;
                    end
                    else begin
                        state <= Ack_s;
                    end
                end
                Data2: begin//8
                    if(count == 0) begin
                        state <= Stop;
                    end
                    else begin
                        state <= Data2;
                        count <= count - 1'b1;
                    end
                end
                Stop: begin//9
                    if(ioport) begin
                        state <= Idle;
                    end
                    else begin
                        state <= Stop;
                    end
                end
            endcase
        end
    end
endmodule
module Lab0(
input CLOCK2_50,
output [6:0] HEX0,
output [6:0] HEX1,
input [3:0] KEY,
inout SDA,
output SCL);

wire    [15:0]      dout;
wire    [3:0]       key;
reg     [7:0]       din;
wire    [7:0]       data;
wire                go;
initial begin
    din <= 8'hE3;
end
assign key  = KEY;
assign data = din;
assign HEX0 = dout[7:2];
assign HEX1 = dout[15:8];
assign go   = (!key[0] || !key[1]);
always @ (negedge key[0] or negedge key[1]) begin
    if(!key[0])
        din <= 8'hE3;
    else if(!key[1])
        din <= 8'hE5;
end
i2c comm(.clk(CLOCK2_50), .go(!go), .rst(!key[3]), .data_in(data), .ioport(SDA), .ioclk(SCL), .data_out(dout));

endmodule
`timescale 1ns / 1ps
module test();
wire SDA, SCL, sdata;
wire [13:0]dout;
wire [3:0]state;
wire [4:0]count;
wire [7:0]cmd;
wire [7:0]addr;
reg [7:0]data;
reg clk, rst, go, simdata;
i2c comm(.clk(clk), .go(go), .rst(rst), .data_in(data), .ioport(SDA), .ioclk(SCL), .data_out(dout), .State(state), .counter(count), .command(cmd), .sdat(sdata));

assign SDA = simdata;

initial begin
data <= 8'hE3;
clk <= 0;
rst <= 0;
go <= 1;
simdata <= 1'bz;
#20 go <= 0;
#40 go <= 1;
#340 simdata <= 0;
#50 simdata <= 1'bz;
#320 simdata <= 0;
#40 simdata <= 1'bz;
#320 simdata <= 0;
#50 simdata <= 1'bz;

end
always
    begin
        #10 clk <= ~clk;
    end
endmodule

这是具有前面提到的问题的模块和顶级文件。我已经尝试修改我用来与传感器一起工作的示例,并收到了 SCK 线设置为始终打开的问题,以防止示例中的双向设置。我试过将模块代码(修改过的)直接放在顶层文件中,以防双向有一些层次问题并实现同样的失败。

我已经通过 Quartus 中的引脚规划器确认 SDA 引脚是双向的。当我使用修改后的示例时,我没有检查 SCK 引脚,因为 SCK 线将始终打开的警告表明它将是单向的。 RTL 查看器证明修改后的示例就是这种情况。不幸的是,我可以看到 RTL 没有明显的问题,状态视图具有正确的方向性。

我不知道接下来要尝试改变什么。我几乎要假设传感器坏了。我已经能够确认时钟信号正在到达传感器,但无法确认 SDA 信息,因为当我尝试用示波器读取它时,信号会破坏时钟和 SDA 线,这可能表明存在干扰,但我没有看看如果这个分线板有这个问题会怎样,因为它主要与 Arduino 一起使用,如 sparkfun 教程页面所示。这是我第一次明确使用 inout/tri-state,我分别研究了这些,看看我是否犯了任何明显的错误,但我可能错过了。

编辑:我更新了代码,添加了我的测试平台代码,这里是来自 modelsim 和 signaltap 的波形。测试台只到等待数据的主机为止。发送命令后我没有收到确认,这让我想也许我在地址后没有收到。ModelSim waveformSignalTap waveform

verilog fpga i2c intel-fpga
© www.soinside.com 2019 - 2024. All rights reserved.