温湿度传感器 SHT3x-DIS 详解 FPGA驱动
芯片简介
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驱动相关推荐
- BH1750 光照传感器文档详解 及 驱动设计
前言 最近接触到一个应用,需要在低功耗的产品上加上光照度采集,正好最近有接触到一款光照传感器 BH1750 ,性能价格都合适,那么今天就抽空来好好测试一下. 那么要写一篇测试文章,我会尽量以新手的角度 ...
- 读书笔记:详解FPGA人工智能的驱动引擎(石侃)
最近读了一本关于我偶像的一本书,知名up主老石的一本书<详解FPGA人工智能的驱动引擎>,这本书写了老石对FPGA的一些心得和行业总结,个人觉得写的非常不错,第一次看还是有点难以深刻理解, ...
- 详解FPGA:人工智能时代的驱动引擎观后感
详解FPGA:人工智能时代的驱动引擎观后感 本书大目录 第一章 延续摩尔定律 第二章 拥抱大数据的洪流 第三章 FPGA在人工智能时代的独特优势 第四章 更简单也更复杂--FPGA开发的新方法 第五章 ...
- 人体热释电红外传感器 PIR 原理详解
人体热释电红外传感器 PIR 原理详解 在电子防盗.人体探测器领域中,被动式热释电红外探测器的应用非常广泛,因其价格低廉.技术性能稳定而受到广大用户和专业人士的欢迎.本文详细介绍了被动式热释电 ...
- 详解Linux-I2C驱动
目录 一.LinuxI2C驱动--概述 1.1 写在前面 1.2 I2C 1.3 硬件 1.4 软件 1.5 参考 二.LinuxI2C驱动--I2C总线 2.1 I2C总线物理结构 2.2 I2C总 ...
- 详解Linux-I2C驱动(硬件原理\驱动分析\测试)
目录 一.LinuxI2C驱动--概述 1.1 写在前面 1.2 I2C 1.3 硬件 1.4 软件 1.5 参考 二.LinuxI2C驱动--I2C总线 2.1 I2C总线物理结构 2.2 I2C总 ...
- 详解FPGA实现8b10b编码原理(含VHDL及verilog源码)
首发自https://hifpga.com/%E9%97%AE%E9%A2%98/37599 为什么要推出8b/10b编码? 8b/10b最常见的是应用于光纤通讯和LVDS信号的.由于光模块光模块只能 ...
- ioctl函数详解(参数详解,驱动unlocked_ioctl使用、命令码如何封装)
@ioctl函数详解 一.ioctl函数的原型 在用户空间的函数原型 #include <sys/ioctl.h> int ioctl(int d, int request, ...); ...
- 读 <<详解FPGA 人工只能时代的驱动引擎>>
fpga 变化架构变化集成度越来越高EDA帮助减少工作量集成了更多IP3D堆叠FPGA->zynq(FPGA+处理器单元)->ACAP(FPGA+处理器单元+可编程AI加速引擎+片上网络) ...
最新文章
- python 原始数据输出函数 repr
- 015_Redis创建集群
- AJAX,只是一种过渡技术吗?
- python自动化构建工具_Python自动化构建工具scons使用入门笔记
- android 富文本框架_当微擎框架遇上uniapp,以一当十同时开发十个平台项目
- 人大金仓数据库sql语句_数据库SQL语句大全——最常用的SQL语句
- Amazon Lambda支持以简单队列服务作为事件源了
- 今日力推: Android 厨客APP / Android 趣刻App
- form空白及iframe空白处理
- 计算机领域的专利文件,计算机方面实用新型专利信息
- HDU5855(最大权闭合图构图技巧+裸的最大流)
- python简易版成绩管理系统_Python学生成绩管理系统简洁版
- 什么是CVR,CTR,CPC,CPA,ROI?
- 数字信号处理——DDS模块设计(3)
- 关于检测Windows电脑电池信息
- TRITTON天猫旗舰店开业 2月20日7折大促
- Pandas--melt和pivot
- make: Warning: File “xxx“ has modification time yyy s in the future
- react函数组件 更新自动展示和暴露方法给父组件
- TC校园邮箱禁止掉邮件自动转发功能
热门文章
- 项目经理如何有效管理项目预算?
- 免费送书啦!火遍全网的AI给老照片上色,这里有一份详细教程!
- 联发科mt8516价格_MT8516芯片资料介绍 ,MT8516处理器
- 在Python中用小波分析图像的哈希值
- 图像质量评价:感知建模——Image Quality Assessment: Unifying Structure and Texture Similarit
- 西门子s7-200smart——8.常开常闭按钮的用法
- Python语言基础与应用-北京大学 上机练习01
- #12V直流电源简单Get
- Springboot毕设项目基于大数据的毕业生去向追踪系统8lrp3java+VUE+Mybatis+Maven+Mysql+sprnig)
- 通信网络单元定级备案证明是什么?定级备案证明开具流程