基于FPGA的IIC控制AHT20读取温湿度
文章目录
一、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’代表温湿度测量), 主机必须等到测量完成。
读出的数据可用以下公式进行转换。
湿度:
温度:
三、实现思路
AHT20采集数据,将数据处理后通过数码管显示,开关控制显示温度或者湿度,也能通过串口发送至电脑,将数据显示出来。
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表示%,说明现在显示的为湿度。
通过串口显示处温度和湿度,每1s发送一次。
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 @(*)begin
case(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)begin
step <= 2'd0;
end
else if(add_step)begin
if(end_step)begin
step <= 2'd1;
end
else 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)begin
cnt_50ms <= 22'd0;
end
else if(add_cnt_50ms)begin
if(end_cnt_50ms)begin
cnt_50ms <= 22'd0;
end
else 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)begin
cnt_1s <= 'd0;
end
else if(add_cnt_1s)begin
if(end_cnt_1s)begin
cnt_1s <= 'd0;
end
else 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)begin
cnt_byte <= 3'd0;
end
else if(add_cnt_byte)begin
if(end_cnt_byte)begin
cnt_byte <= 3'd0;
end
else 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)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
//第二段:组合逻辑判断状态转移条件,描述状态转移规律
always @(*) begin
case(state_c)
IDLE : state_n = (IDLE_2_START) ? START : state_c;
START : begin
if(START_2_REST)begin
state_n = REST;
end
else if(START_2_MSRE)begin
state_n = MSRE;
end
else if(START_2_READ)begin
state_n = READ;
end
else begin
state_n = state_c;
end
end
REST : 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
end
assign 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 @(*)begin
if(state_c == REST)begin
case(cnt_byte)
0 : begin wr_data = 8'h70 ; cmd = 4'b0011; rw_flag = 1'b1 ; end
1 : begin wr_data = 8'hbe ; cmd = 4'b0010; rw_flag = 1'b1 ; end
2 : begin wr_data = 8'h08 ; cmd = 4'b0010; rw_flag = 1'b1 ; end
3 : begin wr_data = 8'h00 ; cmd = 4'b1010; rw_flag = 1'b1 ; end
default : begin wr_data = 'd0 ; cmd = 'd0 ; rw_flag = 'd0 ; end
endcase
end
else if(state_c == MSRE)begin
case(cnt_byte)
0 : begin wr_data = 8'h70 ; cmd = 4'b0011; rw_flag = 1'b1 ; end
1 : begin wr_data = 8'hac ; cmd = 4'b0010; rw_flag = 1'b1 ; end
2 : begin wr_data = 8'h33 ; cmd = 4'b0010; rw_flag = 1'b1 ; end
3 : begin wr_data = 8'h00 ; cmd = 4'b1010; rw_flag = 1'b1 ; end
default : begin wr_data = 'd0 ; cmd = 'd0 ; rw_flag = 'd0 ; end
endcase
end
else if(state_c == READ)begin
case(cnt_byte)
0 : begin wr_data = 8'h71 ; cmd = 4'b0011; rw_flag = 1'b1 ; end
1 : begin wr_data = 8'h00 ; cmd = 4'b0100; rw_flag = 1'b1 ; end
2 : begin wr_data = 8'h00 ; cmd = 4'b0100; rw_flag = 1'b1 ; end
3 : begin wr_data = 8'h00 ; cmd = 4'b0100; rw_flag = 1'b1 ; end
4 : begin wr_data = 8'h00 ; cmd = 4'b0100; rw_flag = 1'b1 ; end
5 : begin wr_data = 8'h00 ; cmd = 4'b0100; rw_flag = 1'b1 ; end
6 : begin wr_data = 8'h00 ; cmd = 4'b1100; rw_flag = 1'b1 ; end
default : begin wr_data = 'd0 ; cmd = 'd0 ; rw_flag = 'd0 ; end
endcase
end
else begin
wr_data = 'd0;
cmd = 'd0;
rw_flag = 'd0;
end
end
//---------<rd_data_r state_data>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rd_data_r <= 40'd0;
state_data <= 8'd0;
end
else if(cmd[2] && trans_done)begin
case (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 ;
endcase
end
else begin
rd_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) begin
if (!rst_n) begin
cnt_scl <= 0;
end
else if(add_cnt_scl)begin
if (end_cnt_scl) begin
cnt_scl <= 0;
end
else begin
cnt_scl <= cnt_scl + 1'b1;
end
end
else begin
cnt_scl <= cnt_scl;
end
end
assign 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)begin
cnt_bit <= 0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 0;
end
else begin
cnt_bit <= cnt_bit + 1'b1;
end
end
else begin
cnt_bit <= cnt_bit;
end
end
assign 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) begin
if (!rst_n) begin
state_c <= IDLE;
end else begin
state_c <= state_n;
end
end
//状态机二段
always @(*)begin
case(state_c)
IDLE: begin
if (IDLE_2_START) begin
state_n = START;
end
else if(IDLE_2_SEND)begin
state_n = SEND;
end
else if(IDLE_2_RECV)begin
state_n = RECV;
end
else begin
state_n = state_c;
end
end
START: begin
if (START_2_SEND) begin
state_n = SEND;
end
else if(START_2_RECV)begin
state_n = RECV;
end
else begin
state_n = state_c;
end
end
SEND: state_n = (SEND_2_RACK) ? R_ACK : state_c;
RECV: state_n = (RECV_2_SACK) ? S_ACK : state_c;
R_ACK: begin
if (RACK_2_IDLE) begin
state_n = IDLE;
end
else if(RACK_2_STOP)begin
state_n = STOP;
end
else begin
state_n = state_c;
end
end
S_ACK: begin
if (SACK_2_IDLE) begin
state_n = IDLE;
end
else if(SACK_2_STOP)begin
state_n = STOP;
end
else begin
state_n = state_c;
end
end
STOP: 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) begin
if(!rst_n)begin
scl <= 1'b1;
end
else if(state_c != IDLE)begin
if(cnt_scl < ((CNT_SCL_MAX-1) >> 1))begin
scl <= 1'b0;
end
else begin
scl <= 1'b1;
end
end
else begin
scl <= scl;
end
end
//描述数据
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
sda_out <= 1'b1;
sda_en <= 1'b0;
rd_data <= 0;
slave_ack <= 1;
end
else begin
case(state_c)
IDLE: begin
sda_out <= 1'b1;
sda_en <= 1'b0;
end
START: begin
sda_en <= 1'b1;
if(cnt_scl >= (((CNT_SCL_MAX-1) >> 1) + ((CNT_SCL_MAX-1) >>2)))begin
sda_out <= 1'b0;
end
else begin
sda_out <= 1'b1;
end
end
SEND: begin
sda_en <= 1'b1;
if(cnt_scl == ((CNT_SCL_MAX-1)>>2))
sda_out <= wr_data[7-cnt_bit];
end
RECV: begin
sda_en <= 1'b0;
sda_out <= 1'b0;
if(cnt_scl == ((CNT_SCL_MAX-1) >>1)+((CNT_SCL_MAX-1) >>2))begin
rd_data[7-cnt_bit] <= sda_in;
end
end
R_ACK: begin
sda_en <= 1'b0;
sda_out <= 1'b0;
if(cnt_scl == ((CNT_SCL_MAX-1) >>1)+((CNT_SCL_MAX-1) >>2))
slave_ack <= sda_in;
end
S_ACK: begin
sda_en <= 1'b1;
sda_out <= cmd[3] ? 1'b1 : 1'b0;
end
STOP: begin
sda_en <= 1'b1;
if(cnt_scl == ((CNT_SCL_MAX-1) >>2))begin
sda_out <= 1'b0;
end
else if(cnt_scl == ((CNT_SCL_MAX-1) >>1)+((CNT_SCL_MAX-1) >>2))begin
sda_out <= 1'b1;
end
else begin
sda_out <= sda_out;
end
end
endcase
end
end
assign trans_done = RACK_2_IDLE || SACK_2_IDLE ||STOP_2_IDLE;
endmodule
tx_top.v
module tx_top (
input clk ,
input rst_n ,
input [1:0] sw ,
input [19:0]tm_data ,
input [19:0]hm_data ,
input data_done ,
output tx
);
wire [7:0] tx_data ;
wire tx_done ;
wire tx_start;
tx_ctrl inst_tx_ctrl(
.clk (clk ),
.rst_n (rst_n ),
.tx_done (tx_done ),
.tm_data (tm_data ),
.hm_data (hm_data ),
.data_done (data_done),
.tx_start (tx_start ),
.tx_data (tx_data )
);
uart_tx inst_uart_tx(
.clk (clk ),
.rst_n (rst_n ),
.tx_data (tx_data ),
.tx_start (tx_start),
.sw (sw ),
.tx (tx ),
.tx_done (tx_done )
);
endmodule
tx_ctrl.v
module tx_ctrl (
input clk ,
input rst_n ,
input tx_done ,
input [19:0] tm_data ,
input [19:0] hm_data ,
input data_done ,
output reg tx_start ,
output reg [ 7:0] tx_data
);
//reg [7:0] tx_data_r ;
//字节计数器
reg [5:0] cnt_byte ;
wire add_cnt_byte ;
wire end_cnt_byte ;
reg cnt_byte_flg ;
wire byte_flg ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_byte <= 'd0;
end
else if(add_cnt_byte)begin
if(end_cnt_byte)begin
cnt_byte <= 'd0;
end
else begin
cnt_byte <= cnt_byte + 1'b1;
end
end
end
assign add_cnt_byte = tx_done;
assign end_cnt_byte = add_cnt_byte && cnt_byte == 39-1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_byte_flg <= 'd0;
end
else if(data_done)begin
cnt_byte_flg <= 'd1;
end
else if(end_cnt_byte)begin
cnt_byte_flg <= 'd0;
end
end
//1s计数器
reg [25:0] cnt_1s ;
wire add_cnt_1s;
wire end_cnt_1s;
localparam TIME_1s = 50_000_000;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_1s <= 'd0;
end
else if(add_cnt_1s)begin
if(end_cnt_1s)begin
cnt_1s <= 'd0;
end
else begin
cnt_1s <= cnt_1s + 1'b1;
end
end
end
assign add_cnt_1s = end_cnt_byte;
assign end_cnt_1s = add_cnt_1s && cnt_1s == (TIME_1s - 1);
assign byte_flg = add_cnt_1s ? 1'b0 : (end_cnt_1s ? 1'b1 : byte_flg);
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_data <= 0;
tx_start <= 0;
end
else if(cnt_byte_flg) begin
tx_start <= 1'b1;
case(cnt_byte)
0 : tx_data <= 8'h0a;
1 : tx_data <= 8'hb5;
2 : tx_data <= 8'hb1;
3 : tx_data <= 8'hc7;
4 : tx_data <= 8'hb0;
5 : tx_data <= 8'hce;
6 : tx_data <= 8'hc2;
7 : tx_data <= 8'hb6;
8 : tx_data <= 8'hc8;
9 : tx_data <= 8'h3a;
10 : tx_data <= 8'h20;
11 : tx_data <= (tm_data[19:16] == 4'ha) ? 8'h2b : 8'h2d;
12 : tx_data <= tm_data[15:12] + 8'd48;
13 : tx_data <= tm_data[11:8] + 8'd48;
14 : tx_data <= 8'h2e;
15 : tx_data <= tm_data[7:4] + 8'd48;
16 : tx_data <= tm_data[3:0] + 8'd48;
17 : tx_data <= 8'ha1;
18 : tx_data <= 8'he6;
19 : tx_data <= 8'h0d;
20 : tx_data <= 8'h0a;
21 : tx_data <= 8'hb5;
22 : tx_data <= 8'hb1;
23 : tx_data <= 8'hc7;
24 : tx_data <= 8'hb0;
25 : tx_data <= 8'hca;
26 : tx_data <= 8'haa;
27 : tx_data <= 8'hb6;
28 : tx_data <= 8'hc8;
29 : tx_data <= 8'h3a;
30 : tx_data <= 8'h20;
31 : tx_data <= 8'h20;
32 : tx_data <= hm_data[19:16] + 8'd48;
33 : tx_data <= hm_data[15:12] + 8'd48;
34 : tx_data <= 8'h2e;
35 : tx_data <= hm_data[11:8] + 8'd48;
36 : tx_data <= hm_data[7:4] + 8'd48;
37 : tx_data <= 8'h25;
38 : tx_data <= 8'h0a;
default : tx_data <= 8'h0;
endcase
end
else begin
tx_data <= 8'h0;
tx_start <= 8'h0;
end
end
endmodule
uart_tx.v
module uart_tx (
input clk ,
input rst_n ,
input [7:0] tx_data ,
input tx_start,
input [1:0] sw ,
output reg tx ,
output tx_done
);
//奇偶校验位
wire tx_check;
//波特率计数器参数
reg [12:0] cnt;
reg add_cnt;
wire end_cnt;
//bit计数器参数
reg [3:0] cnt_bit;
wire add_cnt_bit;
wire end_cnt_bit;
reg [12:0] baud;
reg [10:0] data;//数据寄存器
always @(*) begin
case(sw[1:0])
2'b00: baud = 5208; //9600波特率
2'b01: baud = 3472; //14400波特率
2'b10: baud = 1302; //38400波特率
2'b11: baud = 434; //115200波特率
default: baud = 5208;
endcase
end
//波特率计数器
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt <= 0;
end
else begin
cnt <= cnt + 1;
end
end
else begin
cnt <= cnt;
end
end
assign end_cnt = add_cnt && (cnt == baud-1)||(cnt_bit == 4'd10 && cnt == ((baud-1)>>1));
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
add_cnt <= 1'b0;
end
else if(tx_start)begin
add_cnt <= 1'b1;
end
else if(end_cnt_bit||(cnt_bit == 4'd10 && cnt == ((baud-1)>>1)))begin
add_cnt <= 1'b0;
end
end
//bit计数器
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt_bit <= 0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 0;
end
else begin
cnt_bit <= cnt_bit + 1;
end
end
else begin
cnt_bit <= cnt_bit;
end
end
assign add_cnt_bit = end_cnt;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 4'd11-1;
assign tx_check = ~^tx_data;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data<=11'b11111111111;
end
else if(tx_start)begin
data<={1'b1,tx_check,tx_data,1'b0}; //数据加启示位
end
else begin
data<=data;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx<=1'b1;
end
else if(end_cnt)begin
tx<=data[cnt_bit];
end
else if(end_cnt_bit)begin
tx<=1'b1;
end
else begin
tx<=tx;
end
end
assign tx_done = end_cnt_bit;
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, //摄氏度符号C
o = 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) begin
if(!rst_n)begin
cnt=0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt<=0;
end
else begin
cnt<=cnt+1;
end
end
else begin
cnt<=cnt;
end
end
assign add_cnt=1'b1;
assign end_cnt=add_cnt&&cnt==TIME_20us-1;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
sel<=6'b011_111;
end
else if(end_cnt)begin
sel<={sel[0],sel[5:1]};
end
else begin
sel<=sel;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dot<=1'b1;
data<=4'hf;
end
else begin
case(sel)
6'b011_111:begin data<= sw[0] ? 4'hC : 4'hD ; dot<=1'b1;end
6'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;end
6'b111_011:begin data<= sw[0] ? tm_data[11:8] : hm_data[11:8] ; dot<= sw[0] ? 1'b0 : 1'b1;end
6'b111_101:begin data<= sw[0] ? tm_data[15:12] : hm_data[15:12] ; dot<= sw[0] ? 1'b1 : 1'b0;end
6'b111_110:begin data<= sw[0] ? tm_data[19:16] : hm_data[19:16] ; dot<=1'b1;end
default :begin data<=4'hf ; dot<=1'b1;end
endcase
end
end
always @(*)begin
if(!rst_n)begin
dig<=8'hff;
end
else begin
case(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;
endcase
end
end
endmodule
七、总结
IIC控制AHT20相对来说比较简单,主要考验对IIC协议掌握的程度;AHT20采集的湿度数据为相对湿度,会受到温度的影响,故不会常用;本次的项目为一次练手项目,为个人全程自主完成。