AHT10读取温湿度

一.理论原理

  • I2C 接口模块实现读写数据的流程,将 done 信号(读写一次完成信号)作为输出。AHT10 控制模块例化 I2C 接口模块,将 done 信号作为条件使用,在对应的状态输入对应的命令,最终得到一个传感器温度转换得来的 40 位位宽的数据。将 40 位位宽的数据作为输入传输给数据处理模块,在此模块中,将 40 位位宽的数据转换为温湿度分开的两个 20 位位宽的数据,并将二进制转换为 BCD 码,再将 BCD 码转换为 ASCII 码。将转换完成的 8bit 数据作为输入传输给串口 TX 模块,最终输出到 PC 端上,显示出温湿度情况。

二.手册分析

2.1命令及读取流程

在这里插入图片描述

在这里插入图片描述

2.2器件地址

在这里插入图片描述

2.3读写数据格式

在这里插入图片描述

在这里插入图片描述

2.4数据转换

在这里插入图片描述

2.5测量周期

在这里插入图片描述

2.6考察校准

在这里插入图片描述

三.设计

3.1总体框图设计

在这里插入图片描述

3.2状态转移图

3.2.1 iic_interface

在这里插入图片描述

3.2.2 aht10_ctrl

在这里插入图片描述

3.3时序图

3.3.1 iic_interface

在这里插入图片描述

3.3.2 aht10_ctrl

在这里插入图片描述

在这里插入图片描述


因为图片太长,分两部分截图

四.代码

4.1 aht10_ctrl.v

(iic_interface可直接用前文IIC读写EEPROM一文的接口模块)

/**************************************功能介绍***********************************
Date	: 
Author	: Alegg xy.
Version	: 
Description: 
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module aht10_ctrl ( 
    input				clk		,
    input				rst_n	,
    input       [6:0]   device_id,

    output      [39:0]  data    ,
    output              data_vld,
    output              ready   ,

    output              scl     ,
    inout               sda     
);								 
//---------<参数定义>--------------------------------------------------------- 
    //接口模块控制命令
    `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
    
    //状态机参数定义
    localparam  IDLE        = 'b0000000001,
                INIT_REQ    = 'b0000000010,
                INIT        = 'b0000000100,
                WAIT        = 'b0000001000,
                RD_REQ      = 'b0000010000,
                RD          = 'b0000100000,
                DELAY       = 'b0001000000,
                READ_REQ    = 'b0010000000,
                READ        = 'b0100000000,
                DONE        = 'b1000000000;

    localparam  AHT10_WRIGHT  =  8'b0111_0000;//  写数据命令
    localparam  AHT10_READ    =  8'b0111_0001;//  读数据命令

    localparam  AHT10_INIT_0  =  8'hE1;//  初始化命令序列  
    localparam  AHT10_INIT_1  =  8'h08;
    localparam  AHT10_INIT_2  =  8'h00;

    localparam  AHT10_MEAS_0  =  8'hAC;//  触发测量命令序列   
    localparam  AHT10_MEAS_1  =  8'h33;
    localparam  AHT10_MEAS_2  =  8'h00;

    parameter   MAX_40MS = 'd200_000,
                MAX_80MS = 'd400_000,
                MAX_2S   = 'd100_000_000;  
//---------<内部信号定义>-----------------------------------------------------
    reg 	[9:0]	cstate     ;//现态
    reg	    [9:0]	nstate     ;//次态

    wire    IDLE_INIT_REQ   ;
    wire    INIT_REQ_INIT   ;
    wire    INIT_INIT_REQ   ;
    wire    INIT_WAIT       ;
    wire    WAIT_RD_REQ     ;
    wire    RD_REQ_RD       ;
    wire    RD_RD_REQ       ;
    wire    RD_DELAY        ;
    wire    DELAY_READ_REQ  ;
    wire    READ_REQ_READ   ;
    wire    READ_READ_REQ   ;
    wire    READ_DONE       ;
    wire    DONE_WAIT       ;

    //例化接口模块信号
    wire                done            ;
    reg         [4:0]   cmd             ;
    reg                 cmd_vld         ;
    reg         [7:0]   op_wr_data      ;

    //延时计数器
    reg			[27:0]	cnt_delay	   	;
    wire				add_cnt_delay	;
    wire				end_cnt_delay	;

    //字节计数器
    reg			[3:0]	cnt_byte	   	;
    wire				add_cnt_byte	;
    wire				end_cnt_byte	;

    reg         [27:0]  MAX             ;//计时最大值
    reg         [3:0]   cmd_num         ;

    //数据处理
    wire    [7:0]   rd_data;
    reg     [47:0]  rd_data_r;
    wire            rd_data_vld;

    //延时计数器    
    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'd1;
            end 
        end
    end 
    
    assign add_cnt_delay = (cstate == IDLE) || (cstate == DONE) || (cstate == DELAY);
    assign end_cnt_delay = add_cnt_delay && cnt_delay == MAX - 1'd1;
    
    //考察MAX情况
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            MAX <= MAX_40MS;
        end
        else if (cstate == IDLE) begin
            MAX <= MAX_40MS;//上电延时40ms
        end
        else if (cstate == DELAY) begin
            MAX <= MAX_80MS;//测量延时80ms
        end
        else if (cstate == DONE) begin
            MAX <= MAX_2S;
        end
    end

    //字节计数器
    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'd1;
            end 
        end
    end 
    
    assign add_cnt_byte = done;
    assign end_cnt_byte = add_cnt_byte && cnt_byte == cmd_num - 1;
    
    //考察cmd_num
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cmd_num <= 0;
        end
        else if (cstate == INIT) begin
            cmd_num <= 4;
        end
        else if (cstate == RD) begin
            cmd_num <= 4;
        end
        else if (cstate == READ) begin
            cmd_num <= 7;
        end
    end
    //第一段:时序逻辑描述状态转移
    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 (IDLE_INIT_REQ) begin
                    nstate = INIT_REQ;
                end
                else begin
                    nstate = cstate;
                end
            end 
            INIT_REQ :begin
                if (INIT_REQ_INIT) begin
                    nstate = INIT;
                end
                else begin
                    nstate = cstate;
                end
            end 
            INIT     :begin
                if (INIT_WAIT) begin
                    nstate = WAIT;
                end
                else if (INIT_INIT_REQ) begin
                    nstate = INIT_REQ;
                end
                else begin
                    nstate = cstate;
                end
            end 
            WAIT     :begin
                if (WAIT_RD_REQ) begin
                    nstate = RD_REQ;
                end
                else begin
                    nstate = cstate;
                end
            end 
            RD_REQ   :begin
                if (RD_REQ_RD) begin
                    nstate = RD;
                end
                else begin
                    nstate = cstate;
                end
            end 
            RD       :begin
                if (RD_DELAY) begin
                    nstate = DELAY;
                end
                else if (RD_RD_REQ) begin
                    nstate = RD_REQ;
                end
                else begin
                    nstate = cstate;
                end
            end 
            DELAY    :begin
                if (DELAY_READ_REQ) begin
                    nstate = READ_REQ;
                end
                else begin
                    nstate = cstate;
                end
            end 
            READ_REQ :begin
                if (READ_REQ_READ) begin
                    nstate = READ;
                end
                else begin
                    nstate = cstate;
                end
            end 
            READ     :begin
                if (READ_DONE) begin
                    nstate = DONE;
                end
                else if (READ_READ_REQ) begin
                    nstate = READ_REQ;
                end
                else begin
                    nstate = cstate;
                end
            end 
            DONE     :begin
                if (DONE_WAIT) begin
                    nstate = WAIT;
                end
                else begin
                    nstate = cstate;
                end
            end 
            default  :;
        endcase
    end

    assign IDLE_INIT_REQ  = (cstate == IDLE)        && end_cnt_delay; 
    assign INIT_REQ_INIT  = (cstate == INIT_REQ)    && 1'b1         ; 
    assign INIT_INIT_REQ  = (cstate == INIT)        && done         ; 
    assign INIT_WAIT      = (cstate == INIT)        && end_cnt_byte ; 
    assign WAIT_RD_REQ    = (cstate == WAIT)        && 1'b1         ; 
    assign RD_REQ_RD      = (cstate == RD_REQ)      && 1'b1         ; 
    assign RD_RD_REQ      = (cstate == RD)          && done         ; 
    assign RD_DELAY       = (cstate == RD)          && end_cnt_byte ; 
    assign DELAY_READ_REQ = (cstate == DELAY)       && end_cnt_delay; 
    assign READ_REQ_READ  = (cstate == READ_REQ)    && 1'b1         ; 
    assign READ_READ_REQ  = (cstate == READ)        && done         ; 
    assign READ_DONE      = (cstate == READ)        && end_cnt_byte ; 
    assign DONE_WAIT      = (cstate == DONE)        && end_cnt_delay;            
    
    //发送指令
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            TX(0,0,0);
        end
        else begin
            case (cstate)
                INIT_REQ:begin//发送0xE1
                    case (cnt_byte)
                        0:TX(1,(`START_BIT | `WRITE_BIT),AHT10_WRIGHT);
                        1:TX(1,(`WRITE_BIT),AHT10_INIT_0);
                        2:TX(1,(`WRITE_BIT),AHT10_INIT_1);
                        3:TX(1,(`WRITE_BIT | `STOP_BIT),AHT10_INIT_2);
                        default: TX(0,cmd,op_wr_data);
                    endcase
                end
                RD_REQ:begin//发送0xAC
                    case (cnt_byte)
                        0:TX(1,{`WRITE_BIT | `START_BIT},AHT10_WRIGHT);
                        1:TX(1,{`WRITE_BIT},AHT10_MEAS_0);
                        2:TX(1,{`WRITE_BIT},AHT10_MEAS_1);
                        3:TX(1,{`WRITE_BIT | `STOP_BIT},AHT10_MEAS_2);
                        default: TX(0,cmd,op_wr_data);
                    endcase
                end
                READ_REQ:begin//接受数据
                    case (cnt_byte)
                        0:TX(1,{`WRITE_BIT | `START_BIT},AHT10_READ);
                        1:TX(1,{`READ_BIT},8'h00);
                        2:TX(1,{`READ_BIT},8'h00);
                        3:TX(1,{`READ_BIT},8'h00);
                        4:TX(1,{`READ_BIT},8'h00);
                        5:TX(1,{`READ_BIT},8'h00);
                        6:TX(1,{`READ_BIT | `ACK_BIT | `STOP_BIT},8'h00);
                        default: TX(0,cmd,op_wr_data);
                    endcase
                end
                default: TX(0,cmd,op_wr_data);
            endcase
        end
    end

    //数据处理
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rd_data_r <= 'd0;
        end
        else if (rd_data_vld) begin
            rd_data_r <= {rd_data_r[39:0],rd_data};
        end
    end            
                
    //接口模块例化
    i2c_interface u_i2c_interface(
        .clk        (clk),
        .rst_n      (rst_n),
        .cmd        (cmd),
        .cmd_vld    (cmd_vld),
        .done       (done),
        .wr_data    (op_wr_data),
        .rd_data    (rd_data),
        .rd_data_vld(rd_data_vld),
        .scl        (scl),
        .sda        (sda)
    );

    task TX;
        input           task_cmd_vld;
        input   [4:0]   task_cmd;
        input   [7:0]   task_wr_data;

        begin
            cmd_vld = task_cmd_vld;
            cmd = task_cmd;
            op_wr_data = task_wr_data;
        end
    endtask

    assign data = rd_data_r[39:0];
    assign data_vld = READ_DONE;
    assign ready = cstate == WAIT;
endmodule

4.2 data_process(数据处理模块)

/**************************************功能介绍***********************************
Date	: 
Author	: Alegg xy.
Version	: 
Description: 
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module data_process( 
    input				clk		,
    input				rst_n	,
    input   [39:0]      data    ,
    input               data_vld,
    input               ready   ,

    output  [7:0]       tx_data ,
    output              tx_data_vld
);								 
//---------<参数定义>--------------------------------------------------------- 
    //状态机参数定义
    localparam  IDLE   = 'b01,//
                DATA   = 'b10;//
    
    reg     [19:0]      data_hum;//湿度数据
    reg     [19:0]      data_temp;//温度数据

    reg     [40:0]      data_hum_r;
    reg     [40:0]      data_temp_r;
    wire     [7:0]      data_hum_w0;
    wire     [7:0]      data_hum_w1;
    wire     [7:0]      data_hum_w2;
    wire     [7:0]      data_temp_w0;
    wire     [7:0]      data_temp_w1;
    wire     [7:0]      data_temp_w2;

    reg                 data_vld_0;
    reg                 data_vld_1;

    reg 	[1:0]	cstate     ;//现态
    reg	    [1:0]	nstate     ;//次态
    wire            IDLE_DATA  ;
    wire            DATA_IDLE  ;

    reg			[4:0]	cnt_byte	   	;
    wire				add_cnt_byte	;
    wire				end_cnt_byte	;

    reg         [7:0]   num             ;
//---------<内部信号定义>-----------------------------------------------------
    //数据有效信号打拍
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_vld_0 <= 0;
            data_vld_1 <= 0;
        end
        else begin
            data_vld_0 <= data_vld;
            data_vld_1 <= data_vld_0;
        end
    end

    //数据寄存
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_hum <= 0;
            data_temp <= 0;
        end
        else if (data_vld) begin
            data_hum <= data[39:20];
            data_temp <= data[19:0];
        end
        else begin
            data_hum <= data_hum;
            data_temp <= data_temp;
        end
    end
    
    //二进制转BCD
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_hum_r <= 12'd123;
        end
        else if (data_vld_1) begin
            data_hum_r <= ((data_hum*10000)>>20);
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_temp_r <= 12'd123;
        end
        else if (data_vld_1) begin
            data_temp_r <= (((data_temp*20000)>>20)-5000);
        end
    end
    
    //每一位数据    
    assign data_hum_w0 = (data_hum_r/1000)%10;
    assign data_hum_w1 = (data_hum_r/100)%10;
    assign data_hum_w2 = (data_hum_r/10)%10;

    assign data_temp_w0 = (data_temp_r/1000)%10;
    assign data_temp_w1 = (data_temp_r/100)%10;
    assign data_temp_w2 = (data_temp_r/10)%10;
    
    //byte计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_byte <= 4'd0;
        end 
        else if(add_cnt_byte)begin 
            if(end_cnt_byte)begin 
                cnt_byte <= 4'd0;
            end
            else begin 
                cnt_byte <= cnt_byte + 1'b1;
            end 
        end
    end 

    assign add_cnt_byte = (cstate == DATA) && ready;
    assign end_cnt_byte = add_cnt_byte && cnt_byte == 18;

    //第一段:时序逻辑描述状态转移
    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 (IDLE_DATA) begin
                    nstate = DATA;
                end
                else begin
                    nstate = cstate;
                end
            end
            DATA:begin
                if (DATA_IDLE) begin
                    nstate = IDLE;
                end
                else begin
                    nstate = cstate;
                end
            end
            default : nstate = IDLE;
        endcase
    end

    assign IDLE_DATA = (cstate == IDLE) && ready && data_vld;
    assign DATA_IDLE = (cstate == DATA) && end_cnt_byte;          
    
    //BCD转ASCII码
    always @(*) begin
        if (cstate == DATA) begin
            case (cnt_byte)
                0:  num = 8'hCE;
                1:  num = 8'hC2;
                2:  num = 8'hB6;
                3:  num = 8'hC8;
                4:  num = ":";
                5:  num = data_temp_w0 + "0";
                6:  num = data_temp_w1 + "0";
                7:  num = ".";
                8:  num = data_temp_w2 + "0";
                9:  num =  8'hCA;
                10:  num = 8'hAA;
                11:  num = 8'hB6;
                12:  num = 8'hC8;
                13:  num = ":";
                14:  num = data_hum_w0 + "0";
                15:  num = data_hum_w1 + "0";
                16:  num = ".";
                17:  num = data_hum_w2 + "0";
                18:  num = 8'h0a;
                default: num = "0";
            endcase
        end
    end            


    assign tx_data = num;
    assign tx_data_vld = add_cnt_byte;         
endmodule

五.仿真

5.1仿真代码

`timescale 1ns/1ns
    
module tb_top();

//激励信号定义 
    reg				tb_clk  	;
    reg				tb_rst_n	;

//输出信号定义	 
    wire            tx          ;
    wire            scl         ;
    wire            sda         ;

    pullup(sda);
//时钟周期参数定义	
    parameter		CLOCK_CYCLE = 20;   

    defparam    u_top.u_aht10_ctrl.MAX_40MS = 20,
                u_top.u_aht10_ctrl.MAX_80MS = 40,
                u_top.u_aht10_ctrl.MAX_2S = 1000;
//模块例化
    top u_top(	
    .clk		(tb_clk			),
    .rst_n		(tb_rst_n		),
    .tx         (tx),
    .scl        (scl),
    .sda        (sda)
    );

    i2c_slave_model u_i2c_slave_model(
        .scl(scl),
        .sda(sda)
    );

//产生时钟
    initial 		tb_clk = 1'b0;
    always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;
//产生激励
    initial  begin 
        tb_rst_n = 1'b1;
        #(CLOCK_CYCLE*2);
        tb_rst_n = 1'b0;
        #(CLOCK_CYCLE*20);
        tb_rst_n = 1'b1;

        #5000000;
        $stop;

    end

endmodule 

其中用到了仿真模型,完整工程可以通过https://download.csdn.net/download/weixin_67803687/88384435获取

5.2仿真效果

5.2.1 scl & sda关系

在这里插入图片描述

5.2.2 不同状态计bit数不同

在这里插入图片描述

读数据和写数据时 8bit,起始位,校验位,停止位都是 1bit

5.2.3 不同状态命令值不同

在这里插入图片描述

由图可知我们不同状态的命令不同,需要符合起始位/写数据/读数据/停止位/校验位,对应的关系存在如下情况
在这里插入图片描述

5.2.4 初始化指令 & 温度测量指令

在这里插入图片描述

5.2.5 aht10_ctrl模块初始化命令状态循环

在这里插入图片描述

温度测量命令同理,读数据更改为 7 个字节,也就是循环 7 次

5.2.6 计数器复用

在这里插入图片描述

六.效果

在这里插入图片描述

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值