sHT30的verilog驱动实现

首先根据SHT30的寄存器手册或者是相关的网站,看一下AHT30具体的通信时序,下面是AHT30的数据手册,如果有需要可以下载

再数据手册中可以看到引脚图,

和我们通信相关的是SDA,SCL,ADDR,ALERT

其中SCL和SDA是IIC通信的两条总线

ADDR是器件地址判定的线

ALERT是和报警相关的信号,超出某个阈值就拉高

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

PropertyValue
NameCRC-8
Width8 bit
Protected dataread and/or write data
Polynomial0x31 (x8 + x5 + x4 + 1)
Initialization0xFF
Reflect inputFalse
Reflect outputFalse
Final XOR0x00
ExamplesCRC (0xBEEF) = 0x92

  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 数字量与实际物理量的对应关系如下

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

周期采集命令
  周期采集模式的命令如下

表中 mps 表示每秒采集几次(measurement per second),有 0.5 , 1 , 2 , 4 , 10   m p s 0.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。

上面数据都是理论值,如果不想看的话,可以直接用一个单片机的例程跑一下,用逻辑分析仪抓一下数据,然后剩下就直接仿制数据

我用arduino跑的波形如下

初始化命令

测量开始命令

读取数据波形

根据上图就可以写对应的verilog的代码了

我这里实现的仍然用一个状态机

状态定义如下

	localparam  IDLE                =10'b00_0000_0001;//空闲状态
	localparam  WRITE_INIT          =10'b00_0000_0010;//写初始化命令
	localparam  WAIT_WRITE_INIT     =10'b00_0000_0100;//等待初始化写完命令
	localparam  START_MEASURE       =10'b00_0000_1000;//开始测量命令
	localparam  WAIT_START_MEASURE  =10'b00_0001_0000;//等待测量命令写完
	localparam  WAIT_10MS           =10'b00_0010_0000;//等待10ms时间
	localparam  WAIT_500MS          =10'b00_0100_0000;//等待500ms时间
	localparam  WAIT_20MS           =10'b00_1000_0000;//等待20ms时间
	localparam  READ_DATA           =10'b01_0000_0000;//读取数据
	localparam  WAIT_READ_DATA      =10'b10_0000_0000;//等待读取数据完成

空闲状态,上电后延时一段时间,等传感器稳定后开始初始化,这里取50ms时间的延时

			IDLE:
			begin
				if(cnta<T_50ms-1)
					cnta <= cnta+1'b1;
				else
					cnta <= 'd0;
				if(cnta==T_50ms-1)
					state <= WRITE_INIT;
				else
					state <= IDLE;
			end

发送初始化命令状态,配置本条指令要传送2个字节,数据是16'h30A2

			WRITE_INIT:
			begin
				cnta <= 'd0;
				state <= WAIT_WRITE_INIT;
				write_reg(3'd2,16'h30A2);
			end

等待初始化发送完成状态,就是等待上次初始化命令写完

			WAIT_WRITE_INIT:
			begin
				wrreg_req <= 1'b0;
				if(RW_Done)
					state <= WAIT_10MS;
				else
					state <= WAIT_WRITE_INIT;
			end

10ms延时等待

			WAIT_10MS:
			begin
				// if(cnta<T_10ms-1)
				// 	cnta <= cnta+1'b1;
				// else
				// 	cnta <= 'd0;
				// if(cnta==T_10ms-1)
				// 	state <= START_MEASURE;
				// else
				// 	state <= WAIT_10MS;
				if(start_en)
					state <= START_MEASURE;
				else
					state <= WAIT_10MS;
			end	

这里做了个选择,可以再10ms后自动开始采集,也可以再外部控制开始采集

发送开始采集命令,此命令是触发传感器进行测量,发送配置为2字节,数据为2400

			START_MEASURE:
			begin
				cnta <= 'd0;
				state <= WAIT_START_MEASURE;
				write_reg(3'd2,16'h2400);
			end

等待命令发送完成

			WAIT_START_MEASURE:
			begin
				wrreg_req <= 1'b0;
				if(RW_Done)
					state <= WAIT_500MS;
				else
					state <= WAIT_START_MEASURE;
			end

等待本次iic命令传输完后,再次进入延时,等待传感器进行一次的测量工作,上面波形是延迟500ms,理论上这个值可以减少,但是本设计对采集速度并没有太高要求,因此直接采用500ms

等待500ms后,开始读数据

			WAIT_500MS:
			begin
				if(cnta<T_500ms-1)
					cnta <= cnta+1'b1;
				else
					cnta <= 'd0;
				if(cnta==T_500ms-1)
					state <= READ_DATA;
				else
					state <= WAIT_500MS;
			end	

向SHT30读取数据,读取需要读取6个字节

			READ_DATA:
			begin
				state <= WAIT_READ_DATA;
				read_reg(3'd6);
			end

等待数据读取完成,得到读取的数据

			WAIT_READ_DATA:
			begin
				rdreg_req <= 1'b0;
				if(RW_Done)
				begin
					state <= WAIT_20MS;
					hum_reg <= rddata[23:8];
				end
				else
					state <= WAIT_READ_DATA;
			end

本设计中仅仅需要采集湿度,如果需要采集温度,可以再增加一个tem_reg的变量,根据下图进行配置

tem_reg = rddata[47:32];

数据读取完后,再次进入延时,启动下次传输或者进行其他操作。

本模块驱动也是使用了task的写法,使得代码更加简洁易懂,task的代码如下

	task write_reg;
		input [2:0]len;
		input [55:0] data;
		begin
			w_len <= len;
			case(len)
				1:wrdata <= {data[7:0],56'h0};
				2:wrdata <= {data[15:0],48'h0};
				3:wrdata <= {data[23:0],40'h0};
				4:wrdata <= {data[31:0],32'h0};
				5:wrdata <= {data[39:0],24'h0};
				6:wrdata <= {data[47:0],16'h0};
				7:wrdata <= {data,8'h0};
				default:wrdata <= {data,8'h0};
			endcase
			wrreg_req <= 1'b1;
		end
	endtask

	task read_reg;
		input [3:0]len;
		begin
			r_len <= len;
			rdreg_req <= 1'b1;
		end
	endtask

写任务就是根据写入的数据进行写数据的配置以及生成写请求wrreg_req

都任务是配置读取长度和生成读请求rdreg_req

数据处理:说过,16bit 数字量与实际物理量的对应关系为

由于FPGA不能处理小数,以及除法运算相对比较复杂,尤其是2^16这么大的位宽,因此对上式做一个数学处理,如理过程如下

最后乘以1000的目的是将0~1范围的湿度值转换为0~1000范围的湿度值,便于FPGA处理,这样在后面数据处理比较简单,可以在显示部分合适的位置(十位和个位之间)加上小数点,就可以变成常见的形式,上面的处理方法可以避免FPGA做大位宽的除法,因为除以2^32可以直接使用截位来实现,即将最后的结果右移32bit。

注意,经过处理后,FPGA还是要算一个大位宽的乘法,因此最后的乘法结果寄存器位宽要足够,不然运算可能会出错。

	reg [15:0] hum_reg;
	reg [41:0] hum_data;

我这里直接定义了42bit(位宽可以直接用两个乘数的位宽相加)

使用2^32没有很直接的原因,理论上说是要是2的幂次方就可以,这样FPGA就会不用做除法,

2的幂次一般来说选的越大,最后得到的结果越精确,但是对应需要FPGA的资源就越大。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值