• 基于FPGA的AHT10传感器温湿度读取


    一、系统框架

    分为i2c接口、i2c控制、数据处理、串口四个部分
    在这里插入图片描述
    RTL视图
    在这里插入图片描述

    二、i2c接口

    该传感器通过i2c协议进行通信。需要该接口实现i2c的数据收发。接口模块都是固定代码,不做讲解。
    代码如下:

    `include "param.v"
    
    module i2c_intf(
        input               clk         ,
        input               rst_n       ,
    
        input               req         ,
        input       [3:0]   cmd         ,
        input       [7:0]   din         ,
    
        output      [7:0]   dout        ,
        output              done        ,
        output              slave_ack   ,
        output              i2c_scl     ,
        input               i2c_sda_i   ,
        output              i2c_sda_o   ,
        output              i2c_sda_oe     
        );
    
    //状态机参数定义
    
        localparam  IDLE  = 7'b000_0001,
                    START = 7'b000_0010,
                    WRITE = 7'b000_0100,
                    RACK  = 7'b000_1000,
                    READ  = 7'b001_0000,
                    SACK  = 7'b010_0000,
                    STOP  = 7'b100_0000;
    
    //信号定义
    
        reg     [6:0]       state_c     ;
        reg     [6:0]       state_n     ;
    
        reg     [8:0]       cnt_scl     ;//产生i2c时钟
        wire                add_cnt_scl ;
        wire                end_cnt_scl ;
        reg     [3:0]       cnt_bit     ;//传输数据 bit计数器
        wire                add_cnt_bit ;
        wire                end_cnt_bit ;
        reg     [3:0]       bit_num     ;
        
        reg                 scl         ;//输出寄存器
        reg                 sda_out     ;
        reg                 sda_out_en  ;
    
        reg     [7:0]       rx_data     ;
        reg                 rx_ack      ;
        reg     [3:0]       command     ;
        reg     [7:0]       tx_data     ;//发送数据
    
        wire                idle2start  ; 
        wire                idle2write  ; 
        wire                idle2read   ; 
        wire                start2write ; 
        wire                start2read  ; 
        wire                write2rack  ; 
        wire                read2sack   ; 
        wire                rack2stop   ; 
        wire                sack2stop   ; 
        wire                rack2idle   ; 
        wire                sack2idle   ; 
        wire                stop2idle   ; 
    
    
    //状态机
        always @(posedge clk or negedge rst_n) begin 
            if (rst_n==0) 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(idle2write)
                        state_n = WRITE ;
                    else if(idle2read)
                        state_n = READ ;
                    else 
                        state_n = state_c ;
                end
                START :begin
                    if(start2write)
                        state_n = WRITE ;
                    else if(start2read)
                        state_n = READ ;
                    else 
                        state_n = state_c ;
                end
                WRITE :begin
                    if(write2rack)
                        state_n = RACK ;
                    else 
                        state_n = state_c ;
                end
                RACK :begin
                    if(rack2stop)
                        state_n = STOP ;
                    else if(rack2idle)
                        state_n = IDLE ;
                    else 
                        state_n = state_c ;
                end
                READ :begin
                    if(read2sack)
                        state_n = SACK ;
                    else 
                        state_n = state_c ;
                end
                SACK :begin
                    if(sack2stop)
                        state_n = STOP ;
                    else if(sack2idle)
                        state_n = IDLE ;
                    else 
                        state_n = state_c ;
                end
                STOP :begin
                    if(stop2idle)
                        state_n = IDLE ;
                    else 
                        state_n = state_c ;
                end
                default : state_n = IDLE ;
            endcase
        end
        
        assign idle2start  = state_c==IDLE  && (req && (cmd&`CMD_START));
        assign idle2write  = state_c==IDLE  && (req && (cmd&`CMD_WRITE));
        assign idle2read   = state_c==IDLE  && (req && (cmd&`CMD_READ ));
        assign start2write = state_c==START && (end_cnt_bit && (command&`CMD_WRITE));
        assign start2read  = state_c==START && (end_cnt_bit && (command&`CMD_READ ));
        assign write2rack  = state_c==WRITE && (end_cnt_bit);
        assign read2sack   = state_c==READ  && (end_cnt_bit);
        assign rack2stop   = state_c==RACK  && (end_cnt_bit && (command&`CMD_STOP ));
        assign sack2stop   = state_c==SACK  && (end_cnt_bit && (command&`CMD_STOP ));
        assign rack2idle   = state_c==RACK  && (end_cnt_bit && (command&`CMD_STOP ) == 0);
        assign sack2idle   = state_c==SACK  && (end_cnt_bit && (command&`CMD_STOP ) == 0);
        assign stop2idle   = state_c==STOP  && (end_cnt_bit );
        
    //计数器
        always @(posedge clk or negedge rst_n) begin 
            if (rst_n==0) begin
                cnt_scl <= 0; 
            end
            else if(add_cnt_scl) begin  
                if(end_cnt_scl)
                    cnt_scl <= 0; 
                else
                    cnt_scl <= cnt_scl+1 ;
           end
        end
        assign add_cnt_scl = (state_c != IDLE);
        assign end_cnt_scl = add_cnt_scl  && cnt_scl == (`SCL_PERIOD)-1 ;
    
        always @(posedge clk or negedge rst_n) begin 
            if (rst_n==0) begin
                cnt_bit <= 0; 
            end
            else if(add_cnt_bit) begin
                if(end_cnt_bit)
                    cnt_bit <= 0; 
                else
                    cnt_bit <= cnt_bit+1 ;
           end
        end
        assign add_cnt_bit = (end_cnt_scl);
        assign end_cnt_bit = add_cnt_bit  && cnt_bit == (bit_num)-1 ;
    
        always  @(*)begin
            if(state_c == WRITE | state_c == READ) begin
                bit_num = 8;
            end
            else begin 
                bit_num = 1;
            end 
        end
    //command
        always  @(posedge clk or negedge rst_n)begin
            if(~rst_n)begin
                command <= 0;
            end
            else if(req)begin
                command <= cmd;
            end
        end
    
    //tx_data
        always  @(posedge clk or negedge rst_n)begin
            if(~rst_n)begin
                tx_data <= 0;
            end
            else if(req)begin
                tx_data <= din;
            end
        end
    
    //scl
        always  @(posedge clk or negedge rst_n)begin
            if(~rst_n)begin
                scl <= 1'b1;
            end
            else if(idle2start | idle2write | idle2read)begin//开始发送时,拉低
                scl <= 1'b0;
            end
            else if(add_cnt_scl && cnt_scl == `SCL_HALF-1)begin 
                scl <= 1'b1;
            end 
            else if(end_cnt_scl && ~stop2idle)begin 
                scl <= 1'b0;
            end 
        end
    
    //sda_out
        always  @(posedge clk or negedge rst_n)begin
            if(~rst_n)begin
                sda_out <= 1'b1;
            end
            else if(state_c == START)begin          //发起始位
                if(cnt_scl == `LOW_HLAF)begin       //时钟低电平时拉高sda总线
                    sda_out <= 1'b1;
                end
                else if(cnt_scl == `HIGH_HALF)begin    //时钟高电平时拉低sda总线 
                    sda_out <= 1'b0;                //保证从机能检测到起始位
                end 
            end 
            else if(state_c == WRITE && cnt_scl == `LOW_HLAF)begin  //scl低电平时发送数据   并串转换
                sda_out <= tx_data[7-cnt_bit];      
            end 
            else if(state_c == SACK && cnt_scl == `LOW_HLAF)begin  //发应答位
                sda_out <= (command&`CMD_STOP)?1'b1:1'b0;
            end 
            else if(state_c == STOP)begin //发停止位
                if(cnt_scl == `LOW_HLAF)begin       //时钟低电平时拉低sda总线
                    sda_out <= 1'b0;
                end
                else if(cnt_scl == `HIGH_HALF)begin    //时钟高电平时拉高sda总线 
                    sda_out <= 1'b1;                //保证从机能检测到停止位
                end 
            end 
        end
    
    //sda_out_en  总线输出数据使能
        always  @(posedge clk or negedge rst_n)begin
            if(~rst_n)begin
                sda_out_en <= 1'b0;
            end
            else if(idle2start | idle2write | read2sack | rack2stop)begin
                sda_out_en <= 1'b1;
            end
            else if(idle2read | start2read | write2rack | stop2idle)begin 
                sda_out_en <= 1'b0;
            end 
        end
    
    //rx_data       接收读入的数据
        always  @(posedge clk or negedge rst_n)begin
            if(~rst_n)begin
                rx_data <= 0;
            end
            else if(state_c == READ && cnt_scl == `HIGH_HALF)begin
                rx_data[7-cnt_bit] <= i2c_sda_i;    //串并转换
            end
        end
    
    //rx_ack
        always  @(posedge clk or negedge rst_n)begin
            if(~rst_n)begin
                rx_ack <= 1'b1;
            end
            else if(state_c == RACK && cnt_scl == `HIGH_HALF)begin
                rx_ack <= i2c_sda_i;
            end
        end
    
    
    //输出信号
    
        assign i2c_scl    = scl         ;
        assign i2c_sda_o  = sda_out     ;
        assign i2c_sda_oe = sda_out_en  ;
       
        assign dout = rx_data;
        assign done = rack2idle | sack2idle | stop2idle;
        assign slave_ack = rx_ack;
    
    endmodule
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295

    三、i2c控制模块

    该模块负责采集AHT10传感器的数据,并把采集到的数据输出到数据处理模块。

    状态机设计

    状态转移图

    在这里插入图片描述

    START

    上电、复位后默认状态。根据手册信息可知,等待40ms后跳转到init初始化状态

    INIT

    根据手册信息可知,等待40ms后需要进行初始化,在该状态控制i2c接口发送对应命令即可.发送完毕进入CHECK_INIT状态.在这里插入图片描述

    CHECK_INIT

    发送玩初始化命令后,通过发送命令0x71检测是否初始化成功,如果失败则回到INIT状态,成功则进入IDLE状态

    IDLE

    空闲状态,当传感器初始化完成后会进入空闲状态,在空闲状态持续0.5s后将会进入TRIGGER发送测量命令状态

    TRIGGER

    通过i2c接口发送如下命令即可,发送完毕进入WAIT状态
    在这里插入图片描述

    WAIT

    等待80ms后进入READ状态
    在这里插入图片描述

    READ

    通过i2c接口接受传感器发送的六个字节数据,根据状态为判断数据是否有效,有效则进行输出,无效则忽略。读取完毕后回到IDLE状态。
    在这里插入图片描述

    代码

    `include "param.v"
    
    module i2c_master (
        input               clk     ,
        input               rst_n   ,
        
        input               din_vld ,
        output              req     ,
        output      [3:0]   cmd     ,
        output      [7:0]   data    ,
        input               done    , //传输完成标志
        input       [7:0]   rd_data ,
        output		[ 19:0 ]	hum_data			,//湿度
        output      [ 19:0 ]	temp_data			,//温度	
        output				    dout_vld			
        );
    
    
    //状态机参数
        localparam      START =      7'b000_0001    , //等待40ms
                        INIT =       7'b000_0010    , //初始化
                        CHECK_INIT = 7'b000_0100    , //检测是否初始化完成
                        IDLE =       7'b000_1000    , //空闲
                        TRIGGER =    7'b001_0000    , //触发测量
                        WAIT =       7'b010_0000    , //等待80ms测量完成
                        READ =       7'b100_0000    ; //读取温湿度
    
        parameter	DELAY_40MS = 200_0000;//40ms
        parameter	DELAY_80MS = 400_0000;//80ms
        parameter	DELAY_500MS = 2500_0000;//0.5s
    
    //信号定义
        reg     [7:0]   state_c         ;
        reg     [7:0]   state_n         ;
    
        reg     [2:0]   cnt_byte        ;//数据传输 字节计数器
        wire            add_cnt_byte    ;
        wire            end_cnt_byte    ;
    
        reg             tx_req          ;//请求
        reg     [3:0]   tx_cmd          ;
        reg     [7:0]   tx_data         ;
        reg			[ 47:0 ]			read_data			;
    
        
        reg			[ 27:0 ]			cnt			;//40ms 80ms计数器
        wire							add_cnt			;
        wire							end_cnt			;
    
        reg							    finish_init			;
        wire							start_2_init		;//等待40ms后进入初始化
        wire							init_2_check		;//检查是否初始化成功
        wire							check_2_idle			;//初始化完成进入空闲
        wire							check_2_init			;//初始化失败重新初始化
        wire							idle_2_trigger			;//发送读取温湿度指令
        wire							trigger_2_wait			;//等待转换完成
        wire							wait_2_read			;//读取温湿度
        wire							read_2_idle			;//读取完毕进入空闲
    
        assign start_2_init = state_c == START && (end_cnt);
        assign init_2_check = state_c == INIT && (end_cnt_byte);
        assign check_2_idle = state_c == CHECK_INIT && (finish_init);
        assign check_2_init = state_c == CHECK_INIT && (~finish_init);
        assign idle_2_trigger = state_c == IDLE && (end_cnt);
        assign trigger_2_wait = state_c == TRIGGER && (end_cnt_byte);
        assign wait_2_read = state_c == WAIT && (end_cnt);
        assign read_2_idle = state_c == READ && (end_cnt_byte);
    
    //状态机设计
        always @(posedge clk or negedge rst_n) begin 
            if (rst_n==0) begin
                state_c <= START ;
            end
            else begin
                state_c <= state_n;
           end
        end
    //状态跳转   
        always @(*) begin 
            case(state_c)  
                START :begin
                    if(start_2_init)
                        state_n = INIT ;
                    else 
                        state_n = state_c ;
                end
                INIT :begin
                    if(init_2_check)
                        state_n = IDLE ;
                    else 
                        state_n = state_c ;
                end
                CHECK_INIT :begin
                    if(check_2_idle)
                        state_n = IDLE ;
                    else if(check_2_init) begin
                        state_n = INIT;
                    end
                    else 
                        state_n = state_c ;
                end
                IDLE :begin
                    if(idle_2_trigger)
                        state_n = TRIGGER ;
                    else 
                        state_n = state_c ;
                end
                TRIGGER :begin
                    if(trigger_2_wait)
                        state_n = WAIT ;
                    else 
                        state_n = state_c ;
                end
                WAIT :begin
                    if(wait_2_read)
                        state_n = READ ;
                    else 
                        state_n = state_c ;
                end
                READ :begin
                    if(read_2_idle)
                        state_n = IDLE ;
                    else 
                        state_n = state_c ;
                end
    
                default : state_n = START ;
            endcase
        end
    
        reg			[ 24:0 ]			delay			;
        
    //延时数据寄存器
        always @(posedge clk or negedge rst_n) begin
            if(!rst_n) begin
                delay <= DELAY_40MS;
            end
            else if(state_c == START) begin
                delay <= DELAY_40MS;
            end
            else if(state_c == WAIT) begin
                delay <= DELAY_80MS;
            end
            else if(state_c == IDLE) begin
                delay <= DELAY_500MS;
            end
        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 <= 0;
            end
        end
        assign add_cnt = state_c == START || state_c == WAIT || state_c == IDLE;
        assign end_cnt = cnt == delay - 1 && add_cnt;
    
    //字节计数器
        always @(posedge clk or negedge rst_n) begin
            if(!rst_n)begin
                cnt_byte <= 0;
            end
            else if(add_cnt_byte) begin
                if(end_cnt_byte) begin
                    cnt_byte <= 0;
                end
                else
                    cnt_byte <= cnt_byte + 1;
            end
        end
        assign add_cnt_byte = (state_c == INIT || state_c == CHECK_INIT ||state_c == TRIGGER || state_c == READ) && done;
        assign end_cnt_byte = cnt_byte == (state_c == READ?6:3) && add_cnt_byte;
    
    /*必须使用组合,接口模块done信号延后一个时钟,req等数据需提前
    根据当前状态控制i2c接口发送数据*/
        always  @(*)begin
            case (state_c)
                INIT : 
                    case(cnt_byte)
                        0           :TX(1'b1,{`CMD_START | `CMD_WRITE},{`I2C_ADR,1'b0});//发起始位、地址
                        1           :TX(1'b1, `CMD_WRITE ,`CMD_INIT);  //发数据,结束位
                        2           :TX(1'b1,`CMD_WRITE ,8'b000_1000);  //发数据,结束位
                        3           :TX(1'b1,{`CMD_WRITE | `CMD_STOP} ,8'b0000_0000);  //发数据,结束位
                        default     :TX(1'b0,tx_cmd,tx_data);
                    endcase 
                CHECK_INIT:
                    case(cnt_byte)
                        0           :TX(1'b1,{`CMD_START | `CMD_WRITE},{`I2C_ADR,1'b0});//发起始位、写控制字
                        1           :TX(1'b1,`CMD_WRITE ,`CMD_CHECK);  //发数据
                        2           :TX(1'b1,{`CMD_START | `CMD_WRITE},{`I2C_ADR,1'b1});//发起始位、读控制字
                        3           :TX(1'b1,{`CMD_READ | `CMD_STOP},0);  //最后一个字节时 读数据、发停止位
                        default     :TX(1'b0,tx_cmd,tx_data);
                    endcase
                TRIGGER: 
                    case(cnt_byte)
                        0           :TX(1'b1,{`CMD_START | `CMD_WRITE},{`I2C_ADR,1'b0});//发起始位、写控制字
                        1           :TX(1'b1,`CMD_WRITE ,`CMD_TRIGGER);  //发数据
                        2           :TX(1'b1,`CMD_WRITE ,`DATA_0);  //发数据
                        3           :TX(1'b1,{`CMD_WRITE | `CMD_STOP},`DATA_1);  //最后一个字节时、发停止位
                        default     :TX(1'b0,tx_cmd,tx_data);
                    endcase    
                READ :            
                    case(cnt_byte)
                        0           :TX(1'b1,{`CMD_START | `CMD_WRITE},{`I2C_ADR,1'b1});//发起始位、写控制字
                        1           :TX(1'b1,`CMD_READ ,0);  //读数据
                        2           :TX(1'b1,`CMD_READ ,0);  //读数据
                        3           :TX(1'b1,`CMD_READ ,0);  //读数据
                        4           :TX(1'b1,`CMD_READ ,0);  //读数据
                        5           :TX(1'b1,`CMD_READ ,0);  //读数据
                        6           :TX(1'b1,{`CMD_READ | `CMD_STOP},0);
                        default     :TX(1'b0,tx_cmd,tx_data);
                    endcase
                default: TX(1'b0,0,0);
            endcase
        end
    //初始化完成标志
        always @(posedge clk or negedge rst_n) begin
            if(!rst_n) begin
                finish_init <= 0;
            end
            else if(state_c == CHECK_INIT && done && rd_data[3]) begin
                finish_init <= 1;
            end
        end
    //i2c 接口返回数据
        always @(posedge clk or negedge rst_n) begin
            if(!rst_n) begin
                read_data <= 0;
            end
            else if(state_c == READ && cnt_byte >0 && done) begin
                read_data <= {read_data[39:0],rd_data};
            end
        end
    //用task发送请求、命令、数据(地址+数据)
        task TX;   
            input                   req     ;
            input       [3:0]       command ;
            input       [7:0]       data    ;
            begin 
                tx_req  = req;
                tx_cmd  = command;
                tx_data = data;
            end 
        endtask   
    //输出
    
        assign req     = tx_req ; 
        assign cmd     = tx_cmd ; 
        assign data    = tx_data; 
        assign hum_data = read_data[39:20];
        assign temp_data = read_data[19:0];
        assign dout_vld = read_2_idle;
    
    
    
    endmodule //camera_config_ctrl
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267

    串口模块代码

    module uart_tx(input			wire						clk,
                   input			wire						rst_n,
                   input			wire						tx_enable, // 发送使能
                   input			wire		[ 07:0 ]		data_in, // 需要发送的数据
                   input			wire		[ 19:0 ]		tx_bps, // 发送的波特率
                   output			wire						data, // 发送的数据
                   output			wire						tx_done);
        
        localparam MAX_BIT = 10;
        
        reg			[ 09:0 ]			data_r			; // 数据寄存器
        reg			[ 12:0 ]			cnt_bps			; // 波特率计数器
        reg			[ 03:0 ]			cnt_bit			; // 数据位计数器
        
        wire		[ 12:0 ]			max_bps			; // 波特率对应频率
    
        wire							flag_clear_cnt_bps			; // 计数器归零
        wire							flag_add_cnt_bit			; // 计数器+1
        wire							flag_clear_cnt_bit			; 
        reg								flag_send_data			    ; //发送数据标志
        
        //输入数据寄存
        always @(posedge clk or negedge rst_n) begin
            if(!rst_n) begin
                data_r <= 10'b0;
            end
            else if(tx_enable) begin
                data_r <={1'b1, data_in, 1'b0};
            end
    
        end
        
        // 波特率计数器
        always @( posedge clk or negedge rst_n ) begin
            if ( !rst_n ) begin
                cnt_bps <= 0;
            end
            else if ( flag_send_data ) begin
                if ( flag_clear_cnt_bps ) begin
                    cnt_bps <= 0;
                end
                else begin
                    cnt_bps <= cnt_bps + 1;
                end
            end
            else begin
                cnt_bps <= 0;
            end
            
        end
    
        assign flag_clear_cnt_bps  = cnt_bps >= max_bps -1;
        assign max_bps             = 50_000_000 / tx_bps;
        
        // 数据位计数器
        always @( posedge clk or negedge rst_n ) begin
            if ( !rst_n ) begin
                cnt_bit <= 0;
            end
            else if ( flag_send_data ) begin
                if ( flag_clear_cnt_bit ) begin
                    cnt_bit <= 0;
                end
                else if ( flag_add_cnt_bit )begin
                    cnt_bit <= cnt_bit + 1;
                end
                else begin
                    cnt_bit <= cnt_bit;
                end
            end
            else begin
                cnt_bit <= 0;
            end
        end
    
        assign flag_add_cnt_bit   = flag_clear_cnt_bps;
        assign flag_clear_cnt_bit = cnt_bit >= MAX_BIT - 1 && flag_add_cnt_bit ;
    
    
        //发送数据标志
        always @(posedge clk or negedge rst_n) begin
            if(!rst_n) begin
                flag_send_data <= 0;
            end
            else if(tx_enable) begin
                flag_send_data <= 1;
            end
            else if(flag_clear_cnt_bit) begin
                flag_send_data <= 0;
            end
            else begin
                flag_send_data <= flag_send_data;
            end
        end
        //发送数据
        assign data = flag_send_data ? data_r[cnt_bit]:1;
        assign tx_done = ~flag_send_data  ;
        //发送状态
        // always @(*) begin
        //     if(!rst_n) begin
        //         tx_done = 1;
        //     end
        //     else if(flag_clear_cnt_bit) begin
        //         tx_done = 1;
        //     end
        //     else if(flag_send_data) begin
        //         tx_done = 0;
        //     end
        //     else begin
        //         tx_done = tx_done;
        //     end
        // end
    
     
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116

    四、数据处理模块

    根据手册信息可以得到数据处理的公式
    在这里插入图片描述
    但是按照公式进行转换数据并不正确,考虑到数据涉及到小数运算,先把测量到的数据进行左移8位转为整数,再套入公式可以得到较为合理的数据。最终的公式为:
    在这里插入图片描述
    考虑需要一位小数,则先把原数据进行扩大10倍后再进行上述运算。最终处理后得到的数据我们只取后三位代表十位、个位和小数位。

    串口

    串口数据发送必须以字节为单位,则需要把处理完得到数据一位一位的取出来再转为ascii码进行发送。按如下格式进行数据发送。为了保证发送正确,这里添加一个fifo进行缓存需要发送的数据,让串口模块每次都去取fifo里面的数据进行发送。
    在这里插入图片描述

    代码

    module data_drive (
        input			wire						clk,
        input			wire						rst_n,
        input			wire						din_vld,
        input			wire		[ 19:0 ]		temp_data,
        input			wire		[ 19:0 ]		hum_data,
        output			wire						tx_data
    );
    
    
    reg								send_flag			;
    reg			[ 19:0 ]			temp_data_r			;
    reg			[ 19:0 ]			hum_data_r			;
    reg			[ 7:0 ]			    dout_r			;
    wire		[ 15:0 ]			tmep			;
    wire		[ 15:0 ]			hum			;
    reg			[ 5:0 ]			    cnt			;
    wire							add_cnt			;
    wire							end_cnt			;
    wire							rdreq			;
    wire							wrreq			;
    wire							empty			;
    wire							full			;
    wire							tx_done			;
    wire		[ 7:0 ]				q		;
    assign rdreq = tx_done && ~empty;
    assign wrreq = ~full && send_flag && cnt > 0;
    
    
    //串口模块
    uart_tx u_uart_tx(
        .clk       ( clk       ),
        .rst_n     ( rst_n     ),
        .tx_enable ( rdreq      ),
        .data_in   ( q   ),
        .tx_bps    ( 115200    ),
        .data      ( tx_data      ),
        .tx_done   ( tx_done   )
    );
    
    //用于缓存通过串口发送的数据
    fifo	tx_fifo_inst (
    	.aclr ( ~rst_n ),
    	.clock ( clk ),
    	.data ( dout_r ),
    	.rdreq ( rdreq ),
    	.wrreq ( wrreq ),
    	.empty ( empty ),
    	.full ( full ),
    	.q ( q ),
    	.usedw ( usedw_sig )
    	);
    
    //温湿度数据寄存
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            temp_data_r <= 0;
            hum_data_r <= 0;
        end
        else if(din_vld) begin
            temp_data_r <= (((temp_data*2000)>>12) - (500)); //扩大10倍
            hum_data_r <= ((hum_data *1000) >> 12);
        end
    end
    
    //数据处理标志
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            send_flag <= 0;
        end
        else if(din_vld) begin
            send_flag <= 1;
        end
        else if(end_cnt) begin
            send_flag <= 0;
        end
    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
    end
    assign add_cnt = send_flag;
    assign end_cnt = add_cnt && cnt == 22;
    // CE C2 B6 C8 CA AA B6 C8
    //根据计数器发送不同数据
    always @(posedge clk or negedge rst_n) begin
        case (cnt)
            1 : dout_r <= 8'hce; // 温度
            2 : dout_r <= 8'hc2;
            3 : dout_r <= 8'hb6;
            4 : dout_r <= 8'hc8;
            5 : dout_r <= 8'h3a; // :
            6 : dout_r <= (temp_data_r / 100 % 10 )+48;//十位
            7 : dout_r <= (temp_data_r / 10 % 10  )+48;//个位
            8 : dout_r <= 8'h2e;//.
            9 : dout_r <= (temp_data_r % 10  )+48;
            10 : dout_r <= 8'ha1; //℃
            11 : dout_r <= 8'he6;
            12: dout_r <= 9;     //tab
            13: dout_r <= 8'hca; //湿度
            14: dout_r <= 8'haa;
            15: dout_r <= 8'hb6;
            16: dout_r <= 8'hc8;
            17: dout_r <= 8'h3a;
            18: dout_r <= (hum_data_r / 100 % 10 )+48;
            19: dout_r <= (hum_data_r / 10 % 10 )+48;
            20: dout_r <= 8'h2e;
            21: dout_r <= (hum_data_r % 10  )+48;
            22: dout_r <= 8'h25;//%
            default: dout_r <= 0;
        endcase
    end
    endmodule //data_drive
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124

    五、仿真

    testbench设计

    通过模拟传感器回数据进行测试代码的逻辑正确与否
    代码如下:

    `timescale 1ns/1ps
    
    module tb ();
    
    reg clk;
    reg rst_n;
    
    wire								sda			;
    reg								sda_i			;
    aht10_top u_aht10_top(
        .clk        ( clk        ),
        .rst_n      ( rst_n      ),
        .sda        ( sda        ),
        .scl        ( scl        ),
        .uart_txd   ( uart_txd   )
    );
    assign sda = u_aht10_top.i2c_sda_oe?1'bz:sda_i;
    defparam u_aht10_top.u_i2c_master.DELAY_40MS = 400;
    defparam u_aht10_top.u_i2c_master.DELAY_80MS = 800;
    defparam u_aht10_top.u_i2c_master.DELAY_500MS = 5000;
    localparam CLK_PERIOD = 20;
    always #(CLK_PERIOD/2) clk=~clk;
    
    reg			[ 10:0 ]			i			;
    reg			[ 10:0 ]			j			;
    initial begin
        rst_n<=1'b0;
        clk<=1'b0;
        # CLK_PERIOD;
        rst_n<=1;
    
    
        @(posedge u_aht10_top.u_i2c_master.init_2_check); //初始化
        for (i = 0; i<3 ; i = i+1 ) begin
            @(posedge u_aht10_top.u_i2c_master.add_cnt_byte);
        end
    
        for (i = 0; i<9 ; i = i+1 ) begin //模拟初始化完成
            @(posedge u_aht10_top.u_i2c_intf.add_cnt_bit)
            if(i == 3)
                sda_i = 1;
            else if(i == 8)
                sda_i = 0;
        end
    
        repeat(5) begin
            @(posedge u_aht10_top.u_i2c_master.wait_2_read);//等待控制模块到达读取状态
            @(posedge u_aht10_top.u_i2c_master.add_cnt_byte);
            for (i = 0; i<6 ; i = i+1 ) begin//发送6个数据
                @(posedge u_aht10_top.u_i2c_master.add_cnt_byte);
                for (j = 0; j<9 ; j = j+1 ) begin//模拟从机回数据
                    @(posedge u_aht10_top.u_i2c_intf.end_cnt_scl)
                    if(j == 8)
                        sda_i = 0;
                    else
                        sda_i = {$random};
                end
            end
        end
    
    
    
        $stop;
    end
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    仿真波形

    由于中文和部分特殊符号需要两位ascii码,故出现乱码,但是可以看到温湿度可以正常显示,通过随机数进行模拟,仅供参考。
    在这里插入图片描述

    六、效果

    在这里插入图片描述

    七、源码

    https://github.com/TangtangSix/AHT10

  • 相关阅读:
    图像分类(五) 全面解读复现ResNet
    php利用微信公众号发送模板消息
    腾讯云大数据ES Serverless
    如何使用git-credentials来管理git账号
    【广州华锐互动】影视制作VR在线学习:身临其境,提高学习效率
    【附源码】Python计算机毕业设计网络教育平台设计
    Android 14适配记录
    Spring Data JPA 中的分页和排序
    Android sdk 生成api doc文档
    Java泛型漫谈
  • 原文地址:https://blog.csdn.net/qq_47281915/article/details/126147081