基于FPGA的IIC控制AHT20读取温湿度
基于FPGA的IIC控制AHT20读取温湿度
文章目录
- 基于FPGA的IIC控制AHT20读取温湿度
- 一、IIC简介
- 二、AHT20简介
- 三、实现思路
- 四、仿真效果
- 五、上板验证
- 六、代码
- top.v
- iic_top.v
- iic_ctrl.v
- iic_driver.v
- data_ctrl.v
- sel_driver.v
- 七、总结
一、IIC简介
采用串行总线可以简化系统硬件结构、减小系统体积、提高系统可靠性。常 用的串行总线有单总线(1-Wire Bus)、IIC(Inter-Integrated Circuit)、SPI(Serial Peripheral Interface)等。 IIC 总线是 Phlips 公司推出的一种同步串行总线,是一种支持多主机多从机 系统、总线仲裁以及高低速器件同步功能的高性能串行总线技术。 IIC 总线只有两根双向信号线:一根是数据线 SDA,一根是时钟线 SCL。
多主机多从机----在 IIC 总线系统上可以挂载多个主机设备与从机设备;
总线仲裁----对于总线上挂载有多主机设备的情况下,为避免多个主机同 时向总线发起通信,需要有相应的总线仲裁机制,避免出现总线冲突;
高低速器件同步–通过 IIC 总线连接的两个器件的传输速度不一定相同, 因此需要通过从机的时钟延展(clock stretching)功能保证两者的通信速度一致。
即:如果果从机跟不上主机的速率,IIC 协议规定从机可以通过将 SCL 时钟线拉 低来暂停传输直到从机释放掉 SCL 线,传输才能继续进行。
传输速率:
标准模式:100Kbit/s
快速模式:400kbit/s
高速模式:3.4Mbit/s
工作原理
每一个连接到 IIC 总线上的器件都有一个唯一的器件地址(ID),主机通过 ID 建立多机通信机制,因此 IIC 总线节省了外围器件的片选信号线。虽然 IIC 总 线上可以连接多个主机和多个从机,但是同一时刻,只能有一个主机向总线上发起传输,如果有多个主机同时发起传输,则会根据 IIC 总线的仲裁机制,最终 只给一个主机授权,没有得到仲裁的主机则会停止传输。而且,IIC 总线上只能 由主机发起传输,从机无法主动发起传输,这也就意味着从机和从机之间也是无法直接交换数据的。
IIC 总线在传输数据时必须遵循规定的数据传输时序,一次完整的数据传输 过程中共有四类信号:起始信号、数据信号、应答信号和停止信号。
起始信号:在 SCL 为高电平时,SDA 的电平由高电平跳变为低电平,称为 I2C 总线的起始信号,标志着一次数据传输开始。起始信号由主机主动产生,在 产生起始信号之前 I2C 总线必须处于空闲状态。
停止信号:在 SCL 为高电平时,SDA 由低电平跳变为高电平,称为 I2C 总 线的停止信号,标志着一次数据传输终止。停止信号由主机主动产生,产生停止 信号之后 I2C 总线将返回空闲状态。
数据信号:IIC 总线传输数据时,在 SCL 为高电平时,SDA 必须保持稳定的 逻辑电平,高电平表示数据 1,低电平表示数据 0;只有在 SCL 为低电平时才允 许 SDA 上的电平状态改变。
应答信号:IIC 总线传输数据时,每传输一个字节后都必须有一个 SCL 周期 的应答信号;与应答信号相对应的时钟由主机产生,发送端必须释放 SDA 使其 处于高电平状态,以便接收端在这一个 SCL 周期发出应答信号。
应答信号有两种状态:应答(ACK)、非应答(NACK),接收端在应答位期 间输出低电平则为应答,输出高电平则为非应答。
当由于某种原因,从机设备不产生应答时,如从机在进行其它处理而无法接 收总线上的数据时,必须释放 SDA 总线,然后由主机产生一个停止信号以终止 数据传输。
当主机在接收数据时,接收到最后一个字节后,必须给从机发送一个非应答 信号,使从机释放总线以便主机可以发送停止信号,终止数据传输。
需要注意的是:在某些情况下,从机在收到一个完整的字节后,有可能因为 需要忙于其它工作(如处理内部中断服务)而无法立刻接收下一个字节,这时从 机需要将 SCL 拉为低电平,从而强制主机处于等待状态,直到从机准备好接收 下一个字节时,再释放 SCL 为高电平状态,从而可以继续传输数据。这种现象 称为时钟拉伸(Clock Stretching)。
数据地址
IIC 总线协议规定:起始信号表明一次传输开始,其后为寻址字节(高 7 位 为从机地址、最低 1 位为方向位,方向位表明主机与从机之间的数据传输方向, 0–主机发送数据给从机,1–从机发送数据给主机);在寻址字节后是对应的读、 写操作的数据字节和应答位;在数据传输完成后主机必须发送停止信号。
IIC 总线的数据传输方式有许多读、写组合方式:主机写操作、主机读操作、 主机读写操作。
二、AHT20简介
AHT20,新一代温湿度传感器在尺寸与智能方面建立了新的标准:它嵌入了适于回流焊的双列扁平无引脚SMD 封装,底面 3 x 3mm ,高度1.0mm。传感器输出经过标定的数字信号,标准 IIC 格式。
传感器性能:
由图可知湿度测量范围为0~100%,分辨率为0.024%。
温度测量范围为-40℃~ +85℃,分辨率为0.01℃。
在启动传输后,随后传输的IIC首字节包括7位的IIC设备地址 0x38和一个SDA方向位 x(读R:‘1’,写W:‘0’)。在第8个SCL时钟下降沿之后,通过拉低 SDA引脚 (ACK位),指示传感器数据接收正常。 在发出初始化命令之后(‘1011’1110’)代表初始化,‘1010’1100’代表温湿度测量), 主机必须等到测量完成。
读出的数据可用以下公式进行转换。
湿度:
温度:
三、实现思路
iic_driver模块设计为本篇文章复用性较高的写法,直接延用。
上图为iic_ctrl模块状态机设计
设置上电后为IDLE,等待50ms跳转中间状态START,进入step1;
step0:跳转至REST,读取温湿度值之前,发送0xBE命令(初始化),此命令参数有两个字节, 第一个字节为0x08,第二个字节为0x00;(这里为了方便理解,将REST与W_DATA写数据分开,实际应为一个状态,进行连续发数据的操作)所以step1复位操作应发四个字节数据。分别为:带起始信号的写(cmd=0011),两字节的普通的写(cmd=0010),带停止信号的写(cmd=1010),发送完成跳回START,进入step2;
step1:进行发送读取数据命令的操作,跳转至MSRE,直接发送 0xAC命令(触发测量),此命令参数有两个字节,第一个字节为 0x33,第二个字节为0x00,同为发送四个字节数据,与上述相同,发送完成跳回START,进入step3;
step2:读取温度,进行读取时要等待其数据采集完成,这里设置为等待1s,等待完成跳转至READ,将从机发送的6字节数据保存,在最后发送NACK加停止信号。6字节数据第一个为状态字节,接下来的20bit为湿度数据,后20bit为温度数据。数据读完跳回START,将step计数器置为1,重复上述操作。
四、仿真效果
50ms计数器结束(仿真将时间缩短),状态跳转至START,step为0进行复位操作。
起始信号正确,后面跟着AHT20器件地址0111_000加写操作’0’
第二字节为初始化命令0xBE为1011_1110,第三字节0x08,第四字节0x00,发送完成。
step计数器加一,进入第二步,发送读取温度命令第一字节为器件地址+写指令0为0x70,第二字节为读取温度命令0xAC,第三、四字节为命令后的参数数据0x33与0x00,停止信号正确,发送完成。
step计数器加一,1s计数器计数结束计数跳转至READ读取数据,先发送0x71,器件地址+1读操作,后续问读出数据,这里用的从机模型为EEPROM的模型,内部未写入数据,所以读不出数据。
读完step置为1,重复读取的操作。仿真验证成功。
五、上板验证
开关在下方为1,数码管显示温度,u表示+,C表示现在显示的是温度。
开关在上方为0,数码管显示湿度,o表示%,说明现在显示的为湿度。
video_20250721_194723
六、代码
top.v
module top (input clk,input rst_n,input [0:0] sw,//---------<数码管>------------------------------------------------- output [5:0] sel, output [7:0] dig,//---------<iic>------------------------------------------------- output scl,inout sda
);wire [39:0] rd_data_r;
wire [19:0] tm_data;
wire [19:0] hm_data;//三态门
wire sda_in;
wire sda_out;
wire sda_en;assign sda_in = sda;
assign sda = sda_en ? sda_out : 1'bz;iic_top inst_iic_top(.clk (clk),.rst_n (rst_n),.rd_data_r (rd_data_r),.sda_in (sda_in),.sda_out (sda_out),.sda_en (sda_en),.scl (scl)
);data_ctrl inst_data_ctrl(.clk ( clk ),.rst_n ( rst_n ),.rd_data_r( rd_data_r),.tm_data ( tm_data ),.hm_data ( hm_data )
);sel_driver inst_sel_driver(.clk (clk ),.rst_n (rst_n ),.sw (sw ),.tm_data(tm_data),.hm_data(hm_data),.sel (sel ),.dig (dig )
);endmodule
iic_top.v
module iic_top (input clk,input rst_n,output [39:0] rd_data_r,//---------<iic>------------------------------------------------- input sda_in,output sda_out,output sda_en,output scl
);
wire [7:0] rd_data;
wire trans_done;
wire slave_ack;
wire rw_flag;
wire [3:0] cmd;
wire [7:0] wr_data;iic_ctrl inst_iic_ctrl(.clk ( clk ), .rst_n ( rst_n ), .rd_data ( rd_data ), .trans_done( trans_done ), .wr_data ( wr_data ), .cmd ( cmd ), .rd_data_r ( rd_data_r ), .rw_flag ( rw_flag )
);iic_driver inst_iic_driver (.clk (clk ),.rst_n (rst_n ),.wr_data (wr_data ),.cmd (cmd ),.rw_flag (rw_flag ),.sda_in (sda_in ),.sda_out (sda_out ),.sda_en (sda_en ),.scl (scl ),.rd_data (rd_data ),.slave_ack (slave_ack ),.trans_done(trans_done)
);endmodule
iic_ctrl.v
module iic_ctrl(input clk ,input rst_n ,input [7:0] rd_data ,input trans_done ,output reg [7:0] wr_data ,output reg [3:0] cmd ,output reg [39:0] rd_data_r ,output reg rw_flag
);//状态机
reg [2:0] state_c ;
reg [2:0] state_n ;localparam IDLE = 3'd0,START = 3'd1,REST = 3'd2,MSRE = 3'd3,READ = 3'd4;wire IDLE_2_START ,START_2_REST ,REST_2_START ,START_2_MSRE ,MSRE_2_START ,START_2_READ ,READ_2_START ;reg [7:0] state_data;//步骤计数
reg [1:0] step ;
wire add_step ;
wire end_step ;//50ms计数
reg [21:0] cnt_50ms ;
wire add_cnt_50ms ;
wire end_cnt_50ms ;
parameter TIME_50ms = 2_500_000 ;//1s计数器
reg [25:0] cnt_1s ;
wire add_cnt_1s ;
wire end_cnt_1s ;
parameter TIME_1s = 50_000_000;//字节计数
reg [2:0] byte_num ;
reg [2:0] cnt_byte ;
wire add_cnt_byte ;
wire end_cnt_byte ;always @(*)begincase(state_c)REST,MSRE : byte_num = 3'd4 ;READ : byte_num = 3'd7 ;default : byte_num = 3'd0 ;endcase
end//---------<step>------------------------------------------------- //步骤计数器
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstep <= 2'd0;end else if(add_step)begin if(end_step)begin step <= 2'd1;endelse begin step <= step + 1'b1;end end
end assign add_step = (REST_2_START || MSRE_2_START || READ_2_START);
assign end_step = add_step && step == 2;//---------<cnt_50ms>------------------------------------------------- always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_50ms <= 22'd0;end else if(add_cnt_50ms)begin if(end_cnt_50ms)begin cnt_50ms <= 22'd0;endelse begin cnt_50ms <= cnt_50ms + 1'b1;end end
end assign add_cnt_50ms = (state_c == IDLE);
assign end_cnt_50ms = add_cnt_50ms && cnt_50ms == (TIME_50ms - 1);//---------<cnt_1s>------------------------------------------------- always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_1s <= 'd0;end else if(add_cnt_1s)begin if(end_cnt_1s)begin cnt_1s <= 'd0;endelse begin cnt_1s <= cnt_1s + 1'b1;end end
end assign add_cnt_1s = (step == 2) && (state_c == START);
assign end_cnt_1s = add_cnt_1s && cnt_1s == (TIME_1s -1);//---------<cnt_byte>------------------------------------------------- always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_byte <= 3'd0;end else if(add_cnt_byte)begin if(end_cnt_byte)begin cnt_byte <= 3'd0;endelse begin cnt_byte <= cnt_byte + 1'b1;end end
end assign add_cnt_byte = trans_done && ((state_c == REST) || (state_c == MSRE) || (state_c == READ));
assign end_cnt_byte = add_cnt_byte && cnt_byte == (byte_num - 1);//---------<State Machine>------------------------------------------------- //第一段:同步时序描述状态转移
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= IDLE;end else begin state_c <= state_n;end
end//第二段:组合逻辑判断状态转移条件,描述状态转移规律
always @(*) begincase(state_c)IDLE : state_n = (IDLE_2_START) ? START : state_c;START : beginif(START_2_REST)beginstate_n = REST;endelse if(START_2_MSRE)beginstate_n = MSRE;endelse if(START_2_READ)beginstate_n = READ;endelse beginstate_n = state_c;endendREST : state_n = (REST_2_START) ? START : state_c;MSRE : state_n = (MSRE_2_START) ? START : state_c;READ : state_n = (READ_2_START) ? START : state_c;default : state_n = state_c;endcase
endassign IDLE_2_START = (state_c == IDLE) && end_cnt_50ms;
assign START_2_REST = (state_c == START) && (step == 0) ;
assign REST_2_START = (state_c == REST) && end_cnt_byte;
assign START_2_MSRE = (state_c == START) && (step == 1) ;
assign MSRE_2_START = (state_c == MSRE) && end_cnt_byte;
assign START_2_READ = (state_c == START) && (step == 2) && end_cnt_1s;
assign READ_2_START = (state_c == READ) && end_cnt_byte;//---------<wr_data cmd rw_flag>------------------------------------------------- always @(*)beginif(state_c == REST)begincase(cnt_byte)0 : begin wr_data = 8'h70 ; cmd = 4'b0011; rw_flag = 1'b1 ; end1 : begin wr_data = 8'hbe ; cmd = 4'b0010; rw_flag = 1'b1 ; end2 : begin wr_data = 8'h08 ; cmd = 4'b0010; rw_flag = 1'b1 ; end3 : begin wr_data = 8'h00 ; cmd = 4'b1010; rw_flag = 1'b1 ; enddefault : begin wr_data = 'd0 ; cmd = 'd0 ; rw_flag = 'd0 ; endendcaseendelse if(state_c == MSRE)begincase(cnt_byte)0 : begin wr_data = 8'h70 ; cmd = 4'b0011; rw_flag = 1'b1 ; end1 : begin wr_data = 8'hac ; cmd = 4'b0010; rw_flag = 1'b1 ; end2 : begin wr_data = 8'h33 ; cmd = 4'b0010; rw_flag = 1'b1 ; end3 : begin wr_data = 8'h00 ; cmd = 4'b1010; rw_flag = 1'b1 ; enddefault : begin wr_data = 'd0 ; cmd = 'd0 ; rw_flag = 'd0 ; endendcaseendelse if(state_c == READ)begincase(cnt_byte)0 : begin wr_data = 8'h71 ; cmd = 4'b0011; rw_flag = 1'b1 ; end1 : begin wr_data = 8'h00 ; cmd = 4'b0100; rw_flag = 1'b1 ; end2 : begin wr_data = 8'h00 ; cmd = 4'b0100; rw_flag = 1'b1 ; end3 : begin wr_data = 8'h00 ; cmd = 4'b0100; rw_flag = 1'b1 ; end4 : begin wr_data = 8'h00 ; cmd = 4'b0100; rw_flag = 1'b1 ; end5 : begin wr_data = 8'h00 ; cmd = 4'b0100; rw_flag = 1'b1 ; end6 : begin wr_data = 8'h00 ; cmd = 4'b1100; rw_flag = 1'b1 ; enddefault : begin wr_data = 'd0 ; cmd = 'd0 ; rw_flag = 'd0 ; endendcaseendelse beginwr_data = 'd0;cmd = 'd0;rw_flag = 'd0;end
end//---------<rd_data_r state_data>------------------------------------------------- always @(posedge clk or negedge rst_n)beginif(!rst_n)beginrd_data_r <= 40'd0;state_data <= 8'd0;endelse if(cmd[2] && trans_done)begincase (cnt_byte)1 : state_data <= rd_data ;2 : rd_data_r [39:32] <= rd_data ;3 : rd_data_r [31:24] <= rd_data ;4 : rd_data_r [23:16] <= rd_data ;5 : rd_data_r [15:8 ] <= rd_data ;6 : rd_data_r [7 :0 ] <= rd_data ;endcaseendelse beginrd_data_r <= rd_data_r;state_data <= state_data;end
end
endmodule
iic_driver.v
module iic_driver #(parameter SYS_CLK = 50_000_000,IIC_RATE = 200_000)(input clk ,input rst_n ,input [7:0] wr_data ,input [3:0] cmd ,input rw_flag ,input sda_in ,output reg sda_out ,output reg sda_en ,output reg scl ,output reg [7:0] rd_data ,output reg slave_ack ,output trans_done
);localparam CMD_START = 4'b0001, //开始命令CMD_SEND = 4'b0010, //发送命令CMD_RECV = 4'b0100, //接收命令CMD_STOP = 4'b1000; //停止命令
//状态描述
localparam IDLE = 3'd0,START = 3'd1,SEND = 3'd2,RECV = 3'd3,R_ACK = 3'd4,S_ACK = 3'd5,STOP = 3'd6;wire IDLE_2_START,IDLE_2_SEND,IDLE_2_RECV,START_2_SEND,START_2_RECV,SEND_2_RACK,RECV_2_SACK,RACK_2_IDLE,RACK_2_STOP,SACK_2_IDLE,SACK_2_STOP,STOP_2_IDLE;reg [2:0] state_c; //现态
reg [2:0] state_n; //次态//时钟计数
reg [7:0] cnt_scl;
wire add_cnt_scl;
wire end_cnt_scl;
parameter CNT_SCL_MAX = SYS_CLK/IIC_RATE;//bit计数
reg [2:0] cnt_bit;
wire add_cnt_bit;
wire end_cnt_bit;
parameter CNT_BIT_MAX = 4'd8;//时钟计数器
always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_scl <= 0;end else if(add_cnt_scl)beginif (end_cnt_scl) begincnt_scl <= 0;endelse begincnt_scl <= cnt_scl + 1'b1;end endelse begincnt_scl <= cnt_scl;end
endassign add_cnt_scl = (state_c !== IDLE);
assign end_cnt_scl = add_cnt_scl && (cnt_scl == (CNT_SCL_MAX -1));//bit计数器
always @(posedge clk or negedge rst_n) begin if(!rst_n)begincnt_bit <= 0;endelse if(add_cnt_bit)beginif(end_cnt_bit)begincnt_bit <= 0;endelse begincnt_bit <= cnt_bit + 1'b1;endendelse begincnt_bit <= cnt_bit;end
endassign add_cnt_bit = end_cnt_scl && (state_c == SEND || state_c == RECV);
assign end_cnt_bit = add_cnt_bit && (cnt_bit == (CNT_BIT_MAX-1));//状态机一段
always @(posedge clk or negedge rst_n) beginif (!rst_n) beginstate_c <= IDLE;end else beginstate_c <= state_n;end
end//状态机二段
always @(*)begincase(state_c)IDLE: beginif (IDLE_2_START) beginstate_n = START;end else if(IDLE_2_SEND)beginstate_n = SEND;endelse if(IDLE_2_RECV)beginstate_n = RECV;endelse beginstate_n = state_c;endendSTART: begin if (START_2_SEND) beginstate_n = SEND;end else if(START_2_RECV)beginstate_n = RECV;endelse beginstate_n = state_c;endendSEND: state_n = (SEND_2_RACK) ? R_ACK : state_c;RECV: state_n = (RECV_2_SACK) ? S_ACK : state_c;R_ACK: beginif (RACK_2_IDLE) beginstate_n = IDLE;endelse if(RACK_2_STOP)beginstate_n = STOP;endelse beginstate_n = state_c;endendS_ACK: beginif (SACK_2_IDLE) beginstate_n = IDLE;endelse if(SACK_2_STOP)beginstate_n = STOP;endelse beginstate_n = state_c;endendSTOP: state_n = (STOP_2_IDLE) ? IDLE : state_c;default : state_n = state_c;endcase
end//描述转移条件
assign IDLE_2_START = (state_c == IDLE) && (rw_flag) && cmd[0];
assign IDLE_2_SEND = (state_c == IDLE) && (rw_flag) && !cmd[0] && cmd[1];
assign IDLE_2_RECV = (state_c == IDLE) && (rw_flag) && !cmd[0] && cmd[2];
assign START_2_SEND = (state_c == START) && (end_cnt_scl) && cmd[1];
assign START_2_RECV = (state_c == START) && (end_cnt_scl) && cmd[2];
assign SEND_2_RACK = (state_c == SEND) && (end_cnt_bit) ;
assign RECV_2_SACK = (state_c == RECV) && (end_cnt_bit) ;
assign RACK_2_IDLE = (state_c == R_ACK) && (end_cnt_scl) && !cmd[3];
assign SACK_2_IDLE = (state_c == S_ACK) && (end_cnt_scl) && !cmd[3];
assign RACK_2_STOP = (state_c == R_ACK) && (end_cnt_scl) && cmd[3];
assign SACK_2_STOP = (state_c == S_ACK) && (end_cnt_scl) && cmd[3];
assign STOP_2_IDLE = (state_c == STOP) && (end_cnt_scl) ;//scl描述
always @(posedge clk or negedge rst_n) beginif(!rst_n)beginscl <= 1'b1;end else if(state_c != IDLE)beginif(cnt_scl < ((CNT_SCL_MAX-1) >> 1))beginscl <= 1'b0;endelse beginscl <= 1'b1;endendelse beginscl <= scl;end
end//描述数据
always @(posedge clk or negedge rst_n) beginif(!rst_n)beginsda_out <= 1'b1;sda_en <= 1'b0;rd_data <= 0;slave_ack <= 1;endelse begincase(state_c)IDLE: beginsda_out <= 1'b1;sda_en <= 1'b0;endSTART: beginsda_en <= 1'b1;if(cnt_scl >= (((CNT_SCL_MAX-1) >> 1) + ((CNT_SCL_MAX-1) >>2)))beginsda_out <= 1'b0;endelse beginsda_out <= 1'b1;endendSEND: begin sda_en <= 1'b1;if(cnt_scl == ((CNT_SCL_MAX-1)>>2))sda_out <= wr_data[7-cnt_bit];endRECV: beginsda_en <= 1'b0;sda_out <= 1'b0;if(cnt_scl == ((CNT_SCL_MAX-1) >>1)+((CNT_SCL_MAX-1) >>2))beginrd_data[7-cnt_bit] <= sda_in;endendR_ACK: beginsda_en <= 1'b0;sda_out <= 1'b0;if(cnt_scl == ((CNT_SCL_MAX-1) >>1)+((CNT_SCL_MAX-1) >>2))slave_ack <= sda_in;endS_ACK: beginsda_en <= 1'b1;sda_out <= cmd[3] ? 1'b1 : 1'b0;endSTOP: beginsda_en <= 1'b1;if(cnt_scl == ((CNT_SCL_MAX-1) >>2))beginsda_out <= 1'b0;endelse if(cnt_scl == ((CNT_SCL_MAX-1) >>1)+((CNT_SCL_MAX-1) >>2))beginsda_out <= 1'b1;endelse beginsda_out <= sda_out;endendendcase end
endassign trans_done = RACK_2_IDLE || SACK_2_IDLE ||STOP_2_IDLE;
endmodule
data_ctrl.v
module data_ctrl (input clk ,input rst_n ,input [39:0] rd_data_r ,output[19:0] tm_data ,output[19:0] hm_data
);//湿度数据
wire [16:0] hm_data_r ;
wire [3:0] hm_b001 ;//小数点后三位
wire [3:0] hm_b01 ;//小数点后二位
wire [3:0] hm_b1 ;//小数点后一位
wire [3:0] hm_1 ;//个位
wire [3:0] hm_10 ;//十位//温度数据
wire [16:0] tm_data_rr ;
wire [16:0] tm_data_r ;
wire [3:0] tm_sign ;//符号(温度正负)
wire [3:0] tm_b01 ;//小数点后二位
wire [3:0] tm_b1 ;//小数点后一位
wire [3:0] tm_1 ;//个位
wire [3:0] tm_10 ;//十位assign hm_data_r = rd_data_r[39:20]* 10_0000 /41'h10_0000;assign tm_data_rr= rd_data_r[19:0] * 2_0000 /41'h10_0000;
assign tm_data_r = (tm_data_rr >= 5000) ? (tm_data_rr-5000) : (5000-tm_data_rr);
assign tm_sign = (tm_data_rr >= 5000) ? 4'hA : 4'hB;assign hm_b001 = hm_data_r%10;
assign hm_b01 = hm_data_r/10%10;
assign hm_b1 = hm_data_r/100%10;
assign hm_1 = hm_data_r/1000%10;
assign hm_10 = hm_data_r/10000;assign tm_b01 = tm_data_r%10;
assign tm_b1 = tm_data_r/10%10;
assign tm_1 = tm_data_r/100%10;
assign tm_10 = tm_data_r/1000;assign hm_data = {hm_10,hm_1,hm_b1,hm_b01,hm_b001};
assign tm_data = {tm_sign,tm_10,tm_1,tm_b1,tm_b01};endmodule
sel_driver.v
module sel_driver(input clk ,input rst_n ,input [0:0] sw ,input [19:0] tm_data ,input [19:0] hm_data ,output reg [5:0] sel , //片选output reg [7:0] dig //段选
);parameter zero = 7'b100_0000,one = 7'b111_1001,two = 7'b010_0100,three= 7'b011_0000,four = 7'b001_1001,five = 7'b001_0010,six = 7'b000_0010,seven= 7'b111_1000,eight= 7'b000_0000,nine = 7'b001_0000,A = 7'b001_1101, //显示正号B = 7'b010_1011, //负号C = 7'b100_0110, //摄氏度符号Co = 7'b010_0011; //o代表%parameter TIME_20us=1000; //数码管切换reg [9:0] cnt;wire add_cnt;wire end_cnt;reg dot; //小数点reg [3:0] data; //寄存数字 always @(posedge clk or negedge rst_n) beginif(!rst_n)begincnt=0;endelse if(add_cnt)beginif(end_cnt)begincnt<=0;endelse begincnt<=cnt+1;endendelse begincnt<=cnt;endendassign add_cnt=1'b1;assign end_cnt=add_cnt&&cnt==TIME_20us-1;always @(posedge clk or negedge rst_n) beginif(!rst_n)beginsel<=6'b011_111;endelse if(end_cnt)beginsel<={sel[0],sel[5:1]};endelse beginsel<=sel;endendalways @(posedge clk or negedge rst_n)beginif(!rst_n)begindot<=1'b1;data<=4'hf;endelse begincase(sel)6'b011_111:begin data<= sw[0] ? 4'hC : 4'hD ; dot<=1'b1;end6'b101_111:begin data<= sw[0] ? tm_data[3:0] : hm_data[3:0] ; dot<=1'b0;end 6'b110_111:begin data<= sw[0] ? tm_data[7:4] : hm_data[7:4] ; dot<=1'b1;end6'b111_011:begin data<= sw[0] ? tm_data[11:8] : hm_data[11:8] ; dot<= sw[0] ? 1'b0 : 1'b1;end6'b111_101:begin data<= sw[0] ? tm_data[15:12] : hm_data[15:12] ; dot<= sw[0] ? 1'b1 : 1'b0;end6'b111_110:begin data<= sw[0] ? tm_data[19:16] : hm_data[19:16] ; dot<=1'b1;enddefault :begin data<=4'hf ; dot<=1'b1;endendcaseendendalways @(*)beginif(!rst_n)begindig<=8'hff;endelse begincase(data)4'd0 : dig<={dot,zero };4'd1 : dig<={dot,one };4'd2 : dig<={dot,two };4'd3 : dig<={dot,three};4'd4 : dig<={dot,four };4'd5 : dig<={dot,five };4'd6 : dig<={dot,six };4'd7 : dig<={dot,seven};4'd8 : dig<={dot,eight};4'd9 : dig<={dot,nine };4'hA : dig<={dot,A };4'hB : dig<={dot,B };4'hC : dig<={dot,C };4'hD : dig<={dot,o };default : dig<=8'hff;endcaseend end
endmodule
七、总结
IIC控制AHT20相对来说比较简单,主要考验对IIC协议掌握的程度;AHT20采集的湿度数据为相对湿度,会受到温度的影响,故不会常用;本次的项目为一次练手项目,为个人全程自主完成。