目录
一、任务
使用IIC协议通信的AHT20,将温湿度数据读取出来,并在串口助手上显示。



二、分析
1.需要了解的
需要了解IIC协议简介
也可以看看EEPROM的读写
我写的这个代码与上面的EEPROM的读写类似。
2.需要用到的模块
需要用到,AHT20模块 ,IIC接口模块 ,串口发送模块,读写控制模块 和**数据处理模块。**这里我没有使用CRC,但是,我在代码里面将它存储了,感兴趣的可以自己了解一下。
3.流程分析
根据上面的AHT20的读取流程,最好的是使用状态机来写,这里我分成5个状态:
WAIT
START
IDLE
READ
VLDWAIT:上电之后延时5ms,跳入START状态,
START:发送4个字完成以后,跳入IDLE状态,
IDLE:延时1s(大于80ms),跳入READ状态
READ:发送完8个字节,跳入VLD状态,
VLD:数据简单处理,跳入START状态循环。

三、Visio图
以下是整体的框架。(可以自己去看看RTL视图)

四、代码
top.v
cpp
module top(
input sys_clk ,
input sys_rst_n,
output uart_txd ,
output iic_scl ,
inout iic_sda
);
//---------<Parameter definition>-----------------------------------
//---------<Internal signal definition>-----------------------------
wire sda_in ;
wire sda_out ;
wire sda_oe ;
wire [31:0] humi_data ;
wire [31:0] temp_data ;
wire data_vld ;
wire [7:0] tx_data ;
wire tx_data_vld ;
wire tx_done ;
wire ready ;
//三态门描述
assign iic_sda = sda_oe ? sda_out : 1'bz;
assign sda_in = iic_sda;
aht20_top u_aht20_top(
.clk (sys_clk ),
.rst_n (sys_rst_n),
.humi_data (humi_data),
.temp_data (temp_data),
.data_vld (data_vld ),
.iic_scl (iic_scl ),
.sda_in (sda_in ),
.sda_out (sda_out ),
.sda_oe (sda_oe )
);
data_process u_data_process(
.clk (sys_clk ),
.rst_n (sys_rst_n ),
.humi_data (humi_data ),
.temp_data (temp_data ),
.data_vld (data_vld ),
.tx_data (tx_data ),
.tx_data_vld (tx_data_vld ),
.ready (ready ),
.tx_done (tx_done )
);
uart_tx u_uart_tx(
.clk (sys_clk ),
.rst_n (sys_rst_n ),
.din (tx_data ),
.start_en (tx_data_vld),//开始发送的使能信号
.tx_done (tx_done ),//一帧数据发送完成
.ready (ready ),//控制指示信号 空闲时ready为高,忙时ready为低
.uart_txd (uart_txd )
);
endmodule
uart_tx.v
cpp
module uart_tx#(parameter BAUD = 115200,
SYS_CLOCK = 50_000_000
)(
input clk ,
input rst_n ,
input [7:0] din ,
input start_en ,//开始发送的使能信号
output reg tx_done ,//一帧数据发送完成
output ready ,//控制指示信号 空闲时ready为高,忙时ready为低
output reg uart_txd
);
//---------<Parameter definition>-----------------------------------
localparam BAUD_MAX = SYS_CLOCK/BAUD;//对应波特率情况下,传输1bit需要多少个系统时钟周期
//---------<Internal signal definition>-----------------------------
reg [12:0] cnt_baud ;//波特率周期计数器
wire add_cnt_baud;
wire end_cnt_baud;
reg [3:0] cnt_bit ;//bit计数器
wire add_cnt_bit;
wire end_cnt_bit;
reg [7:0] din_r ;
reg flag;//数据帧传输的指示信号
//din_r
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
din_r <= 'd0;
end
else if(start_en)begin
din_r <= din;
end
end
//flag
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag <= 'd0;
end
else if(start_en)begin
flag <= 1'b1;
end
else if(end_cnt_bit)begin
flag <= 1'b0;
end
end
//cnt_baud
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_baud <= 'd0;
end
else if(add_cnt_baud)begin
if(end_cnt_baud)begin
cnt_baud <= 'd0;
end
else begin
cnt_baud <= cnt_baud + 1'b1;
end
end
end
assign add_cnt_baud = flag;
assign end_cnt_baud = add_cnt_baud && cnt_baud == ((cnt_bit == 9) ? ((BAUD_MAX>>1) + (BAUD_MAX>>2)) : (BAUD_MAX - 1));
//cnt_bit
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 'd0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 'd0;
end
else begin
cnt_bit <= cnt_bit + 1'b1;
end
end
end
assign add_cnt_bit = end_cnt_baud;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 10 - 1;
//uart_txd 并转串
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
uart_txd <= 1'b1;
end
else if(flag) begin
case (cnt_bit)
0 : uart_txd <= 1'b0;//起始位
1 : uart_txd <= din_r[0];//LSB
2 : uart_txd <= din_r[1];
3 : uart_txd <= din_r[2];
4 : uart_txd <= din_r[3];
5 : uart_txd <= din_r[4];
6 : uart_txd <= din_r[5];
7 : uart_txd <= din_r[6];
8 : uart_txd <= din_r[7];
9 : uart_txd <= 1'b1;//停止位
default: ;
endcase
end
else begin
uart_txd <= 1'b1;
end
end
//tx_done
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_done <= 'd0;
end
else begin
tx_done <= end_cnt_bit;
end
end
//ready
assign ready = ~flag;
endmodule
data_process
cpp
input clk ,
input rst_n ,
//aht20_ctrl
input [31:0] humi_data ,
input [31:0] temp_data ,
input data_vld ,
//seg_driver
// output reg [23:0] dig_data ,
// output reg dig_data_vld,
//uart_tx
output reg [7:0] tx_data ,
output tx_data_vld ,
input ready ,
input tx_done
);
//---------<Parameter definition>-----------------------------------
//---------<Internal signal definition>-----------------------------
wire [11:0] humi_bcd ;
wire humi_bcd_vld;
wire [11:0] temp_bcd ;
wire temp_bcd_vld;
reg [7:0] cnt ;//串口发送字节数量计数器
wire add_cnt;
wire end_cnt;
reg tx_flag;//串口发送数据的标志信号
//*******************************************************************
//--温湿度二进制-->BCD码
//*******************************************************************
//湿度转换
bin2bcd u_bin2bcd_1(
/*input */.clk (clk ),//时钟
/*input */.rst_n (rst_n ),//复位
/*input */.en (data_vld ),
/*input [DIN_W-1:0] */.binary_din (humi_data ),//输入二进制数据
/*output reg [DOUT_W-1:0] */.bcd_dout (humi_bcd ),//输出BCD码数据
/*output reg */.bcd_dout_vld(humi_bcd_vld)
);
//温度转换
bin2bcd u_bin2bcd_2(
/*input */.clk (clk ),//时钟
/*input */.rst_n (rst_n ),//复位
/*input */.en (data_vld ),
/*input [DIN_W-1:0] */.binary_din (temp_data ),//输入二进制数据
/*output reg [DOUT_W-1:0] */.bcd_dout (temp_bcd ),//输出BCD码数据
/*output reg */.bcd_dout_vld(temp_bcd_vld)
);
//*******************************************************************
//--串口显示
//*******************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 'd0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt <= 'd0;
end
else begin
cnt <= cnt + 1'b1;
end
end
end
assign add_cnt = tx_flag && ready;
assign end_cnt = add_cnt && cnt == 12 - 1;
//tx_flag
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_flag <= 'd0;
end
else if(humi_bcd_vld)begin
tx_flag <= 1'b1;
end
else if(end_cnt)begin
tx_flag <= 1'b0;
end
end
//tx_data
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_data <= 'd0;
end
else begin
case (cnt)
0 : tx_data <= "0" + humi_bcd[11:8];//湿度数据的十位
1 : tx_data <= "0" + humi_bcd[7:4];//湿度数据的个位
2 : tx_data <= ".";
3 : tx_data <= "0" + humi_bcd[3:0];//湿度数据小数点后1位
4 : tx_data <= "%";
5 : tx_data <= " ";
6 : tx_data <= "0" + temp_bcd[11:8];
7 : tx_data <= "0" + temp_bcd[7:4];
8 : tx_data <= ".";
9 : tx_data <= "0" + temp_bcd[3:0];
10 : tx_data <= 8'hA1;//℃
11 : tx_data <= 8'hE6;
default: tx_data <= 0;
endcase
end
end
assign tx_data_vld = tx_flag && ready;
endmodule
二进制转BCD
cpp
module bin2bcd#(parameter DIN_W = 32,DOUT_W = 12)(
input clk ,//时钟
input rst_n ,//复位
input en ,
input [DIN_W-1:0] binary_din ,//输入二进制数据
//输出信号定义
output reg [DOUT_W-1:0] bcd_dout ,//输出BCD码数据
output reg bcd_dout_vld
);
//状态机参数定义
localparam IDLE = 4'b0001,
READY = 4'b0010,
SHIFT = 4'b0100,
DONE = 4'b1000;
//信号定义
reg [3:0] state_c ;
reg [3:0] state_n ;
reg [DIN_W-1:0] din_r ; //数据锁存
reg [5:0] shift_cnt ; //移位次数计数器
wire add_shift_cnt;
wire end_shift_cnt;
reg [3:0] mem_r0 ;
reg [3:0] mem_r1 ;
reg [3:0] mem_r2 ;
reg [3:0] mem_r3 ;
reg [3:0] mem_r4 ;
reg [3:0] mem_r5 ;
reg [3:0] mem_r6 ;
reg [3:0] mem_r7 ;
reg [3:0] mem_r8 ;
reg [3:0] mem_r9 ;
wire [3:0] mem_w0 ;
wire [3:0] mem_w1 ;
wire [3:0] mem_w2 ;
wire [3:0] mem_w3 ;
wire [3:0] mem_w4 ;
wire [3:0] mem_w5 ;
wire [3:0] mem_w6 ;
wire [3:0] mem_w7 ;
wire [3:0] mem_w8 ;
wire [3:0] mem_w9 ;
wire [39:0] bcd_res ;
wire idle2ready ;
wire shift2done ;
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(idle2ready)
state_n = READY;
else
state_n = state_c;
end
READY:begin
state_n = SHIFT;
end
SHIFT :begin
if(shift2done)
state_n = DONE ;
else
state_n = state_c ;
end
DONE :begin
state_n = IDLE;
end
default : state_n = IDLE;
endcase
end
assign idle2ready = state_c == IDLE && (en);
assign shift2done = state_c == SHIFT && (end_shift_cnt);
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
shift_cnt <= 0;
end
else if(add_shift_cnt)begin
if(end_shift_cnt)
shift_cnt <= 0;
else
shift_cnt <= shift_cnt + 1;
end
end
assign add_shift_cnt = state_c == SHIFT;
assign end_shift_cnt = add_shift_cnt && shift_cnt == DIN_W-1;
//din_r
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
din_r <= 0;
end
else if(en)begin
din_r <= binary_din;
end
else if(state_c == SHIFT)begin //移位状态下,每个时钟周期向左移1位
din_r <= din_r << 1'b1;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
mem_r0 <= 0;
mem_r1 <= 0;
mem_r2 <= 0;
mem_r3 <= 0;
mem_r4 <= 0;
mem_r5 <= 0;
mem_r6 <= 0;
mem_r7 <= 0;
mem_r8 <= 0;
mem_r9 <= 0;
end
else if(idle2ready)begin
mem_r0 <= 0;
mem_r1 <= 0;
mem_r2 <= 0;
mem_r3 <= 0;
mem_r4 <= 0;
mem_r5 <= 0;
mem_r6 <= 0;
mem_r7 <= 0;
mem_r8 <= 0;
mem_r9 <= 0;
end
else if(state_c == SHIFT)begin
mem_r0 <= {mem_w0[2:0],din_r[DIN_W-1]};
mem_r1 <= {mem_w1[2:0],mem_w0[3]};
mem_r2 <= {mem_w2[2:0],mem_w1[3]};
mem_r3 <= {mem_w3[2:0],mem_w2[3]};
mem_r4 <= {mem_w4[2:0],mem_w3[3]};
mem_r5 <= {mem_w5[2:0],mem_w4[3]};
mem_r6 <= {mem_w6[2:0],mem_w5[3]};
mem_r7 <= {mem_w7[2:0],mem_w6[3]};
mem_r8 <= {mem_w8[2:0],mem_w7[3]};
mem_r9 <= {mem_w9[2:0],mem_w8[3]};
end
end
assign mem_w0 = (mem_r0 > 4'd4)?(mem_r0 + 4'd3):mem_r0;
assign mem_w1 = (mem_r1 > 4'd4)?(mem_r1 + 4'd3):mem_r1;
assign mem_w2 = (mem_r2 > 4'd4)?(mem_r2 + 4'd3):mem_r2;
assign mem_w3 = (mem_r3 > 4'd4)?(mem_r3 + 4'd3):mem_r3;
assign mem_w4 = (mem_r4 > 4'd4)?(mem_r4 + 4'd3):mem_r4;
assign mem_w5 = (mem_r5 > 4'd4)?(mem_r5 + 4'd3):mem_r5;
assign mem_w6 = (mem_r6 > 4'd4)?(mem_r6 + 4'd3):mem_r6;
assign mem_w7 = (mem_r7 > 4'd4)?(mem_r7 + 4'd3):mem_r7;
assign mem_w8 = (mem_r8 > 4'd4)?(mem_r8 + 4'd3):mem_r8;
assign mem_w9 = (mem_r9 > 4'd4)?(mem_r9 + 4'd3):mem_r9;
assign bcd_res = {mem_r9,mem_r8,mem_r7,mem_r6,mem_r5,mem_r4,mem_r3,mem_r2,mem_r1,mem_r0};
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
bcd_dout <= 0;
end
else if(state_c == DONE)begin
bcd_dout <= bcd_res[DOUT_W-1:0];
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
bcd_dout_vld <= 1'b0;
end
else begin
bcd_dout_vld <= state_c == DONE;
end
end
endmodule
ath20_top.v
cpp
module aht20_top(
input clk ,
input rst_n ,
//data_process
output [31:0] humi_data ,
output [31:0] temp_data ,
output data_vld ,
//aht20
output iic_scl ,
input sda_in ,
output sda_out ,
output sda_oe
);
//---------<Internal signal definition>-----------------------------
wire start_en ;
wire [3:0] cmd ;
wire [7:0] wr_data ;
wire [7:0] rd_data ;
wire trans_done;
rw_ctrl u_rw_ctrl(
.clk (clk ),
.rst_n (rst_n ),
.start_en (start_en ),
.cmd (cmd ),
.wr_data (wr_data ),
.rd_data (rd_data ),
.trans_done(trans_done),
.humi_data (humi_data ),
.temp_data (temp_data ),
.data_vld (data_vld )
);
iic_intf u_iic_intf(
.clk (clk ),
.rst_n (rst_n ),
.start_en (start_en ),//读/写使能信号
.cmd (cmd ),//命令信号
.wr_data (wr_data ),//写入的数据
.rd_data (rd_data ),//读出的数据
.trans_done(trans_done),//传输1字节完成的脉冲信号
.iic_scl (iic_scl ),
.sda_in (sda_in ),
.sda_out (sda_out ),
.sda_oe (sda_oe )
);
endmodule
rw_ctrl.v
cpp
module rw_ctrl (
input clk ,
input rst_n ,
//iic_intf
output reg start_en ,
output reg [3:0] cmd ,
output reg [7:0] wr_data ,
input [7:0] rd_data ,
input trans_done ,
//data_process
output reg [31:0] humi_data ,
output reg [31:0] temp_data ,
output reg data_vld
);
//---------<状态定义>-------------------------------------------------
localparam WAIT = 5'b00001, // 上电等待5ms
START = 5'b00010, // 发送测量命令:0x70、0xAC、0x33、0x00 --->4个字节
IDLE = 5'b00100, // 等待AHT20数据处理完成--->延时1s
READ = 5'b01000, // 发送读数据指令,并接收数据--->发0x71、收:状态字、温湿度数据(5字节)、CRC--->8个字节
VLD = 5'b10000; // 数据简单处理
reg [4:0] state_c , state_n;
//---------<常数定义>-------------------------------------------------
localparam delay_1s = 50_000_000, // 1s
delay_5ms = 2_500_000 ; // 5ms
localparam start_bit = 4, //4字节
read_bit = 8; //8字节
parameter CMD_START = 4'b0001,
CMD_WRITE = 4'b0010,
CMD_READ = 4'b0100,
CMD_STOP = 4'b1000;
//---------<状态跳变条件定义>-------------------------------------------------
wire wait2start;
wire start2idle;
wire idle2read ;
wire read2vld ;
wire vld2idle ;
//---------<寄存器定义>-------------------------------------------------
//延时计数器
reg [25:0] cnt_delay ;//分时复用的延时计数器
wire add_cnt_delay;
wire end_cnt_delay;
reg [25:0] xx;
//字节计数器
reg [3:0] cnt_byte ;//分时复用的字节计数器
wire add_cnt_byte;
wire end_cnt_byte;
reg [3:0] yy;
//接收数据
reg [7:0] state_word;//状态字
reg [7:0] CRC ;//CRC校验
//接收数据
reg [7:0] humi_high ;
reg [7:0] humi_mid ;
reg [7:0] humi_temp_byte; // 包含湿度低4位和温度高4位
reg [7:0] temp_mid ;
reg [7:0] temp_low ;
//数据寄存
reg [19:0] humi_data_r ;
reg [19:0] temp_data_r ;
reg [1:0] data_vld_r ;
//---------<延时计数器复用>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_delay <= 'd0;
end
else if(add_cnt_delay)begin
if(end_cnt_delay)begin
cnt_delay <= 'd0;
end
else begin
cnt_delay <= cnt_delay + 1'b1;
end
end
end
assign add_cnt_delay = state_c == WAIT || state_c == IDLE ;
assign end_cnt_delay = add_cnt_delay && cnt_delay == xx - 1;
always @(*)begin
case (state_c)
WAIT : xx = delay_5ms ;
IDLE : xx = delay_1s ;
default: xx = 0;
endcase
end
//---------<bit计数器复用>-------------------------------------------------
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 =( state_c == START || state_c == READ) && trans_done;
assign end_cnt_byte = add_cnt_byte && cnt_byte == yy - 1;
always @(*)begin
case (state_c)
START : yy = start_bit;
READ : yy = read_bit ;
default: yy = 0;
endcase
end
//---------<描述状态转移>-------------------------------------------------
always @(posedge clk) begin
if(!rst_n)
state_c <= WAIT;
else
state_c <= state_n;
end
//---------<描述状态转移规律>-------------------------------------------------
always @(*) begin
case(state_c)
WAIT :begin
if(wait2start)
state_n = START;
else
state_n = state_c;
end
START:begin
if(start2idle)
state_n = IDLE;
else
state_n = state_c;
end
IDLE :begin
if(idle2read)
state_n = READ;
else
state_n = state_c;
end
READ :begin
if(read2vld)
state_n = VLD;
else
state_n = state_c;
end
VLD :begin
if(vld2idle)
state_n = START;
else
state_n = state_c;
end
default : state_n = WAIT;
endcase
end
//---------<状态条件赋值>-------------------------------------------------
assign wait2start = (state_c == WAIT) && (end_cnt_delay) ;
assign start2idle = (state_c == START) && (end_cnt_byte) ;
assign idle2read = (state_c == IDLE) && (end_cnt_delay) ;
assign read2vld = (state_c == READ) && (end_cnt_byte) ;
assign vld2idle = (state_c == VLD);
//---------<状态输出>-------------------------------------------------
//start_en cmd wr_data
always @(*)begin
case (state_c)
START : begin
case (cnt_byte)
0 : begin start_en = 1'b1;cmd = CMD_START | CMD_WRITE;wr_data = 8'h70;end
1 : begin start_en = 1'b1;cmd = CMD_WRITE ;wr_data = 8'hAC;end
2 : begin start_en = 1'b1;cmd = CMD_WRITE ;wr_data = 8'h33;end
3 : begin start_en = 1'b1;cmd = CMD_STOP | CMD_WRITE ;wr_data = 8'h00;end
default: begin start_en = 1'b0;cmd = 0 ;wr_data = 8'h00;end
endcase
end
READ : begin
case (cnt_byte)
0 : begin start_en = 1'b1;cmd = CMD_START | CMD_WRITE;wr_data = 8'h71;end
7 : begin start_en = 1'b1;cmd = CMD_STOP | CMD_READ ;wr_data = 8'h00;end
default: begin start_en <= 1'b1;cmd <= CMD_READ ;wr_data <= 8'h00;end
endcase
end
default: begin start_en <= 1'b0;cmd <= 4'd0 ;wr_data <= 8'h00;end
endcase
end
//data
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
// templete_data <= 'd0;
state_word <= 'd0;
humi_high <= 'd0;
humi_mid <= 'd0;
humi_temp_byte <= 'd0;
temp_mid <= 'd0;
temp_low <= 'd0;
CRC <= 'd0;
end
else if(state_c == READ && cnt_byte >= 1 && trans_done)begin
case (cnt_byte)
1 : state_word <= rd_data;
2 : humi_high <= rd_data;
3 : humi_mid <= rd_data;
4 : humi_temp_byte <= rd_data;
5 : temp_mid <= rd_data;
6 : temp_low <= rd_data;
7 : CRC <= rd_data;
default: ;
endcase
// templete_data[39-(cnt_byte-2)*8 -:8] <= rd_data;
end
end
//data_vld_r
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data_vld_r <= 'd0;
end
else begin
data_vld_r <= {data_vld_r[0],read2vld};
end
end
//流水线处理
always @(*)begin
if(!rst_n)begin
humi_data_r = 'd0;
temp_data_r = 'd0;
end
else if(data_vld_r[0])begin
humi_data_r = {humi_high,humi_mid,humi_temp_byte[7:4]};
temp_data_r = {humi_temp_byte[3:0],temp_mid,temp_low};
end
else begin
humi_data_r = 'd0;
temp_data_r = 'd0;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
humi_data <= 'd0;
temp_data <= 'd0;
data_vld <= 'd0;
end
else if(data_vld_r[0])begin
//RH[%] = (Srh / 2^^20) * 100% -- 扩大10倍
//2^^9 + 2^^8 + 2^^7 + 2^^6 + 2^^5 + 2^^3 == 1000
humi_data <= ((humi_data_r<<9) + (humi_data_r<<8) + (humi_data_r<<7) +
(humi_data_r<<6) + (humi_data_r<<5) + (humi_data_r<<3))>>20;
//T(℃) = (St / 2^^20) * 200 - 50--扩大10倍
//2^^10 + 2^^9 + 2^^8 + 2^^7 + 2^^6 + 2^^4 == 2000
temp_data <= (((temp_data_r<<10) + (temp_data_r<<9) + (temp_data_r<<8) + (temp_data_r<<7) +
(temp_data_r<<6) + (temp_data_r<<4))>>20) - 500;
data_vld <= 1'b1;
end
else begin
data_vld <= 1'b0;
end
end
endmodule
iic_intf.v
cpp
module iic_intf#(parameter IIC_RATE = 100_000,
SYS_CLOCK = 50_000_000
)(
//user
input clk ,
input rst_n ,
input start_en ,//读/写使能信号
input [3:0] cmd ,//命令信号
input [7:0] wr_data ,//写入的数据
output reg [7:0] rd_data ,//读出的数据
output trans_done,//传输1字节完成的脉冲信号
//iic
output reg iic_scl ,
input sda_in ,
output reg sda_out ,
output reg sda_oe
);
//---------<Parameter definition>-----------------------------------
localparam IDLE = 7'b000_0001,//空闲状态
START = 7'b000_0010,//起始信号状态
WR_DATA = 7'b000_0100,//写数据状态
R_ACK = 7'b000_1000,//接收应答状态
RD_DATA = 7'b001_0000,//读数据状态
S_ACK = 7'b010_0000,//发送应答状态
STOP = 7'b100_0000;//停止信号状态
localparam SCL_MAX = SYS_CLOCK/IIC_RATE;
localparam HALF_SCL = SCL_MAX>>1,
CHANGE_TIME = SCL_MAX>>2,
SAMPLE_TIME = HALF_SCL + CHANGE_TIME;
//---------<Internal signal definition>-----------------------------
reg [6:0] state_c ;
reg [6:0] state_n ;
reg [8:0] cnt_scl ;//串行时钟计数器
wire add_cnt_scl;
wire end_cnt_scl;
reg [2:0] cnt_bit ;//比特计数器
wire add_cnt_bit;
wire end_cnt_bit;
//状态转移条件
wire idle2start ;
wire idle2wr_data ;
wire idle2rd_data ;
wire start2wr_data;
wire wr_data2r_ack;
wire r_ack2idle ;
wire r_ack2stop ;
wire rd_data2s_ack;
wire s_ack2idle ;
wire s_ack2stop ;
wire stop2idle ;
//*******************************************************************
//--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 : begin
if(idle2start)
state_n = START;
else if(idle2wr_data)
state_n = WR_DATA;
else if(idle2rd_data)
state_n = RD_DATA;
else
state_n = state_c;
end
START : begin
if(start2wr_data)
state_n = WR_DATA;
else
state_n = state_c;
end
WR_DATA : begin
if(wr_data2r_ack)
state_n = R_ACK;
else
state_n = state_c;
end
R_ACK : begin
if(r_ack2idle)
state_n = IDLE;
else if(r_ack2stop)
state_n = STOP;
else
state_n = state_c;
end
RD_DATA : begin
if(rd_data2s_ack)
state_n = S_ACK;
else
state_n = state_c;
end
S_ACK : begin
if(s_ack2idle)
state_n = IDLE;
else if(s_ack2stop)
state_n = STOP;
else
state_n = state_c;
end
STOP : begin
if(stop2idle)
state_n = IDLE;
else
state_n = state_c;
end
default : ;
endcase
end
assign idle2start = (state_c == IDLE) && start_en && cmd[0];
assign idle2wr_data = (state_c == IDLE) && start_en && cmd[1];
assign idle2rd_data = (state_c == IDLE) && start_en && cmd[2];
assign start2wr_data = (state_c == START) && end_cnt_scl;
assign wr_data2r_ack = (state_c == WR_DATA) && end_cnt_bit;
assign r_ack2idle = (state_c == R_ACK) && end_cnt_scl && ~cmd[3];
assign r_ack2stop = (state_c == R_ACK) && end_cnt_scl && cmd[3];
assign rd_data2s_ack = (state_c == RD_DATA) && end_cnt_bit;
assign s_ack2idle = (state_c == S_ACK) && end_cnt_scl && cmd[3] == 0;
assign s_ack2stop = (state_c == S_ACK) && end_cnt_scl && cmd[3];
assign stop2idle = (state_c == STOP) && end_cnt_scl;
//cnt_scl
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_scl <= 'd0;
end
else if(add_cnt_scl)begin
if(end_cnt_scl)begin
cnt_scl <= 'd0;
end
else begin
cnt_scl <= cnt_scl + 1'b1;
end
end
end
assign add_cnt_scl = state_c != IDLE;
assign end_cnt_scl = add_cnt_scl && cnt_scl == SCL_MAX - 1;
//cnt_bit
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 'd0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 'd0;
end
else begin
cnt_bit <= cnt_bit + 1'b1;
end
end
end
assign add_cnt_bit = end_cnt_scl && (state_c == WR_DATA || state_c == RD_DATA);
assign end_cnt_bit = add_cnt_bit && cnt_bit == 8 - 1;
//iic_scl
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
iic_scl <= 1'b1;
end
else if(state_c != IDLE)begin
if(cnt_scl < HALF_SCL)
iic_scl <= 1'b0;
else
iic_scl <= 1'b1;
end
else begin
iic_scl <= 1'b1;
end
end
//sda_out sda_oe
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sda_oe <= 1'b0;
sda_out <= 1'b1;
end
else begin
case (state_c)
IDLE : begin
sda_oe <= 1'b0;
sda_out <= 1'b1;
end
START : begin
sda_oe <= 1'b1;
sda_out <= (cnt_scl < SAMPLE_TIME) ? 1'b1 : 1'b0;
// if(cnt_scl < SAMPLE_TIME)
// sda_out <= 1'b1;
// else
// sda_out <= 1'b0;
end
WR_DATA : begin
sda_oe <= 1'b1;
if(cnt_scl == CHANGE_TIME - 1)//并转串
sda_out <= wr_data[7-cnt_bit];
else
sda_out <= sda_out;
end
R_ACK : begin
sda_oe <= 1'b0;
sda_out <= 1'b1;
end
RD_DATA : begin
sda_oe <= 1'b0;
sda_out <= 1'b1;
end
S_ACK : begin
sda_oe <= 1'b1;
if(cnt_scl == CHANGE_TIME - 1)
sda_out <= cmd[3] ? 1'b1 : 1'b0;
end
STOP : begin
sda_oe <= 1'b1;
if(cnt_scl == CHANGE_TIME - 1)
sda_out <= 1'b0;
else if(cnt_scl == SAMPLE_TIME - 1)
sda_out <= 1'b1;
end
default: ;
endcase
end
end
//rd_data
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rd_data <= 'd0;
end
else if(state_c == RD_DATA && cnt_scl == SAMPLE_TIME - 1)begin // 串转并
rd_data[7-cnt_bit] <= sda_in;
end
end
//trans_done
assign trans_done = r_ack2idle | s_ack2idle | stop2idle;
endmodule
五、实验现象
以下就是串口助手显示的温湿度数据。

以上就是AHT20温湿度读取并在串口显示(IIC协议)。(如果有错误,还请大家指出来,谢谢!)