芯片简介

  SHT3x-DIS 是一款温湿度传感器,I2C 接口,通信速度最高可达 1MHz,测量精度 ±1.5%RH,±0.1∘C\pm 1.5\%\mathrm{RH},\ \pm 0.1^\circ C±1.5%RH, ±0.1∘C 。数字输出经过校准和线性化,并进行了温度补偿。

SHT3x-DIS 内部结构及电路示意如上图,由于 SDA 与SCL 为开漏,因此须接上拉电阻。除此之外,SDA 与 SCL 应串联限流电阻。

引脚说明:

  • SDA

  I2C 串行数据总线,双向,最高支持 1MHz1\rm MHz1MHz,大于 400kHz400\rm kHz400kHz 的通信需符合 I2C 快速模式标准。

  • SCL

  I2C 串行时钟总线,双向,支持在 0∼1MHz0\sim1\mathrm{MHz}0∼1MHz 间自由选择。

  • nRESET

  复位引脚,低电平有效,复位脉冲至少维持 1us 才能可靠地使芯片复位。若不使用建议悬空(芯片内部有 50kΩ50k\Omega50kΩ 的上拉电阻),或接 2kΩ2k\Omega2kΩ 以上的上拉电阻到 VDD。

  • ALERT

  告警引脚,当温湿度超过设置的阈值时,该引脚给出高电平。若不使用,必须悬空。(datasheet 提到告警阈值是可编程的,但是没看到给出设置的方法)

  • ADDR

  地址引脚,当该引脚接地时,设置 I2C 设备地址为 0x44,当该引脚拉高时,设置地址为 0x45。ADDR 引脚不可悬空,必须接地或接高电平。

  • VDD

  供电引脚,2.15V 到 5.5V。

  • VSS

  Ground。

  • R

  Reserved,无电气作用,接地。

采样模式

  SHT3x-DIS 支持 I2C 快速模式,最高 1MHz。向传感器发送命令后,至少 1ms 后才可发送下一条命令。

  SH3x-DIS 的数据和命令都映射到 16bit 的地址空间,并使用 CRC 校验进行保护。16bit 命令已经包含了 3bit 的 CRC 校验和,同时传感器的每次数据接收与发送,均使用 8 位 CRC 校验。

  当进行写操作时,必须提供 CRC 校验,SHT-3x-DIS 只接受具有正确校验和的数据;当进行读操作时,SHT3x-DIS 提供 8bit CRC 校验。

  当芯片上电后,经过一段时间后(1ms@5V,1.5ms@2.4V)自动进入空闲状态,准备好接收指令。在未处在接收指令或测量状态时,芯片自动进入空闲状态,以实现节能。

  测量通信序列由 7bit 设备地址,0 作为写入位,16bit 测量命令组成。传感器对每个字节的正确接收做出确认,在第 8 个 SCL 下降沿后给出 SDA=L 以示正确接收到了该字节。在接收完测量命令后,传感器开始测量湿度和温度。

单次采集模式

  该模式下,传感器每接收到一次测量命令,进行一次温湿度测量,温度和湿度均使用 16bit 数字量表示。

  单次采集模式的 16bit 指令如下表所示

  • 可重复性(Repeatability)

  可重复性影响采样的持续时间,重复性越高,测量时间越长(功耗越大),测量精度越高。三种可重复性下的测量时间以及对应的温湿度测量精度如下表



  • 时钟拉伸(Clock Stretching)

  在 disable Clock Stretching 时,若 Master 发出读取报头时传感器尚未完成数据采样,将回复不确认 NACK (SDA=H)。

  当 enable Clock Stretching 时,传感器相应读报头,随后下拉 SCL ,直到测量完成。一旦数据测量完成,传感器将释放 SCL,并传输测量数据。

  两种时钟拉伸状态下的传输时序如上图,白色部分由微控制器给出,灰色部分由传感器给出。

  上分支所示是 Clock Stretching disabled 的情况,若数据尚未完成采样,传感器 NACK 读报头;当数据准备好之后,Mater 需再次发送读报文头,传感器 ACK 读报文头,并依次给出温度、湿度数据。

  下分支对应 Clock Stretching enabled 的情况,Master 只需要发出一次读报文头,若传感器数据尚未准备好,将 NACK 读报文头,并强制拉低 SCL 以终止数据传输,在数据准备好后释放 SCL,并随着 SCL 依次给出温度、湿度数据。

  16bit 数字量与实际物理量的对应关系如下

  • 相对湿度

RH=SRH216−1×100%RH=\frac{S_{RH}}{2^{16}-1}\times100\% RH=216−1SRH​​×100%

  • 温度

T(∘C)=−45+175⋅ST216−1T(^\circ C)=-45+175\cdot\frac{S_T}{2^{16}-1} T(∘C)=−45+175⋅216−1ST​​

周期采集模式

  此模式下,一个测量命令将产生一个测量数据流,每个数据对由 16bit 温度和 16bit 湿度组成。

  • 周期采集命令

  周期采集模式的命令如下

表中 mps 表示每秒采集几次(measurement per second),有 0.5,1,2,4,10mps0.5,1,2,4,10\ mps0.5,1,2,4,10 mps 共 5 种测量频率。周期采集模式下,没有 Clock Stretching。

  周期采集模式还有一个 ART (accelerated response time) 模式,发出 ART 命令后,传感器将以 4Hz 的频率进行数据采集。

  • 读取周期采集数据

  要读取数据,使用如下的读取时序(注意 Fetch Data Command 后没有停止信号 P,而是紧接着一个 restart 信号 S,随后给出读报文头)

若传感器内没有数据,I2C 读取头将回应 NACK,通信停止。若有数据,将回应 ACK,并给出温湿度数据,读取完成后将清空数据存储器

  • 终止周期采集模式

  采用如下指令终止周期采集模式,回到 Single Shot 模式。恢复为单次采集模式至少要 1ms。

复位 Reset

  SHT3x-DIS 有如下几种复位方式:

  • Interface Reset

  接口复位,保持 SDA=H,切换 SCL 9次以上。该复位仅重置接口,不清空采样数据存储器。

  • Soft Reset

  软复位,通过发送命令进行复位,软复位将重置传感器,并重新加载校准数据。

  • Reset through General Call

  通过 I2C 一般呼叫模式产生复位,功能上与通过 nRESET 复位相同。这种复位会使挂载在同一根 I2C 总线上的所有支持一般呼叫的设备重置。一般呼叫的 I2C 序列如下

  • Reset through the nReset Pin

  拉低 nRESET 引脚至少 1us 可使 SHT3x-DIS 复位。

  • Hard Reset

  硬复位,下电后重新上电。

加热器 Heater

  SHT3x-DIS 内部有一个加热器,仅用于合理性验证(而不是为了什么保持传感器工作温度之类的),默认关闭。通过以下命令开关加热器。无法控制加热到多高温度,温度变化在几摄氏度(受环境影响)。

状态寄存器

  状态寄存器(16bit)包含关于加热器的运行状态、警报模式以及最后一个命令和最后一个写入序列的执行状态等信息。

  读取状态寄存器的命令如下

寄存器含义如下

通过如下命令清空状态寄存器

FPGA驱动实现

SHT3x-DIS 驱动代码

  • SHT3x_singleShot.v
/* * file            : SHT3x_singleShot.v* author        : 今朝无言* Lab         : WHU-EIS-LMSWE* date           : 2023-03-20* version       : v1.0* description : SHT3x-DISn单采集模式 (Single Shot Mode)* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
module SHT3x_singleShot(
input               clk_1M,         //4倍SCL,采用1MHz时钟,SCL_freq = 250kHz
input               rst_n,input             samp_en,        //上升沿有效
output              busy,
output              alert,output    reg [15:0]  T,              //温度,数字量
output  reg [15:0]  RH,             //相对湿度,数字量inout              SCL,
inout               SDA,
input               ALERT,
output              nRESET
);
// 物理量 <- 数字量
// T(℃)=-45+175*S_T/65535
// RH=S_RH/65535*100%localparam    ADDR                = 7'h44;      //I2C设备地址
localparam  RW_W                = 1'b0;
localparam  RW_R                = 1'b1;
localparam  CMD_SINGLE_SHOT     = 16'h2400;       //Sigle Shot, High Repeatability, Clock Stretching Disabled//---------------------I2C Master State Define----------------------
// I2C Master sub 状态定义
localparam  IDLE        = 8'h01;  //空闲,释放SCL/SDA
localparam  START       = 8'h02;  //起始,SCL=H,SDA=D
localparam  SEND_DATA   = 8'h04;  //发送数据
localparam  GET_DATA    = 8'h08;  //读取数据,释放SDA
localparam  CHECK_ACK   = 8'h10;  //检查SDA的ACK/NACK,释放SDA
localparam  ACK         = 8'h20;  //发出ACK,SDA=L
localparam  NACK        = 8'h40;  //发出NACK,SDA=H
localparam  STOP        = 8'h80;  //停止,SCL=H,SDA=R//------------------------SHT State Define-------------------------
// ---Send Command---
//IDLE,START,SEND_ADDR_W,CHECK_ACK1,SEND_COMMAND_MSB,CHECK_ACK2,SEND_COMMAND_LSB,CHECK_ACK3,STOP
localparam  C_IDLE              = 8'h00;
localparam  C_START             = 8'h01;
localparam  C_SEND_ADDR_W       = 8'h02;
localparam  C_CHECK_ACK1        = 8'h03;
localparam  C_SEND_COMMAND_MSB  = 8'h04;
localparam  C_CHECK_ACK2        = 8'h05;
localparam  C_SEND_COMMAND_LSB  = 8'h06;
localparam  C_CHECK_ACK3        = 8'h07;
localparam  C_STOP              = 8'h08;// ---Get Data---
//IDLE,START,SEND_ADDR_R,CHECK_ACK,GET_T_MSB,ACK1,GET_T_LSB,ACK2,GET_CRC1,ACK3,
//GET_RH_MSB,ACK4,GET_RH_LSB,ACK5,GET_CRC2,ACKH6,STOP
localparam  D_IDLE              = 8'h10;
localparam  D_START             = 8'h11;
localparam  D_SEND_ADDR_R       = 8'h12;
localparam  D_CHECK_ACK         = 8'h13;
localparam  D_GET_T_MSB         = 8'h14;
localparam  D_ACK1              = 8'h15;
localparam  D_GET_T_LSB         = 8'h16;
localparam  D_ACK2              = 8'h17;
localparam  D_GET_CRC1          = 8'h18;
localparam  D_ACK3              = 8'h19;
localparam  D_GET_RH_MSB        = 8'h1a;
localparam  D_ACK4              = 8'h1b;
localparam  D_GET_RH_LSB        = 8'h1c;
localparam  D_ACK5              = 8'h1d;
localparam  D_GET_CRC2          = 8'h1e;
localparam  D_ACK6              = 8'h1f;
localparam  D_STOP              = 8'h20;// ---SHT3x state---
localparam  SHT_IDLE            = 8'h01;
localparam  SHT_COMMAND_START   = 8'h02;  // IDLE -> START
localparam  SHT_SEND_COMMAND    = 8'h04;  // I2C busy, Send Command
localparam  SHT_SAMPLING        = 8'h08;  // wait 20ms
localparam  SHT_DATA_START      = 8'h10;  // IDLE -> START
localparam  SHT_GET_DATA        = 8'h20;  // I2C busy, Get Data
localparam  SHT_STOP            = 8'h40;  // T <- T_tmp, RH <- RH_tmp//------------------------------------------------------------------
reg     [7:0]   I2C_state       = IDLE;        //该state输入I2C_Master_subreg     [7:0]   SHT_state       = SHT_IDLE;
reg     [7:0]   SHT_next_state  = SHT_IDLE;reg     [7:0]   C_state         = C_IDLE;
reg     [7:0]   C_next_state    = C_IDLE;reg       [7:0]   D_state         = D_IDLE;
reg     [7:0]   D_next_state    = D_IDLE;reg               start_flag      = 1'b0;
reg             C_start_flag    = 1'b0;
reg             D_start_flag    = 1'b0;reg        [7:0]   wrdat_buf       = 8'd0;
wire    [7:0]   rddat_tmp;
wire            check_ack;wire          change_state;reg        [15:0]  T_tmp           = 16'd0;
reg     [15:0]  RH_tmp          = 16'd0;//--------------------------state trans------------------------------
always @(posedge change_state or negedge rst_n) beginif(~rst_n) beginSHT_state     <= SHT_IDLE;endelse beginSHT_state      <= SHT_next_state;end
endalways @(posedge change_state) begincase(SHT_state)SHT_COMMAND_START, SHT_SEND_COMMAND: beginC_state            <= C_next_state;enddefault: beginC_state            <= C_state;endendcase
endalways @(posedge change_state) begincase(SHT_state)SHT_DATA_START, SHT_GET_DATA: beginD_state           <= D_next_state;enddefault: beginD_state            <= D_state;endendcase
end//--------------------Send Command State Machine---------------------
always @(*) begincase(C_state)C_IDLE: beginif(C_start_flag) beginC_next_state  <= C_START;endelse beginC_next_state    <= C_IDLE;endendC_START: beginC_next_state  <= C_SEND_ADDR_W;endC_SEND_ADDR_W: beginC_next_state    <= C_CHECK_ACK1;endC_CHECK_ACK1: beginC_next_state  <= C_SEND_COMMAND_MSB;endC_SEND_COMMAND_MSB: beginC_next_state  <= C_CHECK_ACK2;endC_CHECK_ACK2: beginC_next_state  <= C_SEND_COMMAND_LSB;endC_SEND_COMMAND_LSB: beginC_next_state  <= C_CHECK_ACK3;endC_CHECK_ACK3: beginC_next_state  <= C_STOP;endC_STOP: beginC_next_state  <= C_IDLE;enddefault: beginC_next_state <= C_IDLE;endendcase
end//----------------------Get Data State Machine-----------------------
always @(*) begincase(D_state)D_IDLE: beginif(D_start_flag) beginD_next_state  <= D_START;endelse beginD_next_state    <= D_IDLE;endendD_START: beginD_next_state  <= D_SEND_ADDR_R;endD_SEND_ADDR_R: beginD_next_state    <= D_CHECK_ACK;endD_CHECK_ACK: beginD_next_state    <= D_GET_T_MSB;endD_GET_T_MSB: beginD_next_state    <= D_ACK1;endD_ACK1: beginD_next_state  <= D_GET_T_LSB;endD_GET_T_LSB: beginD_next_state    <= D_ACK2;endD_ACK2: beginD_next_state  <= D_GET_CRC1;endD_GET_CRC1: beginD_next_state  <= D_ACK3;endD_ACK3: beginD_next_state  <= D_GET_RH_MSB;endD_GET_RH_MSB: beginD_next_state  <= D_ACK4;endD_ACK4: beginD_next_state  <= D_GET_RH_LSB;endD_GET_RH_LSB: beginD_next_state  <= D_ACK5;endD_ACK5: beginD_next_state  <= D_GET_CRC2;endD_GET_CRC2: beginD_next_state  <= D_ACK6;endD_ACK6: beginD_next_state  <= D_STOP;endD_STOP: beginD_next_state  <= D_IDLE;enddefault: beginD_next_state <= D_IDLE;endendcase
end//----------------------I2C State Machine-----------------------
always @(*) begincase({C_state,D_state}){C_IDLE, D_IDLE}: beginI2C_state   <= IDLE;end//---Send Command---{C_START, D_IDLE}: beginI2C_state    <= START;end{C_SEND_ADDR_W, D_IDLE}: beginI2C_state <= SEND_DATA;end{C_CHECK_ACK1, D_IDLE}: beginI2C_state  <= CHECK_ACK;end{C_SEND_COMMAND_MSB, D_IDLE}: beginI2C_state    <= SEND_DATA;end{C_CHECK_ACK2, D_IDLE}: beginI2C_state  <= CHECK_ACK;end{C_SEND_COMMAND_LSB, D_IDLE}: beginI2C_state    <= SEND_DATA;end{C_CHECK_ACK3, D_IDLE}: beginI2C_state  <= CHECK_ACK;end{C_STOP, D_IDLE}: beginI2C_state    <= STOP;end//---Get Data---{C_IDLE, D_START}: beginI2C_state    <= START;end{C_IDLE, D_SEND_ADDR_R}: beginI2C_state <= SEND_DATA;end{C_IDLE, D_CHECK_ACK}: beginI2C_state   <= CHECK_ACK;end{C_IDLE, D_GET_T_MSB}: beginI2C_state   <= GET_DATA;end{C_IDLE, D_ACK1}: beginI2C_state <= ACK;end{C_IDLE, D_GET_T_LSB}: beginI2C_state <= GET_DATA;end{C_IDLE, D_ACK2}: beginI2C_state <= ACK;end{C_IDLE, D_GET_CRC1}: beginI2C_state  <= GET_DATA;end{C_IDLE, D_ACK3}: beginI2C_state <= ACK;end{C_IDLE, D_GET_RH_MSB}: beginI2C_state    <= GET_DATA;end{C_IDLE, D_ACK4}: beginI2C_state <= ACK;end{C_IDLE, D_GET_RH_LSB}: beginI2C_state    <= GET_DATA;end{C_IDLE, D_ACK5}: beginI2C_state <= ACK;end{C_IDLE, D_GET_CRC2}: beginI2C_state  <= GET_DATA;end{C_IDLE, D_ACK6}: beginI2C_state <= ACK;end{C_IDLE, D_STOP}: beginI2C_state  <= STOP;enddefault: beginI2C_state  <= IDLE;endendcase
end//--------------------------SHT State Machine--------------------------
always @(*) begincase(SHT_state)SHT_IDLE: beginif(start_flag) beginSHT_next_state  <= SHT_COMMAND_START;endelse beginSHT_next_state    <= SHT_IDLE;endendSHT_COMMAND_START: beginSHT_next_state        <= SHT_SEND_COMMAND;endSHT_SEND_COMMAND: beginif(C_state==C_IDLE) beginSHT_next_state <= SHT_SAMPLING;endelse beginSHT_next_state <= SHT_SEND_COMMAND;endendSHT_SAMPLING: beginif(~delay_busy) beginSHT_next_state    <= SHT_DATA_START;endelse beginSHT_next_state   <= SHT_SAMPLING;endendSHT_DATA_START: beginSHT_next_state       <= SHT_GET_DATA;endSHT_GET_DATA: beginif(D_state==D_IDLE) beginSHT_next_state <= SHT_STOP;endelse beginSHT_next_state <= SHT_GET_DATA;endendSHT_STOP: beginSHT_next_state     <= SHT_IDLE;enddefault: beginSHT_next_state     <= SHT_IDLE;endendcase
end//---------------------------I2C Master sub----------------------------
I2C_Master_sub I2C_Master_sub_inst(.clk         (clk_1M),.wrdat_buf     (wrdat_buf),.rddat_tmp      (rddat_tmp),.check_ack      (check_ack),.SCL            (SCL),.SDA          (SDA),.change_state (change_state),.state           (I2C_state)
);//---------------------------Delay----------------------------
reg     delay_en    = 1'b0;
wire    delay_busy;monostable_flipflop #(.Width(32))
monostable_flipflop_inst(.clk       (clk_1M),.rst_n     (1'b1),.delay_N    (32'd100_000),     //delay 100ms.reset_N   (1'b1),.in         (delay_en),.out     (delay_busy)
);//------------------------------Control-------------------------------// ---samp_en edge detect---
reg     samp_en_d0;
reg     samp_en_d1;
wire    samp_en_pe;always @(posedge clk_1M) beginsamp_en_d0    <= samp_en;samp_en_d1   <= samp_en_d0;
endassign   samp_en_pe  = samp_en_d0 & (~samp_en_d1);// ---start flag---
//启动samp进程
always @(posedge clk_1M) beginif(samp_en_pe && ~busy) beginstart_flag  <= 1'b1;endelse if(SHT_state == SHT_COMMAND_START) beginstart_flag   <= 1'b0;endelse beginstart_flag    <= start_flag;end
end// ---busy---
assign  busy = (SHT_state==SHT_IDLE)? 1'b0 : 1'b1;// ---Send Command start flag---
always @(*) begincase(SHT_state)SHT_COMMAND_START: beginC_start_flag   <= 1'b1;enddefault: beginC_start_flag  <= 1'b0;endendcase
end// ---Get Data start flag---
always @(*) begincase(SHT_state)SHT_DATA_START: beginD_start_flag  <= 1'b1;enddefault: beginD_start_flag  <= 1'b0;endendcase
end// ---delay_en---
always @(*) beginif(C_state==C_STOP) begindelay_en   <= 1'b1;endelse begindelay_en  <= 1'b0;end
end// ---T_tmp---
always @(posedge clk_1M) begincase(D_state)D_START: beginT_tmp         <= 16'd0;endD_ACK1: beginT_tmp[15:8]       <= rddat_tmp;endD_ACK2: beginT_tmp[7:0]     <= rddat_tmp;enddefault: beginT_tmp         <= T_tmp;endendcase
end// ---RH_tmp---
always @(posedge clk_1M) begincase(D_state)D_START: beginRH_tmp            <= 16'd0;endD_ACK4: beginRH_tmp[15:8]  <= rddat_tmp;endD_ACK5: beginRH_tmp[7:0]        <= rddat_tmp;enddefault: beginRH_tmp            <= RH_tmp;endendcase
end// ---wrdat_buf---
always @(posedge clk_1M) begincase({C_state, D_state}){C_IDLE, D_IDLE}: beginwrdat_buf <= 8'd0;end{C_START, D_IDLE}: beginwrdat_buf   <= {ADDR, RW_W};end{C_CHECK_ACK1, D_IDLE}: beginwrdat_buf   <= CMD_SINGLE_SHOT[15:8];end{C_CHECK_ACK2, D_IDLE}: beginwrdat_buf  <= CMD_SINGLE_SHOT[7:0];end{C_CHECK_ACK3, D_IDLE}: beginwrdat_buf   <= 8'd0;end{C_IDLE, D_START}: beginwrdat_buf   <= {ADDR, RW_R};end{C_IDLE, D_CHECK_ACK}: beginwrdat_buf    <= 8'd0;enddefault: beginwrdat_buf <= wrdat_buf;endendcase
end// ---T/RH---
always @(posedge clk_1M) begincase(SHT_state)SHT_STOP: beginT      <= T_tmp;           //缓存T/RH,给出稳定的输出结果RH     <= RH_tmp;enddefault: beginT        <= T;RH     <= RH;endendcase
end//-------------------------alert & nReset----------------------------
assign  alert   = ALERT;
assign  nRESET  = 1'b1;endmodule
  • I2C_Master_sub.v

  该模块是 I2C Master 的 SCL/SDA 状态输出控制模块,关于 I2C 如何使用 Verilog 实现详见我之前的博文

/* * file            : I2C_Master_sub.v* author      : 今朝无言* Lab         : WHU-EIS-LMSWE* date           : 2023-03-19* version       : v1.0* description : I2C master 的 SDA/SCL 控制模块(通过 state)*/
module I2C_Master_sub(
input               clk,            //4倍SCLinput        [7:0]   wrdat_buf,
output  reg [7:0]   rddat_tmp,
output  reg         check_ack,      //检查Slave给出的ACK信号,若为NACK,输出一个高电平脉冲inout               SCL,
inout               SDA,output  reg         change_state,   //上升沿时 top 模块应执行 state <- next_state
input       [7:0]   state
);localparam    IDLE        = 8'h01;  //空闲,释放SCL/SDA
localparam  START       = 8'h02;  //起始,SCL=H,SDA=D
localparam  SEND_DATA   = 8'h04;  //发送数据
localparam  GET_DATA    = 8'h08;  //读取数据,释放SDA
localparam  CHECK_ACK   = 8'h10;  //检查SDA的ACK/NACK,释放SDA
localparam  ACK         = 8'h20;  //发出ACK,SDA=L
localparam  NACK        = 8'h40;  //发出NACK,SDA=H
localparam  STOP        = 8'h80;  //停止,SCL=H,SDA=Rreg             SCL_link    = 1'b0;
reg             SDA_link    = 1'b0;reg                SCL_buf     = 1'b1;   //o_buf
reg             SDA_buf     = 1'b1;wire           SCL_ibuf;           //i_buf
wire            SDA_ibuf;reg        [3:0]   bit_cnt     = 4'd15;//----------------------IO_BUF-----------------------------
//IOBUF for SCL
IOBUF IOBUF_SCL(.O      (SCL_ibuf),     // Buffer的输出,接采集信号.IO        (SCL),          // connect directly to top-level port.I     (SCL_buf),      // Buffer的输入,接要输出到FPGA外的信号.T     (~SCL_link)     // =1时,O <- IO;=0时,IO <- I
);//IOBUF for SDA
IOBUF IOBUF_SDA(.O      (SDA_ibuf),.IO      (SDA),.I        (SDA_buf),.T        (~SDA_link)
);//---------------------clk div-----------------------------
//将一个SCL周期划分为4份,便于逻辑实现
reg     [1:0]   clk_cnt = 2'd0;always @(posedge clk) beginclk_cnt        <= clk_cnt + 1'b1;
end//---------------------SCL_link-----------------------------
always @(posedge clk) begincase(state)IDLE: beginSCL_link  <= 1'b0;endSTART, SEND_DATA, GET_DATA, CHECK_ACK, ACK, NACK, STOP: beginSCL_link   <= 1'b1;enddefault: beginSCL_link  <= 1'b0;endendcase
end//---------------------SDA_link-----------------------------
always @(posedge clk) begincase(state)IDLE, GET_DATA, CHECK_ACK: beginSDA_link <= 1'b0;endSTART, SEND_DATA, ACK, NACK, STOP: beginSDA_link    <= 1'b1;enddefault: beginSDA_link  <= 1'b0;endendcase
end//---------------------SCL_buf-----------------------------
always @(posedge clk) begincase(state)IDLE: begin                                          //1111SCL_buf       <= 1'b1;endSTART: begin                                        //1110case(clk_cnt)2'd0, 2'd1, 2'd2: beginSCL_buf        <= 1'b1;end2'd3: beginSCL_buf     <= 1'b0;enddefault: ;endcaseendSTOP: begin                                         //0111case(clk_cnt)2'd1, 2'd2, 2'd3: beginSCL_buf        <= 1'b1;end2'd0: beginSCL_buf     <= 1'b0;enddefault: ;endcaseendSEND_DATA, GET_DATA, CHECK_ACK, ACK, NACK: begin    //0110case(clk_cnt)2'd1, 2'd2: beginSCL_buf       <= 1'b1;end2'd0, 2'd3: beginSCL_buf      <= 1'b0;enddefault: ;endcaseenddefault: begin                                      //1111SCL_buf       <= 1'b1;endendcase
end//---------------------bit_cnt-----------------------------
always @(posedge clk) begincase(state)SEND_DATA, GET_DATA: begincase(clk_cnt)2'd2: beginbit_cnt       <= bit_cnt - 1'b1;enddefault: ;endcaseendSTART, ACK, NACK, CHECK_ACK: beginbit_cnt     <= 4'd7;enddefault: beginbit_cnt       <= 4'd15;endendcase
end//--------------------rddat_tmp----------------------------
always @(posedge clk) begincase(state)GET_DATA: begincase(clk_cnt)2'd1: beginrddat_tmp[bit_cnt]   <= SDA_ibuf;enddefault: ;endcaseenddefault: beginrddat_tmp  <= rddat_tmp;endendcase
end//--------------------check_ack----------------------------
always @(posedge clk) begincase(state)CHECK_ACK: begincase(clk_cnt)2'd1: begincheck_ack   <= SDA_ibuf;enddefault: begincheck_ack  <= check_ack;endendcaseenddefault: begincheck_ack   <= 0;endendcase
end//---------------------SDA_buf-----------------------------
always @(posedge clk) begincase(state)IDLE: beginSDA_buf       <= 1'b1;endSTART: begin                                        //1100,从而在SCL=H时,产生SDA=Dcase(clk_cnt)2'd0, 2'd1: beginSDA_buf     <= 1'b1;end2'd2, 2'd3: beginSDA_buf      <= 1'b0;enddefault: ;endcaseendSEND_DATA: begin                                    //在clk_cnt=0给出数据,从而在clk_cnt=1,2时(SCL=H)保持SDA的稳定case(clk_cnt)2'd0: beginSDA_buf       <= wrdat_buf[bit_cnt];enddefault: ;endcaseendGET_DATA: beginSDA_buf     <= 1'b1;endCHECK_ACK: beginSDA_buf     <= 1'b0;endACK: beginSDA_buf       <= 1'b0;endNACK: beginSDA_buf      <= 1'b1;endSTOP: begin                                         //0011,从而在SCL=H时,产生SDA=Rcase(clk_cnt)2'd0, 2'd1: beginSDA_buf     <= 1'b0;end2'd2, 2'd3: beginSDA_buf      <= 1'b1;enddefault: ;endcaseenddefault: beginSDA_buf       <= 1'b1;endendcase
end//-------------------change_state---------------------------
always @(posedge clk) begincase(state)IDLE, ACK, NACK, CHECK_ACK, STOP: begincase(clk_cnt)2'd3: beginchange_state <= 1'b1;enddefault: beginchange_state  <= 1'b0;endendcaseendSEND_DATA, GET_DATA: begincase(bit_cnt)4'd15: begincase(clk_cnt)2'd3: beginchange_state <= 1'b1;enddefault: beginchange_state  <= 1'b0;endendcaseenddefault: beginchange_state    <= 1'b0;endendcaseenddefault: begincase(clk_cnt)2'd3: beginchange_state   <= 1'b1;enddefault: beginchange_state  <= 1'b0;endendcaseendendcase
endendmodule
  • monostable_flipflop.v

  单稳态电路,用于实现延时。单采集模式下,Sampling 阶段会持续 20 ms 左右,期间 SHT3x-DIS 会 NACK 读请求头

/* * file            : monostable_flipflop.v* author     : 今朝无言* Lab         : WHU-EIS-LMSWE* date           : 2023-03-19* version       : v1.0* description : 单稳态触发器*/
module monostable_flipflop(
input                   clk,
input                   rst_n,input     [Width-1:0] delay_N,
input                   reset_N,input                   in,
input                   out
);
parameter Width = 32;reg       [Width-1:0] delay_N_buf = 1;
reg     [Width-1:0] cnt         = 0;always @(posedge clk or posedge reset_N) beginif(reset_N) begindelay_N_buf        <= delay_N;endelse begindelay_N_buf     <= delay_N_buf;end
endreg      in_d0;
reg     in_d1;
wire    in_pe;assign    in_pe   = in_d0 & (~in_d1);always @(posedge clk) beginin_d0   <= in;in_d1 <= in_d0;
endalways @(posedge clk or negedge rst_n) beginif(~rst_n) begincnt     <= 0;endelse if(in_pe) begincnt     <= delay_N_buf;endelse if(cnt > 0) begincnt      <= cnt - 1'b1;endelse begincnt     <= cnt;end
endassign   out = (|cnt)? 1 : 0;endmodule

实机测试结果

  笔者的 SHT35-DIS 芯片湿度采集似乎有问题,一直在跳变,温度采集是很稳定的,大概在 30∘C30^\circ C30∘C ,这是由于传感器芯片靠近 FPGA 的缘故。后来外接 SHT30-DIS 型号的温湿度芯片,测试结果正常(53%,22.5∘C22.5^\circ C22.5∘C)。

  • 参考文献

[1] Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf

温湿度传感器 SHT3x-DIS 详解 FPGA驱动相关推荐

  1. BH1750 光照传感器文档详解 及 驱动设计

    前言 最近接触到一个应用,需要在低功耗的产品上加上光照度采集,正好最近有接触到一款光照传感器 BH1750 ,性能价格都合适,那么今天就抽空来好好测试一下. 那么要写一篇测试文章,我会尽量以新手的角度 ...

  2. 读书笔记:详解FPGA人工智能的驱动引擎(石侃)

    最近读了一本关于我偶像的一本书,知名up主老石的一本书<详解FPGA人工智能的驱动引擎>,这本书写了老石对FPGA的一些心得和行业总结,个人觉得写的非常不错,第一次看还是有点难以深刻理解, ...

  3. 详解FPGA:人工智能时代的驱动引擎观后感

    详解FPGA:人工智能时代的驱动引擎观后感 本书大目录 第一章 延续摩尔定律 第二章 拥抱大数据的洪流 第三章 FPGA在人工智能时代的独特优势 第四章 更简单也更复杂--FPGA开发的新方法 第五章 ...

  4. 人体热释电红外传感器 PIR 原理详解

    人体热释电红外传感器 PIR 原理详解     在电子防盗.人体探测器领域中,被动式热释电红外探测器的应用非常广泛,因其价格低廉.技术性能稳定而受到广大用户和专业人士的欢迎.本文详细介绍了被动式热释电 ...

  5. 详解Linux-I2C驱动

    目录 一.LinuxI2C驱动--概述 1.1 写在前面 1.2 I2C 1.3 硬件 1.4 软件 1.5 参考 二.LinuxI2C驱动--I2C总线 2.1 I2C总线物理结构 2.2 I2C总 ...

  6. 详解Linux-I2C驱动(硬件原理\驱动分析\测试)

    目录 一.LinuxI2C驱动--概述 1.1 写在前面 1.2 I2C 1.3 硬件 1.4 软件 1.5 参考 二.LinuxI2C驱动--I2C总线 2.1 I2C总线物理结构 2.2 I2C总 ...

  7. 详解FPGA实现8b10b编码原理(含VHDL及verilog源码)

    首发自https://hifpga.com/%E9%97%AE%E9%A2%98/37599 为什么要推出8b/10b编码? 8b/10b最常见的是应用于光纤通讯和LVDS信号的.由于光模块光模块只能 ...

  8. ioctl函数详解(参数详解,驱动unlocked_ioctl使用、命令码如何封装)

    @ioctl函数详解 一.ioctl函数的原型 在用户空间的函数原型 #include <sys/ioctl.h> int ioctl(int d, int request, ...); ...

  9. 读 <<详解FPGA 人工只能时代的驱动引擎>>

    fpga 变化架构变化集成度越来越高EDA帮助减少工作量集成了更多IP3D堆叠FPGA->zynq(FPGA+处理器单元)->ACAP(FPGA+处理器单元+可编程AI加速引擎+片上网络) ...

最新文章

  1. python 原始数据输出函数 repr
  2. 015_Redis创建集群
  3. AJAX,只是一种过渡技术吗?
  4. python自动化构建工具_Python自动化构建工具scons使用入门笔记
  5. android 富文本框架_当微擎框架遇上uniapp,以一当十同时开发十个平台项目
  6. 人大金仓数据库sql语句_数据库SQL语句大全——最常用的SQL语句
  7. Amazon Lambda支持以简单队列服务作为事件源了
  8. 今日力推: Android 厨客APP / Android 趣刻App
  9. form空白及iframe空白处理
  10. 计算机领域的专利文件,计算机方面实用新型专利信息
  11. HDU5855(最大权闭合图构图技巧+裸的最大流)
  12. python简易版成绩管理系统_Python学生成绩管理系统简洁版
  13. 什么是CVR,CTR,CPC,CPA,ROI?
  14. 数字信号处理——DDS模块设计(3)
  15. 关于检测Windows电脑电池信息
  16. TRITTON天猫旗舰店开业 2月20日7折大促
  17. Pandas--melt和pivot
  18. make: Warning: File “xxx“ has modification time yyy s in the future
  19. react函数组件 更新自动展示和暴露方法给父组件
  20. TC校园邮箱禁止掉邮件自动转发功能

热门文章

  1. 项目经理如何有效管理项目预算?
  2. 免费送书啦!火遍全网的AI给老照片上色,这里有一份详细教程!
  3. 联发科mt8516价格_MT8516芯片资料介绍 ,MT8516处理器
  4. 在Python中用小波分析图像的哈希值
  5. 图像质量评价:感知建模——Image Quality Assessment: Unifying Structure and Texture Similarit
  6. 西门子s7-200smart——8.常开常闭按钮的用法
  7. Python语言基础与应用-北京大学 上机练习01
  8. #12V直流电源简单Get
  9. Springboot毕设项目基于大数据的毕业生去向追踪系统8lrp3java+VUE+Mybatis+Maven+Mysql+sprnig)
  10. 通信网络单元定级备案证明是什么?定级备案证明开具流程