一、概述:通过I2C读取温度芯片tmp100的温度

上一个章节时学习了I2C读写EEPROM,基于此基础,再结合tmp100的特性重新设计,完成温度的读取。
通过tmp100的完成温度数据的采集,这是实验室的老师布置的一个研修任务。一开始没有拿到芯片手册,我以为和读写eeprom的过程会是一样的,这导致后面走了一点弯路。因为eeprom是存储器,而在对这种存储器的读写的时候,往往都会考虑读写的初始地址,当初始地址写满或读空后,再去读写下一个地址空间。
但是tmp100芯片则不一样,它并非存储器,而是一种简单的传感器,只需要对寄存器进行配置即可,按照对应的寄存器说明配置不同的值(也就是向寄存器去写特定的值,这个过程和向存储器写数据其实是类似的),然后芯片就会根据这些寄存器的值去执行对应的功能。后面拿到了手册,手册上会有对寄存器的具体描述以及使用I2C配置寄存器和读温度的时序。

二、首先可以来看一下tmp100芯片的寄存器以及原理图

tmp100芯片在i2c总线上的设备地址

通过下面三张图片分别是tmp100的原理图,可知: 左边U1:ADD1 ADD0 = 00; **

芯片emmc寿命怎么查看_fpga


芯片emmc寿命怎么查看_stm32_02


下表是从机设备的地址:由上面两张图可得:SLAVE ADDRESS == 1001000

芯片emmc寿命怎么查看_芯片emmc寿命怎么查看_03


Pointer Register
下图展示的是 Pointer Register(指向寄存器) ,该寄存器会根据P1和P0的值来指向对应的4个寄存器,这4个寄存器执行不同的功能。
P1P000指向Temperature Register,该寄存器只可以被读取,显然这个寄存器存储的是温度值;
P1P0
01指向Configuration Register,这个寄存器即可读出也可写入。
剩下的两个寄存器的功能是高温低温的阈值报警,此处未使用,可结合实际情况考虑是否写入。

芯片emmc寿命怎么查看_寄存器_04


Temperature Register
下图是 Temperature Register(温度寄存器),根据描述可知,该寄存器用于存储采集的数据,必须读取两个字节才能获得数据,前12位用于指示温度,其余位默认为零。
上表可知,该寄存器是只读寄存器,只能被读取。

芯片emmc寿命怎么查看_fpga_05


Configuration Register
下图是 Configuration Register,这是一个支持读写操作的8位寄存器。每一个bit都有特定的含义。D0-D7在手册中都有详细的描述,手册中,下面这张图后面就是对每个bit功能的详细描述。

芯片emmc寿命怎么查看_stm32_06


这里就简单提一下 R1R0 表示读取温度的
精度** ,也就是Temperature Register中前12bit的数据,。这里Configuration Register配置为0101_0110 ,RIR0==01,表示读出的精度是小数点后两位,手册中的数据如下:

芯片emmc寿命怎么查看_fpga_07


芯片emmc寿命怎么查看_芯片emmc寿命怎么查看_08

三、了解完寄存器之后可以查看其读写的时序图

下图展示的是写的时序图,配置顺序:设备地址(前面7位是1001000,最后一位为低代表写操作)->tmp100应答->pointer register->tmp100应答->写入的数据1[这里配置Configuration Register]->tmp100应答->写入数据2->主机应答。因为是配置寄存器,这里在数据1写入之后主机就可以发送停止信号了。

芯片emmc寿命怎么查看_fpga_09

下面展示的是读的时序图,配置顺序和写的类似,不过从温度寄存器读出来的数据是16bit,而i2c每次读取的单位是一个字节,故这里需要连续读两次。读取的高八位和低八位,分别代表温度的整数和小数。

芯片emmc寿命怎么查看_寄存器_10

状态机

接下来就是根据上面的时序以及需要配置的寄存器设计出相应的状态图,在对应的状态下执行相应的功能,结合前面学习I2C读写EEPROM的内容,我这里也会通过按键来控制读和写两个过程。

当写按键按下时,会执行写部分的时序,完成寄存器的配置,配置完成之后,对其进行复位,在执行读的时序,通过抓取读出来的数据就可以观察到温度的变化了。根据以上思路,状态机设计如下:红色路径表示读的过程,绿色路径表示配置寄存器。

芯片emmc寿命怎么查看_芯片emmc寿命怎么查看_11

结合状态机设计出读取温度的控制模块代码

代码部分需要考虑以下几个部分:
1.按键按下后产生的标志是通过系统时钟来采集的,需要对这个标志打拍和延时,便于被i2c_clk这个时钟采到,这个时钟是这个控制模块的实际驱动时钟,也便于产生scl的时钟;系统时钟是50mhz,I2C驱动时钟是1mhz,串行输出时钟scl是250khz。
2.产生有效的读写标志来判断状态机跳转读还是写的路径,有效的读写标志会产生一个起始信号,告诉状态机开始工作。
实际代码如下:

`timescale  1ns/1ns
module  i2c_ctrl
#(
    parameter   DEVICE_ADDR_W   =   8'b1001_0000     ,   //i2c设备地址SEND_D_ADDR
    parameter   DEVICE_ADDR_R   =   8'b1001_0001     ,   //i2c设备地址SEND_D_ADDR
    parameter   POINT_CMD_WR    =   8'b0000_0001    ,   //指向写的路径
    parameter   CONFIG_REG      =   8'b0101_0110    ,   //配置寄存器
    parameter   POINT_CMD_RD    =   8'b0000_0000    ,   //指向读的路径
    parameter   SYS_CLK_FREQ    =   26'd50_000_000  ,   //输入系统时钟频率
    parameter   SCL_FREQ        =   18'd250_000         //i2c设备scl时钟频率,250KZH
)
(
    input   wire            sys_clk     ,   //输入系统时钟,50MHz
    input   wire            sys_rst_n   ,   //输入复位信号,低电平有效
    input   wire            write       ,   //输入写使能信号,来自按键,经过消抖,只拉高一个sys_clk
    input   wire            read        ,   //输入读使能信号,来自按键,经过消抖,只拉高一个sys_clk
    input   wire    [7:0]   wr_data     ,   //输入i2c设备数据
    output  wire    [15:0]  rd_data     ,   //输出i2c设备读取数据
    output  wire            i2c_scl     ,   //输出至i2c设备的串行时钟信号scl
    inout   wire            i2c_sda         //输出至i2c设备的串行数据信号sda
);

//************************************************************************//
//******************** Parameter and Internal Signal *********************//
//************************************************************************//
// parameter define
parameter            CNT_CLK_MAX     =   (SYS_CLK_FREQ/SCL_FREQ) >> 2'd3   ;   //cnt_clk计数器计数最大值,i2c_clk是1mhz,计数到25翻转一次即可得到i2c_clk时钟

parameter            CNT_START_MAX   =   'd599_999; //控制读写使能拉高的时间,确保按键触发的信号能够在状态机跳转过程被检测到

parameter            IDLE            =   'd00;  //初始状态
parameter            START_1         =   'd01;  //开始状态1
parameter            SEND_D_ADDR     =   'd02;  //设备地址写入状态 这里设置为7位
parameter            ACK_1           =   'd03;  //应答状态
parameter            POINT_W         =   'd04;  //point 寄存器指向写的配置
parameter            ACK_W           =   'd05;  //应答状态
parameter            CFG_R           =   'd06;  //config 就寄存器配置
parameter            POINT_R         =   'd07;  //point 寄存器指向读的配置
parameter            ACK_3           =   'd08;  //应答状态
parameter            START_2         =   'd09;  //开始状态2
parameter            SEND_RD_ADDR    =   'd10;  //设备地址写入状态
parameter            ACK_5           =   'd11;  //应答状态
parameter            RD_DATA         =   'd12;  //读数据状态1
parameter            N_ACK           =   'd13;  //应答状态主机给的
parameter            RD_DATA1        =   'd14;  //读数据状态2            
parameter            N_ACK1          =   'd15;  //应答状态主机给的
parameter            STOP            =   'd16;  //结束状态

parameter            CNT_WR_RD_MAX   =   8'd200;   //cnt_wr/cnt_rd计数器计数最大值,write和read信号展宽的计数,至少保持两个i2c_clk时钟周期,200的话就是四个i2c_clk周期

reg             i2c_clk         ;   //i2c驱动时钟
reg             i2c_end         ;   //i2c一次读/写操作完成
wire            sda_in          ;   //sda输入数据寄存
wire            sda_en          ;   //sda数据写入使能信号

reg     [7:0]   cnt_wr          ;   //写触发有效信号保持时间计数器
reg             write_valid     ;   //写触发有效信号
reg     [7:0]   cnt_rd          ;   //读触发有效信号保持时间计数器
reg             read_valid      ;   //读触发有效信号
reg     [15:0]  cnt_start       ;   //一次读/写时间间隔计数
reg     [7:0]   cnt_clk         ;   //系统时钟计数器,控制生成clk_i2c时钟信号
reg     [5:0]   state           ;   //状态机状态
reg             cnt_i2c_clk_en  ;   //cnt_i2c_clk计数器使能信号
reg     [1:0]   cnt_i2c_clk     ;   //clk_i2c时钟计数器,控制生成cnt_bit信号
reg     [2:0]   cnt_bit         ;   //sda比特计数器
reg             ack             ;   //应答信号
reg             i2c_sda_reg     ;   //sda数据缓存
reg             i2c_sda_reg_r   ;
reg             ack_r;
reg     [15:0]  rd_data_reg     ;   //自i2c设备读出数据


reg     [15:0]   rd_data_r;
reg              wr_en,rd_en;
reg              rd_en_r,wr_en_r;
reg              i2c_start;
reg              i2c_scl_r;

assign  rd_data=rd_data_r ;
assign  i2c_scl=i2c_scl_r ;

//********************************************************************//
//***************************** 读写使能信号 ****************************//
//********************************************************************//

//write_valid 和 read_valid信号是对输入的一个系统脉冲信号write和read信号进行展宽,
//至少保持i2c_clk时钟域下两个时钟周期长度,便于在i2c_clk时钟域下被采集
//涉及到单bit跨时钟域处理,慢采快

//cnt_wr:写触发有效信号保持时间计数器,计数写触发有效信号保持时钟周期数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_wr    <=  8'd0;
    else    if(write_valid == 1'b0)
        cnt_wr    <=  8'd0;
    else    if(write_valid == 1'b1)
        cnt_wr    <=  cnt_wr + 1'b1;

//write_valid:写触发有效信号
//由于写触发信号保持时间为一个系统时钟周期(20ns),
//不能被i2c驱动时钟i2c_clk(一个周期是20*50ns)正确采集,延长写触发信号生成写触发有效信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        write_valid    <=  1'b0;
    else    if(cnt_wr == (CNT_WR_RD_MAX - 1'b1))
        write_valid    <=  1'b0;
    else    if(write == 1'b1)
        write_valid    <=  1'b1;

//cnt_rd:读触发有效信号保持时间计数器,计数读触发有效信号保持时钟周期数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_rd    <=  8'd0;
    else    if(read_valid == 1'b0)
        cnt_rd    <=  8'd0;
    else    if(read_valid == 1'b1)
        cnt_rd    <=  cnt_rd + 1'b1;

//read_valid:读触发有效信号
//由于读触发信号保持时间为一个系统时钟周期(20ns),
//不能被i2c驱动时钟i2c_scl正确采集,延长读触发信号生成读触发有效信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        read_valid    <=  1'b0;
    else    if(cnt_rd == (CNT_WR_RD_MAX - 1'b1))
        read_valid    <=  1'b0;
    else    if(read == 1'b1)
        read_valid    <=  1'b1;

//cnt_start:单字节数据读/写操作时间间隔计数
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_start   <=  16'd0;
    else    if((wr_en == 1'b0) && (rd_en == 1'b0))
        cnt_start   <=  16'd0;
    else    if(cnt_start == (CNT_START_MAX - 1'b1))
        cnt_start   <=  16'd0;
    else    if((wr_en_r == 1'b1) || (rd_en == 1'b1))
        cnt_start   <=  cnt_start + 1'b1;

//wr_en:输出写使能信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        wr_en   <=  1'b0;
    else    if((wr_en == 1'b1)&&(cnt_start == (CNT_START_MAX - 1'b1)))
        wr_en   <=  1'b0;
    else    if(write_valid == 1'b1)
        wr_en   <=  1'b1;
//rd_en:输出读使能信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_en   <=  1'b0;
    else    if((rd_en == 1'b1)&&(cnt_start == (CNT_START_MAX - 1'b1)))
        rd_en   <=  1'b0;
    else    if(read_valid == 1'b1)
        rd_en   <=  1'b1;

//检测读写使能上升沿,拉高i2c_start一个周期,用来启动状态机
always @(posedge i2c_clk or negedge sys_rst_n) begin
    if (sys_rst_n == 1'b0) begin
        wr_en_r <= 1'b0;
        rd_en_r<= 1'b0;
    end
    else begin
        wr_en_r <= wr_en;
        rd_en_r<= rd_en;        
    end
end

always @(posedge i2c_clk or negedge sys_rst_n) begin
    if (sys_rst_n == 1'b0) begin
        i2c_start <= 1'b0;       
    end
    else if ((wr_en_r == 1'b0&&wr_en==1'b1)||(rd_en_r == 1'b0&&rd_en==1'b1)) begin
        i2c_start <= 1'b1;  
    end
    else begin
       i2c_start <= 1'b0;      
    end
end

//assign i2c_start = ((wr_en_r == 1'b0&&wr_en==1'b1)||(rd_en_r == 1'b0&&rd_en==1'b1))?11'b1:1'b0;

//************************************************************************//
//******************************* Main Code ******************************//
//************************************************************************//
// cnt_clk:系统时钟计数器,控制生成clk_i2c时钟信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk <=  8'd0;
    else    if(cnt_clk == CNT_CLK_MAX - 1'b1)
        cnt_clk <=  8'd0;
    else
        cnt_clk <=  cnt_clk + 1'b1;

// i2c_clk:i2c驱动时钟1MHZ
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        i2c_clk <=  1'b1;
    else    if(cnt_clk == CNT_CLK_MAX - 1'b1)
        i2c_clk <=  ~i2c_clk;

// cnt_i2c_clk_en:cnt_i2c_clk计数器使能信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_i2c_clk_en  <=  1'b0;
    else    if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
        cnt_i2c_clk_en  <=  1'b0;
    else    if(i2c_start == 1'b1)
        cnt_i2c_clk_en  <=  1'b1;

// cnt_i2c_clk:i2c_clk时钟计数器,控制生成cnt_bit信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_i2c_clk <=  2'd0;
    else    if(cnt_i2c_clk_en == 1'b1)
        cnt_i2c_clk <=  cnt_i2c_clk + 1'b1;

// cnt_bit:sda比特计数器
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_bit <=  3'd0;
    else    if((state == IDLE) || (state == START_1) || (state == START_2)
                || (state == ACK_1) || (state == ACK_3)
                || (state == ACK_5) || (state == N_ACK)||(state == N_ACK1)||(state==ACK_W))
        cnt_bit <=  3'd0;
    else    if((cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
        cnt_bit <=  3'd0;
    else    if((cnt_i2c_clk == 2'd3) && (state != IDLE))
        cnt_bit <=  cnt_bit + 1'b1;
// state:状态机状态跳转
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state   <=  IDLE;
    else    case(state)
        IDLE:
            if (i2c_start == 1'b1) begin
                state <= START_1;
            end
        START_1:
            if(cnt_i2c_clk == 3)
                state   <=  SEND_D_ADDR;
            else
                state   <=  state;
        SEND_D_ADDR:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_1;
            else
                state   <=  state;
        ACK_1:
            if(wr_en == 1'b1&&(cnt_i2c_clk == 3) && (ack == 1'b0))
                state   <=  POINT_W;
            else    if(rd_en == 1'b1&&(cnt_i2c_clk == 3) && (ack == 1'b0))
                state   <=  POINT_R;
            else
                state   <=  state;
//写
        POINT_W:
            if((cnt_bit == 3'd7) && (cnt_i2c_clk == 3))
                state   <=  ACK_W;
            else
                state   <=  state;
        ACK_W:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                state   <=  CFG_R;
            else
                state   <=  state;
        CFG_R:
            if((cnt_bit == 3'd7) && (cnt_i2c_clk == 3))
                state   <=  ACK_3;
            else
                state   <=  state;
//读
        POINT_R:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_3;
            else
                state   <=  state;

        ACK_3:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                begin
                  if(rd_en == 1'b1)
                        state   <=  START_2;
                    else
                        state   <=  STOP;
                end
             else
                state   <=  state;

        START_2:
            if(cnt_i2c_clk == 3)
                state   <=  SEND_RD_ADDR;
            else
                state   <=  state;
        SEND_RD_ADDR:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_5;
            else
                state   <=  state;
        ACK_5:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                state   <=  RD_DATA;
            else
                state   <=  state;
        RD_DATA:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  N_ACK;
            else
                state   <=  state;
        N_ACK:
            if(cnt_i2c_clk == 3)
                state   <=  RD_DATA1;
            else
                state   <=  state;
        RD_DATA1:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  N_ACK1;
            else
                state   <=  state;
        N_ACK1:
            if(cnt_i2c_clk == 3)
                state   <=  STOP;
            else
                state   <=  state;
        STOP:
            if((cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
                state   <=  IDLE;
            else
                state   <=  state;
        default:    state   <=  IDLE;
    endcase

// ack:应答信号
always@(*)
    case    (state)
        IDLE,START_1,SEND_D_ADDR,POINT_R,POINT_W,CFG_R,
        START_2,SEND_RD_ADDR,RD_DATA,RD_DATA1,N_ACK1:
            ack <=  1'b1;
        ACK_1,ACK_W,ACK_3,ACK_5:
            if(cnt_i2c_clk == 2'd0)
                ack <=  sda_in;
            else
                ack <=  ack;
        N_ACK:
                ack <= 1'b0;
        default:    ack <=  1'b1;
    endcase

// i2c_scl:输出至i2c设备的串行时钟信号scl
always@(*)
    case    (state)
        IDLE:
            i2c_scl_r <=  1'b1;
        START_1:
            if(cnt_i2c_clk == 2'd3)
                i2c_scl_r <=  1'b0;
            else
                i2c_scl_r <=  1'b1;
        SEND_D_ADDR,ACK_1,POINT_W,ACK_W,CFG_R,POINT_R,
        ACK_3,START_2,SEND_RD_ADDR,ACK_5,RD_DATA,RD_DATA1,N_ACK,N_ACK1:
            if((cnt_i2c_clk == 2'd1) || (cnt_i2c_clk == 2'd2))
                i2c_scl_r <=  1'b1;
            else
                i2c_scl_r <=  1'b0;
        STOP:
            if((cnt_bit == 3'd0) &&(cnt_i2c_clk == 2'd0))
                i2c_scl_r <=  1'b0;
            else
                i2c_scl_r <=  1'b1;
        default:    i2c_scl_r <=  1'b1;
    endcase

// i2c_sda_reg:sda数据缓存,即把主机写给从机的一些数值缓存到i2c_sda_reg寄存器中,满足条件时把寄存器中的数据取出来送到i2c的串行数据线上i2c_sda
// rd_data_reg:该寄存器是串行时钟线i2c_sda读出从机的数据,然后缓存到该寄存器中
always@(*)
    case    (state)
        IDLE:
            begin
                i2c_sda_reg <=  1'b1;
                rd_data_reg <=  'd0;
            end
        START_1:
            if(cnt_i2c_clk <= 2'd0)
                i2c_sda_reg <=  1'b1;
            else
                i2c_sda_reg <=  1'b0;
        SEND_D_ADDR:
            if(cnt_bit <= 3'd7)
                i2c_sda_reg <=  DEVICE_ADDR_W[7 - cnt_bit];
            else
                i2c_sda_reg <=  1'b0;
        ACK_1:
            i2c_sda_reg <=  1'b1;
       
        POINT_W:
            i2c_sda_reg <=  POINT_CMD_WR[7 - cnt_bit];
        ACK_W:
            i2c_sda_reg <=  1'b1;
        CFG_R:
            i2c_sda_reg <=  CONFIG_REG[7 - cnt_bit];

        POINT_R:
            i2c_sda_reg <=  POINT_CMD_RD[7 - cnt_bit];

        ACK_3:
            i2c_sda_reg <=  1'b1;

        START_2:
            if(cnt_i2c_clk <= 2'd1)
                i2c_sda_reg <=  1'b1;
            else
                i2c_sda_reg <=  1'b0;
        SEND_RD_ADDR:
            if(cnt_bit <= 3'd7)
                i2c_sda_reg <=  DEVICE_ADDR_R[7 - cnt_bit];
            else
                i2c_sda_reg <=  1'b1;
        ACK_5:
            i2c_sda_reg <=  1'b1;
        RD_DATA:
            if(cnt_i2c_clk  == 2'd2)
                rd_data_reg[15 - cnt_bit]    <=  sda_in;
            else
                rd_data_reg <=  rd_data_reg;
        N_ACK:
            i2c_sda_reg <=  ack;
        RD_DATA1:
            if(cnt_i2c_clk  == 2'd2)
                rd_data_reg[7 - cnt_bit]    <=  sda_in;
            else
                rd_data_reg <=  rd_data_reg;
        N_ACK1:
            i2c_sda_reg <=  ack;
        STOP:
            if((cnt_bit == 3'd0) && (cnt_i2c_clk < 2'd3))
                i2c_sda_reg <=  1'b0;
            else
                i2c_sda_reg <=  1'b1;
        default:
            begin
                i2c_sda_reg <=  1'b1;
                rd_data_reg <=  rd_data_reg;
            end
    endcase

// rd_data:自i2c设备读出数据
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_data_r <=  'd0;
    else    if((state == RD_DATA) && (cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
        rd_data_r[15:8] <=  rd_data_reg[15:8];
    else    if((state == RD_DATA1) && (cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
        rd_data_r[7:0] <=  rd_data_reg[7:0];

// i2c_end:一次读/写结束信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        i2c_end <=  1'b0;
    else    if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
        i2c_end <=  1'b1;
    else
        i2c_end <=  1'b0;

// sda_in:sda输入数据寄存
assign  sda_in = i2c_sda;
// sda_en:sda数据写入使能信号
assign  sda_en = ((state == RD_DATA) || (state == RD_DATA1)||
                  (state == ACK_1  ) || (state == ACK_W   )||
                  (state == ACK_3  ) || (state == ACK_5   ))? 1'b0 : 1'b1;
// i2c_sda:输出至i2c设备的串行数据信号sda
assign  i2c_sda = (sda_en == 1'b1) ? i2c_sda_reg : 1'bz;



//打拍去ila中观察的信号
always @(posedge i2c_clk or posedge sys_rst_n) begin
    if (sys_rst_n == 1'b0) begin
        i2c_sda_reg_r<=1'b0;
        ack_r<= 1'b0;       
    end
    else begin
        i2c_sda_reg_r<=i2c_sda_reg;
        ack_r<= ack;        
    end
end

ila_0 your_instance_name (
    .clk(sys_clk), // input wire clk

    .probe0 (i2c_scl           ),  // input wire [0:0]   probe0  
    .probe1 (i2c_sda           ),  // input wire [0:0]   probe1 
    .probe2 (rd_data_r         ),  // input wire [15:0]  probe2 
    .probe3 (state             ),  // input wire [4:0]   probe3 
    .probe4 (i2c_sda_reg_r     ),  // input wire [0:0]   probe4 
    .probe5 (cnt_bit           ),  // input wire [2:0]   probe5 
    .probe6 (cnt_i2c_clk       ),  // input wire [1:0]   probe6 
    .probe7 (ack_r             ),  // input wire [0:0]   probe7 
    .probe8 (sda_in            ),  // input wire [0:0]   probe8
    .probe9 (rd_en             ),  // input wire [0:0]   probe9
    .probe10(wr_en             ),  // input wire [0:0]   probe10
    .probe11(i2c_start         ),  // input wire [0:0]   probe11
    .probe12(cnt_i2c_clk_en    ),  // input wire [0:0]   probe12
    .probe13(sda_en            )   // input wire [0:0]   probe13  
);

endmodule

代码完成之后,建一个顶层例化两个按键消抖模块,然后连接好就可以了在线调试了。首先按下写的按键,配置好Configuration Register寄存器,然后在按下复位按键,注意复位按键不要绑定到板卡的系统复位,先按下写的按键,接着在按下读的按键,就可以读到温度了。使用打火机对芯片加温后,按下复位再按下读的按键,即可得到新的温度了。下面是ila抓到的温度是31.32摄氏度,这是当时冬天的室内温度。

芯片emmc寿命怎么查看_单片机_12