目录

一、任务

二、分析

1.需要了解的

2.需要用到的模块

3.流程分析

三、Visio图

四、代码

五、实验现象


一、任务

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


二、分析

1.需要了解的

需要了解IIC协议简介

也可以看看EEPROM的读写

我写的这个代码与上面的EEPROM的读写类似。

2.需要用到的模块

需要用到,AHT20模块IIC接口模块串口发送模块,读写控制模块数据处理模块。这里我没有使用CRC,但是,我在代码里面将它存储了,感兴趣的可以自己了解一下。

3.流程分析

根据上面的AHT20的读取流程,最好的是使用状态机来写,这里我分成5个状态:

WAIT 
START
IDLE 
READ 
VLD  

WAIT:上电之后延时5ms,跳入START状态,

START:发送4个字完成以后,跳入IDLE状态,

IDLE:延时1s(大于80ms),跳入READ状态

READ:发送完8个字节,跳入VLD状态,

VLD:数据简单处理,跳入START状态循环。


三、Visio图

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


四、代码

top.v

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

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)begindin_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)beginflag <= '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)begincnt_baud <= 'd0;end else if(add_cnt_baud)begin if(end_cnt_baud)begin cnt_baud <= 'd0;endelse 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)begincnt_bit <= 'd0;end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 'd0;endelse 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)beginuart_txd <= 1'b1;end else if(flag) begin case (cnt_bit)0 : uart_txd <= 1'b0;//起始位1 : uart_txd <= din_r[0];//LSB2 : 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: ;endcaseend else begin uart_txd <= 1'b1;end 
end//tx_done
always @(posedge clk or negedge rst_n)begin if(!rst_n)begintx_done <= 'd0;end else begin tx_done <= end_cnt_bit;end 
end//ready 
assign ready = ~flag;endmodule

data_process

    input               clk         ,input               rst_n       ,//aht20_ctrlinput       [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_txoutput 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)begincnt <= 'd0;end else if(add_cnt)begin if(end_cnt)begin cnt <= 'd0;endelse 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)begintx_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)begintx_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;endcaseend 
endassign tx_data_vld = tx_flag && ready;endmodule

二进制转BCD

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) beginstate_c <= IDLE ;endelse beginstate_c <= state_n;endendalways @(*) begin case(state_c)  IDLE :beginif(idle2ready) state_n = READY;else state_n = state_c;endREADY:begin state_n = SHIFT;end SHIFT :beginif(shift2done) state_n = DONE ;else state_n = state_c ;endDONE :beginstate_n = IDLE;enddefault : state_n = IDLE;endcaseendassign idle2ready = state_c == IDLE  && (en);assign shift2done = state_c == SHIFT && (end_shift_cnt);always @(posedge clk or negedge rst_n)beginif(!rst_n)beginshift_cnt <= 0;endelse if(add_shift_cnt)beginif(end_shift_cnt)shift_cnt <= 0;elseshift_cnt <= shift_cnt + 1;endendassign add_shift_cnt = state_c == SHIFT;       assign end_shift_cnt = add_shift_cnt && shift_cnt == DIN_W-1;   //din_ralways  @(posedge clk or negedge rst_n)beginif(!rst_n)begindin_r <= 0;endelse if(en)begin din_r <= binary_din;    end else if(state_c == SHIFT)begin    //移位状态下,每个时钟周期向左移1位din_r <= din_r << 1'b1;endendalways  @(posedge clk or negedge rst_n)beginif(!rst_n)beginmem_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;endelse 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)beginmem_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]};endendassign 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)beginif(!rst_n)beginbcd_dout <= 0;endelse if(state_c == DONE)beginbcd_dout <= bcd_res[DOUT_W-1:0];endendalways  @(posedge clk or negedge rst_n)beginif(!rst_n)beginbcd_dout_vld <= 1'b0;endelse beginbcd_dout_vld <= state_c == DONE;endendendmodule

ath20_top.v

module aht20_top( input               clk       ,input               rst_n     ,//data_processoutput      [31:0]  humi_data ,output      [31:0]  temp_data ,output              data_vld  ,//aht20output              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

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, // 上电等待5msSTART = 5'b00010, // 发送测量命令:0x70、0xAC、0x33、0x00 --->4个字节IDLE  = 5'b00100, // 等待AHT20数据处理完成--->延时1sREAD  = 5'b01000, // 发送读数据指令,并接收数据--->发0x71、收:状态字、温湿度数据(5字节)、CRC--->8个字节VLD   = 5'b10000; // 数据简单处理
reg [4:0]   state_c , state_n;//---------<常数定义>------------------------------------------------- 
localparam  delay_1s  = 50_000_000,  // 1sdelay_5ms = 2_500_000 ;  // 5mslocalparam  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)begincnt_delay <= 'd0;end else if(add_cnt_delay)begin if(end_cnt_delay)begin cnt_delay <= 'd0;endelse 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)begincnt_byte <= 'd0;end else if(add_cnt_byte)begin if(end_cnt_byte)begin cnt_byte <= 'd0;endelse 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) beginif(!rst_n)state_c <= WAIT;elsestate_c <= state_n;
end//---------<描述状态转移规律>------------------------------------------------- 
always @(*) begincase(state_c)WAIT :beginif(wait2start)state_n = START; elsestate_n = state_c;endSTART:beginif(start2idle)state_n = IDLE;else state_n = state_c;endIDLE :beginif(idle2read)state_n = READ;elsestate_n = state_c;endREAD :beginif(read2vld)state_n = VLD;elsestate_n = state_c;endVLD  :beginif(vld2idle)state_n = START;elsestate_n = state_c;enddefault : 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 : begincase (cnt_byte)0 : begin start_en = 1'b1;cmd = CMD_START | CMD_WRITE;wr_data = 8'h70;end1 : begin start_en = 1'b1;cmd = CMD_WRITE            ;wr_data = 8'hAC;end2 : begin start_en = 1'b1;cmd = CMD_WRITE            ;wr_data = 8'h33;end3 : begin start_en = 1'b1;cmd = CMD_STOP | CMD_WRITE ;wr_data = 8'h00;enddefault: begin start_en = 1'b0;cmd = 0 ;wr_data = 8'h00;endendcaseendREAD : begincase (cnt_byte)0 : begin start_en = 1'b1;cmd = CMD_START | CMD_WRITE;wr_data = 8'h71;end7 : begin start_en = 1'b1;cmd = CMD_STOP  | CMD_READ ;wr_data = 8'h00;enddefault: begin start_en <= 1'b1;cmd <= CMD_READ  ;wr_data <= 8'h00;endendcaseenddefault: begin start_en <= 1'b0;cmd <= 4'd0 ;wr_data <= 8'h00;endendcase
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)begindata_vld_r <= 'd0;end else begin data_vld_r <= {data_vld_r[0],read2vld};end 
end//流水线处理
always @(*)begin if(!rst_n)beginhumi_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 beginhumi_data_r = 'd0;temp_data_r = 'd0;end 
endalways @(posedge clk or negedge rst_n)begin if(!rst_n)beginhumi_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 == 1000humi_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 == 2000temp_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 
endendmodule

iic_intf.v

module iic_intf#(parameter IIC_RATE = 100_000,SYS_CLOCK = 50_000_000
)( //userinput               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字节完成的脉冲信号//iicoutput  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)beginstate_c <= IDLE;end else begin state_c <= state_n;end 
end//第二段:组合逻辑判断状态转移条件,描述状态转移方向
always @(*) begincase(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
endassign 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)begincnt_scl <= 'd0;end else if(add_cnt_scl)begin if(end_cnt_scl)begin cnt_scl <= 'd0;endelse 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)begincnt_bit <= 'd0;end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 'd0;endelse 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)beginiic_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)beginsda_oe <= 1'b0;sda_out <= 1'b1;end else begin case (state_c)IDLE    : beginsda_oe <= 1'b0;sda_out <= 1'b1;endSTART   : beginsda_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;endWR_DATA : beginsda_oe <= 1'b1;if(cnt_scl == CHANGE_TIME - 1)//并转串sda_out <= wr_data[7-cnt_bit];else sda_out <= sda_out;endR_ACK   : beginsda_oe <= 1'b0;sda_out <= 1'b1;endRD_DATA : beginsda_oe <= 1'b0;sda_out <= 1'b1;endS_ACK   : beginsda_oe <= 1'b1;if(cnt_scl == CHANGE_TIME - 1)sda_out <= cmd[3] ? 1'b1 : 1'b0;endSTOP    : beginsda_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: ;endcaseend 
end//rd_data
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginrd_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协议)。(如果有错误,还请大家指出来,谢谢!)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/96847.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/96847.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/96847.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Pycharm SSH连接

添加远程服务器文件——>设置——>项目下的Python解释器——>添加解释器——>SSH在弹出的弹窗中&#xff0c;输入远程的主机、端口和用户名、一直下一步&#xff0c;得到如下图所示的结果&#xff1a;选择Conda 环境&#xff1a;第一步选择Conda环境&#xff1b;第…

c# 读取xml文件内的数据

好多大型的项目&#xff0c;把一些固定的参数都存在 xml文件里。创建c# winfom 项目&#xff0c;test_xml创建resources文件夹存放xml文件创建parameters.xml文件<root><test_xml><param name "threshold" value "128"/><param name …

Legion Y7000P IRX9 DriveList

Legion Y7000P IRX9 DriveList 联想Y7000P驱动列表 驱动列表 intelwlan-TYY5057FK6MQBRF0.exe NVVGA-TYY5057F3M0H9RF0.exe RTKwlan-TYY5077FFSNECRF0.exe audio-TYY5057F4N1JARF0.exe chipset-TYY5037FB10X3RF0.exe hdr-TYY5027FXNF9AWF0.exe intelVGA-TYY5057F5R9J7RF…

编程与数学 02-017 Python 面向对象编程 23课题、测试面向对象的程序

编程与数学 02-017 Python 面向对象编程 23课题、测试面向对象的程序一、单元测试&#xff08;Unit Testing&#xff09;使用 unittest 模块使用 pytest二、集成测试&#xff08;Integration Testing&#xff09;三、模拟对象&#xff08;Mocking&#xff09;四、测试驱动开发&…

[React]Antd Cascader组件地区选择

前言表单中添加一个地区选择功能&#xff0c;要求支持增删改查功能。Cascader 使用Cascader组件动态加载地区选项。使用 loadData 实现动态加载选项&#xff0c;&#xff08;loadData 与 showSearch 无法一起使用&#xff09;。 这里使用了Form.Item组件。 <Form.Itemlabel{…

深度学习-----《PyTorch神经网络高效训练与测试:优化器对比、激活函数优化及实战技巧》

一、训练过程并行批量训练机制一次性输入64个批次数据&#xff0c;创建64个独立神经网络并行训练。所有网络共享参数&#xff08;Ω&#xff09;&#xff0c;更新时计算64个批次的平均损失&#xff0c;统一更新全局参数。梯度更新策略使用torch.no_grad()上下文管理器清理反向传…

Matplotlib 可视化大师系列(五):plt.pie() - 展示组成部分的饼图

目录Matplotlib 可视化大师系列博客总览Matplotlib 可视化大师系列&#xff08;五&#xff09;&#xff1a;plt.pie() - 展示组成部分的饼图一、 饼图是什么&#xff1f;何时使用&#xff08;何时避免&#xff09;&#xff1f;二、 函数原型与核心参数三、 从入门到精通&#x…

C++ Core Guidelines 核心理念

引言 C 是一门功能强大但复杂性极高的编程语言。为了帮助开发者更高效、安全地使用现代 C&#xff0c;C 核心指南&#xff08;CppCoreGuidelines&#xff09;应运而生。这份由 C 之父 Bjarne Stroustrup 等人主导的指南&#xff0c;提供了大量关于 C 编码的规则、最佳实践和设…

vue3 - 组件间的传值

组件间传参 父传子v-on/props 父组件使用v-on:绑定要传的参数:parentData"parentData"&#xff1a; <template><div><Child1 :parentData"parentData"></Child1></div> </template> <script setup lang"ts…

Kafka 在 6 大典型用例的落地实践架构、参数与避坑清单

一、选型速查表场景关键目标推荐清单&#xff08;示例&#xff09;消息&#xff08;Messaging&#xff09;解耦、低延迟、可靠投递acksall、enable.idempotencetrue、retries>0、min.insync.replicas2、合理分区键、DLT网站活动追踪吞吐极高、可回放主题按类型拆分&#xff…

Node.js(1)—— Node.js介绍与入门

前面我们谈到一些前端开发的内容&#xff0c;学习了HTML、css和JavaScript&#xff0c;已经掌握了如何编写一些简单功能的网页。但是只属于前端部分&#xff0c;我们只能在本地打开文件进行浏览&#xff0c;不能让其他人打开我们编写的网站&#xff1b;这时就需要后端部分上场了…

Python办公——爬虫百度翻译网页版(自制翻译小工具——进阶更新版)

目录 专栏导读 前言 项目概述 功能特点 技术栈 核心架构设计 类结构设计 界面布局设计 核心功能实现 1. 智能语言检测 2. 异步翻译处理 3. HTTP请求处理 4. 结果解析与显示 界面设计亮点 1. 响应式布局 2. 用户体验优化 3. 现代化组件 技术难点与解决方案 1. 跨线程UI更新 2. U…

CentOS7 + Docker 部署 Dify 超详细图文教程

如今Agent在互联网上大行其道&#xff0c;网上吵得火热&#xff0c;各个企业也都想搭建自己的Agent。COZE的开源还有最近新出的JoyAgent也都让大家跃跃欲试&#xff0c;今天为大家带来的是Dify的部署方式&#xff0c;相比其他工作流平台&#xff0c;Dify对于整个Agent制作的流程…

vscode(MSVC)进行c++开发的时,在debug时查看一个eigen数组内部的数值

vscode进行c开发的时&#xff0c;在debug时查看一个eigen数组内部的数值问题描述解决方案拓展其他可视化使用visual studio时的可视化使用别的编译器的可视化问题描述 使用vscode进行c开发&#xff0c;编译器是MSVC&#xff0c;在debug的时候想查看一个eigen数组的数值&#x…

【51单片机】【protues仿真】基于51单片机八路抢答器数码管系统

目录 一、主要功能 二、使用步骤 三、硬件资源 四、软件设计 五、实验现象 一、主要功能 1、数码管显示 2、主持人按下开始&#xff0c;8位选手开始抢答 3、第一次使用要设置抢答时间&#xff0c;支持掉电存储 4、选手抢答成功&#xff0c;数码管会显示其号码 5、按下主…

深度学习:CUDA、PyTorch下载安装

目录 一、安装准备 二、安装CUDA 2.1 查看本机支持的 CUDA 版本及相关信息 2.2 卸载CUDA(可选) 2.3 下载cuda安装包 2.4 安装 2.5 配置环境变量 三、安装PyTorch 3.1 版本选择 3.2 下载安装 方法一&#xff1a;直接安装&#xff08;不建议&#xff0c;网差的话会死机…

MyBatis-Plus 快速入门 -常用注解

目录 1. 常用注解 TableName TableId TableField 2. IdType 枚举 3. 使用 TableField 的常见场景 4. 完整示例 5. 总结 在使用 MyBatis-Plus 的过程中&#xff0c;我们经常会用到一些注解来完成实体类与数据库表字段之间的映射关系。本文将带你快速入门&#xff0c;了解…

2025-08-23Excel 条件高亮工具,秒高亮显示符合筛选条件的行数据

Excel 条件高亮工具&#xff0c;秒高亮显示符合筛选条件的行数据 先看图【加班终结者】Excel 条件高亮工具&#xff08;试用版&#xff09; ——让错误数据一秒现形&#xff0c;免费先爽 30 次&#xff01; 你是不是也这样&#xff1f; • 财务对账&#xff0c;1000 行工资表里…

vue 一键打包上传

npm run build之后&#xff0c;将dist文件夹自动压缩&#xff0c;通过ssh自动连接服务器&#xff0c;把压缩包放到指定目录下&#xff0c;然后自动解压根目录创建gulpfile.js文件const gulp require(gulp); const GulpSSH require(gulp-ssh); const archiver require(archiv…