一.芯片介绍
1.物理层
接口
AHT10的供电电压在1.8v~3.6v,其中推荐3.3v
2.协议层
AHT10是一个标准的IIC协议,其具体的时序如下:
在SCL为高电平时,SDA由高电平跳变到低电平,标志着一次数据的传输开始,SDA传输数据时必须在SCL低电平才可以跳变,SCL在高电平时SDA必须保持稳定,当1byte数据传输完成后,会有一个响应位。在SCL高电平时,如果SDA为低电平则表示响应有效,否则则是无响应,最后在SCL高电平时SDA从低电平跳转到高电平标志一次数据发送完成。
适配的时钟频率为:100KHZ 和400KHZ
二.AHT10测量步骤
1.命令集
2.读取流程(手册给出最好2s测量一次)
状态位说明
1.启动传感器:给传感器上电,上电后需要传感器最多需要20ms(此时SCL为高电平)来达到空闲状态
2.启动和停止时序满足IIC协议,开始SCL在高电平时检测到SDA下降沿表示数据发送开始,结束SCL高电平时检测到SDA上升沿表示结束
3.发送命令:通过发送8bit数据来传输命令读取数据:0X71
4.传感器读取流程(在此做了简化,省略了一些步骤)
首先上电后要等待40ms,然后发送0XE1来初始化(此命令有两个字节:0x08和0x00),之后直接发送0xAC命令(此命令有两个字节:0x33,0x00)来触发测量,等待75ms(我这里等待了80ms)等待测量完成,之后发送0x71读取温湿度
读取后传输的数据一共有6byte,其中第一个字节为传感器的状态,后2.5个字节为湿度数据,最后2.5个字节为温度数据
3.数据处理
但是由于FPGA芯片不能处理小数,所以可以将RH和T都先扩大10000倍在处理来达到最后得到整数和小数数据。
三.代码设计
1.端口列表
AHT10(IIC)控制模块
module aht20_iic_control(
input wire clk ,
input wire rst_n ,
//数据接口
output wire [39:0] temp_data ,//得到的40位数据(温度数据,湿度数据)
output wire temp_data_vld ,//数据有效信号
output wire ready ,
//iic接口
output wire iic_scl ,//IIC时钟线
inout wire iic_sda //IIC数据总线
);
AHT10(IIC)驱动模块
module iic_dirver#
(
parameter T = "100k",
SYSTEM_CLOCK = 50_000_000
)
(
input wire clk ,
input wire rst_n ,
//控制接口
input wire [4:0] cmd ,//命令指令
input wire cmd_vld ,//命令有效信号
output wire done ,//命令传输完成
//数据接口
input wire [7:0] wr_data ,//写入的数据
output wire [7:0] rd_data ,//读取的数据
output wire rd_data_vld,//读取数据有效信号
output wire iic_scl ,//iic时钟
inout wire iic_sda //iic数据总线
);
其中:
cmd:命令信号用来控制iic驱动模块向从机发送指令的流程
Done:用来表示这一次指令发送完毕
module tx_ctrl(
input wire clk ,
input wire rst_n ,
input wire [39:0] temp_data,//读取到的40位数据包括温度和湿度
input wire temp_data_vld,//数据有效信号
input wire ready ,//tx空闲信号
output wire [7:0] tx_data ,//发送数据
output wire tx_data_vld//发送数据有效信号
);
其中:
temp_data:为从AHT10中读取的除第一个字节以外的剩下的五个字节数据
Ready:为tx模块发送的,表示自己在空闲状态可以发送数据的信号
TX模块
module uart_tx#
(
parameter BPS = 115200,
parameter CLK_FRE = 50_000_000,
parameter CHECK_BIT = "NONE"//NONE 不校验 DDO奇数 EVEN偶数
)
(
input wire clk ,
input wire rst_n ,
input wire tx_data_vld,//数据发送标志
input wire [7:0] tx_data ,//发送数据
output reg tx ,
output wire ready //准备好发送
);
2.状态转移图
控制模块:
IDLE:这是一个上电等待状态,上电后等待40ms跳转
RST_REQ:这是一个复位请求发送状态
RST_WAIT_DONE:这是一个等待复位命令发送完成的状态,当4byte数据发送完成后跳转到WAIT状态,否则跳转到RST_REQ
WAIT:这是一个等待的空闲状态,什么也不用做
SUR_REQ:这是一个温度测量请求发送状态
SUR_WAIT_DONE:这是一个温度测量请求发送等待完成状态,当4byte数据发送完成后,跳转到DELAY状态,否则跳转到SUR_REQ状态
DEALY:这是一个等待延时状态,再此状态等待80ms
READ_REQ:这是一个读取温度请求状态
READ_WAIT_DONE:这是一个读取温度请求等待发送完成状态,当7byte数据发送完成后跳转到DONE状态否则跳转到READ_REQ状态
DONE:这是一个完成状态,表示一个测量循环完成,再此状态延迟2s后跳转到WAIT状态
驱动模块:
IDEL:空闲状态,等待指令
START:开始状态,表示开始数据写/读
WRITE: 向从机写状态
R_ACK:等待接收从状态机响应(这里等待1bit时间)
READ:从从状态机读数据状态
S_ACK:向从状态机发送响应信号状态
STOP:发送停止信号状态
3.代码实现
控制模块
/**************************************功能介绍***********************************
Date :
Author : WZY.
Version :
Description:
器件流程:首先上电后需要40ms的时间启动(在这20s内scl必须保持高电平),之后主机向从机发送初始化指令,发送结束后
发送检测温度指令,之后等待80ms的时间等待温度测量完成,紧接着发送温度读取指令,之后等待2s(推荐2s检测一次温度)返
回发送温度检测指令,以此循环往复。
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module aht20_iic_control(
input wire clk ,
input wire rst_n ,
//数据接口
output wire [39:0] temp_data ,//得到的40位数据(温度数据,湿度数据)
output wire temp_data_vld ,//数据有效信号
output wire ready ,
//iic接口
output wire iic_scl ,//IIC时钟线
inout wire iic_sda //IIC数据总线
);
//---------<参数定义>---------------------------------------------------------
//状态机参数定义
localparam IDLE = 10'b0000000001,//
RST_REQ = 10'b0000000010,//
RST_WAIT_DONE = 10'b0000000100,//
WAIT = 10'b0000001000,//
SUR_REQ = 10'b0000010000,//
SUR_WAIT_DONE = 10'b0000100000,//
READ_REQ = 10'b0001000000,//
READ_WAIT_DONE = 10'b0010000000,//
DELAY = 10'b0100000000,
DONE = 10'b1000000000;//
parameter MAX_80MS = 22'd3_999_999,
MAX_40MS = 21'd1_999_999,
MAX_2S = 28'd99_999_999;
parameter WR_CTRL_BIT = 8'b01110000,
RD_CTRL_BIT = 8'b01110001;
`define START_BIT 5'b00001
`define WRITE_BIT 5'b00010
`define READ_BIT 5'b00100
`define STOP_BIT 5'b01000
`define ACK_BIT 5'b10000
//---------<内部信号定义>-----------------------------------------------------
reg [9:0] cstate ;//现态
reg [9:0] nstate ;//次态
wire idle2rst_req ;
wire rst_req2rst_wait_done ;
wire rst_wait_done2wait ;
wire rst_wait_done2rst_req ;
wire wait2sur_req ;
wire wait2read_req ;
wire sur_req2sur_wait_done ;
wire sur_wait_done2delay ;
wire sur_wait_done2sur_req ;
wire read_req2read_wait_done ;
wire read_wait_done2done ;
wire read_wait_done2read_req ;
wire delay2read_req ;
wire done2wait ;
//模块例化
wire done ;
reg [4:0] cmd ;
reg cmd_vld ;
reg [7:0] op_wr_data ;
//命令计数器参数
reg [3:0] cmd_num ;
reg [3:0] cnt_cmd ;
wire add_cnt_cmd ;
wire end_cnt_cmd ;
//延时计数器
reg [21:0] cnt_delay ;
wire add_cnt_delay ;
wire end_cnt_delay ;
reg [20:0] cnt_delay_40ms ;
wire add_cnt_delay_40ms ;
wire end_cnt_delay_40ms ;
reg [27:0] cnt_delay_2s ;
wire add_cnt_delay_2s ;
wire end_cnt_delay_2s ;
//****************************************************************
// 状态机
//****************************************************************
//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cstate <= IDLE;
end
else begin
cstate <= nstate;
end
end
//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
case(cstate)
IDLE : begin
if (idle2rst_req) begin
nstate = RST_REQ;
end
else begin
nstate = cstate;
end
end
RST_REQ : begin
if (rst_req2rst_wait_done) begin
nstate = RST_WAIT_DONE;
end
else begin
nstate = cstate;
end
end
RST_WAIT_DONE : begin
if (rst_wait_done2wait) begin
nstate = WAIT;
end
else if (rst_wait_done2rst_req) begin
nstate = RST_REQ;
end
else begin
nstate = cstate;
end
end
WAIT : begin
if (wait2sur_req) begin
nstate = SUR_REQ;
end
else begin
nstate = cstate;
end
end
SUR_REQ : begin
if (sur_req2sur_wait_done) begin
nstate = SUR_WAIT_DONE;
end
else begin
nstate = cstate;
end
end
SUR_WAIT_DONE : begin
if (sur_wait_done2delay) begin
nstate = DELAY;
end
else if (sur_wait_done2sur_req) begin
nstate = SUR_REQ;
end
else begin
nstate = cstate;
end
end
READ_REQ : begin
if (read_req2read_wait_done) begin
nstate = READ_WAIT_DONE;
end
else begin
nstate = cstate;
end
end
READ_WAIT_DONE : begin
if (read_wait_done2done) begin
nstate = DONE;
end
else if (read_wait_done2read_req) begin
nstate = READ_REQ;
end
else begin
nstate = cstate;
end
end
DELAY : begin
if (delay2read_req) begin
nstate = READ_REQ;
end
else begin
nstate = cstate;
end
end
DONE : begin
if (done2wait) begin
nstate = WAIT;
end
else begin
nstate = cstate;
end
end
default : ;
endcase
end
//第三段:描述输出,时序逻辑或组合逻辑皆可
assign idle2rst_req = cstate == IDLE && end_cnt_delay_40ms;//上电后延迟40ms
assign rst_req2rst_wait_done = cstate == RST_REQ && 1;
assign rst_wait_done2wait = cstate == RST_WAIT_DONE && end_cnt_cmd;//指令发送完成
assign rst_wait_done2rst_req = cstate == RST_WAIT_DONE && done;//子模块1byte命令发送完成
assign wait2sur_req = cstate == WAIT && 1;
assign sur_req2sur_wait_done = cstate == SUR_REQ && 1;
assign sur_wait_done2delay = cstate == SUR_WAIT_DONE && end_cnt_cmd;//指令发送完成
assign sur_wait_done2sur_req = cstate == SUR_WAIT_DONE && done;//子模块1byte命令发送完成
assign read_req2read_wait_done = cstate == READ_REQ && 1;
assign read_wait_done2done = cstate == READ_WAIT_DONE && end_cnt_cmd;//指令发送完成
assign read_wait_done2read_req = cstate == READ_WAIT_DONE && done ;//子模块1byte命令发送完成
assign delay2read_req = cstate == DELAY && end_cnt_delay;//等待80ms
assign done2wait = cstate == DONE && end_cnt_delay_2s; //等待2s
//****************************************************************
// 指令计数器
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_cmd <= 3'd0;
end
else if(add_cnt_cmd)begin
if(end_cnt_cmd)begin
cnt_cmd <= 3'd0;
end
else begin
cnt_cmd <= cnt_cmd + 1'b1;
end
end
end
assign add_cnt_cmd = done;
assign end_cnt_cmd = add_cnt_cmd && cnt_cmd == cmd_num - 1;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cmd_num <= 0;
end
else if (idle2rst_req) begin
cmd_num <= 4;
end
else if (wait2sur_req) begin
cmd_num <= 4;
end
else if (delay2read_req) begin
cmd_num <= 7;
end
end
//****************************************************************
// 延时计数器
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_delay <= 22'd0;
end
else if(add_cnt_delay)begin
if(end_cnt_delay)begin
cnt_delay <= 22'd0;
end
else begin
cnt_delay <= cnt_delay + 1'b1;
end
end
end
assign add_cnt_delay = cstate == DELAY;
assign end_cnt_delay = add_cnt_delay && cnt_delay == MAX_80MS;
//****************************************************************
// 延时计数器
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_delay_40ms <= 22'd0;
end
else if(add_cnt_delay_40ms)begin
if(end_cnt_delay_40ms)begin
cnt_delay_40ms <= 22'd0;
end
else begin
cnt_delay_40ms <= cnt_delay_40ms + 1'b1;
end
end
end
assign add_cnt_delay_40ms = cstate == IDLE;
assign end_cnt_delay_40ms = add_cnt_delay_40ms && cnt_delay_40ms == MAX_40MS;
//****************************************************************
// 延时计数器
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_delay_2s <= 22'd0;
end
else if(add_cnt_delay_2s)begin
if(end_cnt_delay_2s)begin
cnt_delay_2s <= 22'd0;
end
else begin
cnt_delay_2s <= cnt_delay_2s + 1'b1;
end
end
end
assign add_cnt_delay_2s = cstate == DONE;
assign end_cnt_delay_2s = add_cnt_delay_2s && cnt_delay_2s == MAX_2S;