全球最实用的IT互联网信息网站!

AI人工智能P2P分享&下载搜索网页发布信息网站地图

当前位置:诺佳网 > 电子/半导体 > 可编程逻辑 >

基于FPGA的AHT10温湿度传感器驱动设计

时间:2025-06-27 10:12

人气:

作者:admin

标签:

导读:传感器输出经过标定的数字信号输出,通过标准的I2C接口传输数据。...

概念

输出经过标定的输出,通过标准的传输数据;

相对湿度的分辨率在0.024%RH,工作范围为0~100%RH;

温度值的分辨率在0.01℃,工作范围为-40~85℃;

AHT10的供电范围为1.8~3.6V,推荐使用3.3V供电;

接口包含了完全静态逻辑,因而不存在最小串行SCL频率;

IIC协议,同步半双工协议

起始位:主机在时钟高电平期间拉低总线;

数据位:主机在时钟低电平期间发送数据,主机在高电平期间保持不变;从机在时钟高电平期间采样数据;

应答为:主机收到数据后发送应答,从机才会发送下一字节数据;

停止位:主机在时钟高电平期间释放总线,主机在低电平期间保持不变;

可使用的I2C通信模式有经典型和高速模式,经典模式最小时钟周期是250us,高速模式最小时钟周期是100us;

AHT10配置与通信

I2C通信通过设备地址与从机进行通信,

d6b9bb7e-500c-11f0-b715-92fbcf53809c.jpg

首先上电启动传感器,启动后需要先等待40ms(设备才开始正常工作),然后发送8‘h71 来获取状态字节,状态说明如下:

d6c8cfd8-500c-11f0-b715-92fbcf53809c.jpg

获取到校准使能位后,查看其是否已校准,若已校准则跳过当前步骤;若未校准则发送8‘hE1,进行初始化,然后发送8’h08,8‘h00;

接着开始触发测量,测量先发送8’hAC,然后发送8‘h33,8'h00;

测量命令发送完成后,需要等待80ms,用于温湿度的测量;之后再发送命令8‘h71,以读取状态寄存器是否处于空闲状态(bit7 => idle);若是空闲状态,可以直接读取之后六个字节的温湿度数值;

读取温湿度数据构成

d6d63498-500c-11f0-b715-92fbcf53809c.png

相对湿度和温度转换公式

将接收到的湿度值转换成%RH的格式:

R H [ % ] = ( S R H / 2 20 ) RH [\%] = (SRH/2^{20}) RH[%]=(SRH/220)

温度转换成℃表示:

T ( ℃ ) = ( S T / 2 20 ) . T(℃) = (ST/2^{20}). T(℃)=(ST/220).

设计框架

整个模块的设计,首先是通过,发送命令打开AHT10驱动控制模块、显示模块以及串口模块;设备驱动用控制模块实现,使用I2C与AHT10进行通信,然后将读取的温湿度值先进行数值转换并取整,并转换成ASCII码方便查看温湿度值;然后将数据缓存到FIFO中,当缓存了一组完整的温度值数据后进行输出,发送给上位机,或是直接通过管显示;

d6e2c4ba-500c-11f0-b715-92fbcf53809c.jpg

I2C模块

d6ed0f4c-500c-11f0-b715-92fbcf53809c.jpg

I2C接口模块状态机如上图所示,从空闲状态可以先发送起始位再读写一个字节数据,或直接读写一个字节数据,然后是收发应答位;最后发送停止位,就完成了一组数据的读写;

对于I2C通信,数据的传输速率选择的是50M/250 Bps;

include"pa.v"modulei2c_master(  input       clk     ,  input       t_n    ,  input       req     ,  input   [3:0]  cmd     ,  input   [7:0]  din     ,  output   [7:0]  dout    ,  output       done    ,  output       slave_k  ,  output       i2c_scl   ,  input       i2c_a_i  ,  output       i2c_sda_o  ,  output       i2c_sda_oe     );//状态机参数定义localparam IDLE =7'b000_0001,       START =7'b000_0010,       WRI =7'b000_0100,       RACK =7'b000_1000,       RE =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@(posedgeclkornegedgerst_n)beginif(rst_n==0)begin      state_c <= IDLE ;        endelsebegin             state_c <= state_n;       endendalways @(*) begincase(state_c)               IDLE :beginif(idle2start)                     state_n = START ;                elseif(idle2write)                     state_n = WRITE ;                elseif(idle2read)                     state_n = READ ;                else                      state_n = state_c ;            end             START :beginif(start2write)                     state_n = WRITE ;                elseif(start2read)                     state_n = READ ;                else                      state_n = state_c ;            end             WRITE :beginif(write2rack)                     state_n = RACK ;                else                      state_n = state_c ;            end             RACK :beginif(rack2stop)                     state_n = STOP ;                elseif(rack2idle)                     state_n = IDLE ;                else                      state_n = state_c ;            end             READ :beginif(read2sack)                     state_n = SACK ;                else                      state_n = state_c ;            end             SACK :beginif(sack2stop)                     state_n = STOP ;                elseif(sack2idle)                     state_n = IDLE ;                else                      state_n = state_c ;            end             STOP :beginif(stop2idle)                     state_n = IDLE ;                else                      state_n = state_c ;            enddefault : state_n = IDLE ;        endcaseendassign 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 ornegedge rst_n) beginif (rst_n==0) begin             cnt_scl <= 0;          endelseif(add_cnt_scl) beginif(end_cnt_scl)                 cnt_scl <= 0;              else                 cnt_scl <= cnt_scl+1 ;       endendassign add_cnt_scl = (state_c != IDLE);    assign end_cnt_scl = add_cnt_scl  && cnt_scl == (`SCL_PERIOD)-1 ;    always @(posedge clk ornegedge rst_n) beginif (rst_n==0) begin             cnt_bit <= 0;          endelseif(add_cnt_bit) beginif(end_cnt_bit)                 cnt_bit <= 0;              else                 cnt_bit <= cnt_bit+1 ;       endendassign add_cnt_bit = (end_cnt_scl);    assign end_cnt_bit = add_cnt_bit  && cnt_bit == (bit_num)-1 ;    always  @(*)beginif(state_c == WRITE | state_c == READ) begin             bit_num = 8;        endelsebegin              bit_num = 1;        endend//commandalways  @(posedge clk ornegedge rst_n)beginif(~rst_n)begin             command <= 0;        endelseif(req)begin             command <= cmd;        endend//tx_dataalways  @(posedge clk ornegedge rst_n)beginif(~rst_n)begin             tx_data <= 0;        endelseif(req)begin             tx_data <= din;        endend//sclalways  @(posedge clk ornegedge rst_n)beginif(~rst_n)begin             scl <= 1'b1;        endelseif(idle2start | idle2write | idle2read)begin//开始发送时,拉低             scl <= 1'b0;        endelseif(add_cnt_scl && cnt_scl == `SCL_HALF-1)begin              scl <= 1'b1;        endelseif(end_cnt_scl && ~stop2idle)begin              scl <= 1'b0;        endend//sda_outalways  @(posedge clk ornegedge rst_n)beginif(~rst_n)begin             sda_out <= 1'b1;        endelseif(state_c == START)begin//发起始位if(cnt_scl == `LOW_HLAF)begin//时钟低电平时拉高sda总线                 sda_out <= 1'b1;            endelseif(cnt_scl == `HIGH_HALF)begin//时钟高电平时拉低sda总线                  sda_out <= 1'b0;                //保证从机能检测到起始位endendelseif(state_c == WRITE && cnt_scl == `LOW_HLAF)begin//scl低电平时发送数据   并串转换             sda_out <= tx_data[7-cnt_bit];               endelseif(state_c == SACK && cnt_scl == `LOW_HLAF)begin//发应答位             sda_out <= (command&`CMD_STOP)?1'b1:1'b0;        endelseif(state_c == STOP)begin//发停止位if(cnt_scl == `LOW_HLAF)begin//时钟低电平时拉低sda总线                 sda_out <= 1'b0;            endelseif(cnt_scl == `HIGH_HALF)begin//时钟高电平时拉高sda总线                  sda_out <= 1'b1;                //保证从机能检测到停止位endendend//sda_out_en  总线输出数据使能always  @(posedge clk ornegedge rst_n)beginif(~rst_n)begin             sda_out_en <= 1'b0;        endelseif(idle2start | idle2write | read2sack | rack2stop)begin             sda_out_en <= 1'b1;        endelseif(idle2read | start2read | write2rack | stop2idle)begin              sda_out_en <= 1'b0;        endend//rx_data       接收读入的数据always  @(posedge clk ornegedge rst_n)beginif(~rst_n)begin             rx_data <= 0;        endelseif(state_c == READ && cnt_scl == `HIGH_HALF)begin             rx_data[7-cnt_bit] <= i2c_sda_i;    //串并转换endend//rx_ackalways  @(posedge clk ornegedge rst_n)beginif(~rst_n)begin             rx_ack <= 1'b1;        endelseif(state_c == RACK && cnt_scl == `HIGH_HALF)begin             rx_ack <= i2c_sda_i;        endend//输出信号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

`include"param.v"moduleaht_ctrl (  input       sys_clk  ,  input       rst_n  ,inputdriver_en,  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,         INIT =   7'b000_0010,         CHECK_INIT =7'b000_0100,         IDLE =   7'b000_1000,         TRIGGER =  7'b001_0000,         WT =   7'b010_0000,         READ =   7'b100_0000;   parameterDELAY_40MS =200_0000 ,         DELAY_80MS =400_0000 ,         DELAY_500MS =2500_0000;  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;  wireadd_cnt;  wireend_cnt;  reg[24:0]delay;  regfinish_init;  wirestart2init;  wireinit2check;  wirecheck2idle;  wirecheck2init;  wireidle2trigger;  wiretrigger2wait;  wirewait2read;  wireread2idle;regdriver_en_r1;regdriver_en_r2;wirene_driver;// ne_driveralways@(posedgesys_clkornegedgerst_n)beginif(!rst_n)begindriver_en_r1 <= 0 ;  driver_en_r2  <= 0 ;  endelsebegin driver_en_r1  <= driver_en ; driver_en_r2  <= driver_en_r1 ;endendassign ne_driver = (~driver_en_r1 && driver_en_r2) ;//statealways @(posedge sys_clk ornegedge rst_n) beginif (rst_n==0) begin             state_c <= START ;        endelsebegin             state_c <= state_n;       endend//状态转移always @(*) begincase(state_c)               START :beginif(ne_driver) begin state_n = state_c ;  endelseif(start2init) begin                     state_n = INIT ;endelsebegin                      state_n = state_c ;endend             INIT :beginif(ne_driver) begin state_n = START ;endelseif(init2check)begin                     state_n = IDLE ;endelse                      state_n = state_c ;            end             CHECK_INIT :beginif(ne_driver) begin state_n = START ;endelseif(check2idle)begin                     state_n = IDLE ;endelseif(check2init) begin                     state_n = INIT;                endelse                      state_n = state_c ;            end             IDLE :beginif(ne_driver)begin state_n = START ;endelseif(idle2trigger)begin                     state_n = TRIGGER ;endelse                      state_n = state_c ;            end             TRIGGER :beginif(ne_driver)begin state_n = START ;endelseif(trigger2wait)begin                     state_n = WAIT ;endelse                      state_n = state_c ;            end             WAIT :beginif(ne_driver)begin state_n = START ;endelseif(wait2read)                     state_n = READ ;                else                      state_n = state_c ;            end             READ :beginif(ne_driver)begin state_n = START ;endelseif(read2idle)                     state_n = IDLE ;                else                      state_n = state_c ;            enddefault : state_n = START ;        endcaseendassign start2init = state_c == START && (end_cnt);    assign init2check = state_c == INIT && (end_cnt_byte);    assign check2idle = state_c == CHECK_INIT && (finish_init) && end_cnt_byte;    assign check2init = state_c == CHECK_INIT && (~finish_init)&& end_cnt_byte;    assign idle2trigger = state_c == IDLE && (end_cnt);    assign trigger2wait = state_c == TRIGGER && (end_cnt_byte);    assign wait2read = state_c == WAIT && (end_cnt);    assign read2idle = state_c == READ && (end_cnt_byte);     // delayalways @(posedge sys_clk ornegedge rst_n) beginif(!rst_n) begin             delay <= DELAY_40MS;        endelseif(state_c == START) begin             delay <= DELAY_40MS;        endelseif(state_c == WAIT) begin             delay <= DELAY_80MS;        endelseif(state_c == IDLE) begin             delay <= DELAY_500MS;        endend// cntalways @( posedge sys_clk ornegedge rst_n ) beginif ( !rst_n ) begin             cnt <= 0;        endelseif ( add_cnt ) beginif ( end_cnt ) begin                 cnt <= 0;            endelsebegin                 cnt <= cnt + 1;            endendelsebegin             cnt <= 0;        endendassign add_cnt = (state_c == START || state_c == WAIT || state_c == IDLE) && driver_en_r2;    assign end_cnt = cnt == delay - 1 && add_cnt || ne_driver ;// cnt_bytealways @(posedge sys_clk ornegedge rst_n) beginif(!rst_n)begin             cnt_byte <= 0;        endelseif(add_cnt_byte) beginif(end_cnt_byte) begin                 cnt_byte <= 0;            endelse                 cnt_byte <= cnt_byte + 1;        endendassign 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) || ne_driver ;//always  @(*)begincase (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);                endcasedefault: TX(1'b0,0,0);        endcaseend// finish_initalways @(posedge sys_clk ornegedge rst_n) beginif(!rst_n) begin             finish_init <= 0;        endelseif(state_c == CHECK_INIT && done && rd_data[3]) begin             finish_init <= 1;        endend// read_dataalways @(posedge sys_clk ornegedge rst_n) beginif(!rst_n) begin             read_data <= 0;        endelseif(state_c == READ && cnt_byte >0&& done)begin      read_data <= {read_data[39:0],rd_data};        endend//task TX;            input                   req     ;        input       [3:0]       command ;        input       [7:0]       data    ;        begin              tx_req  = req;             tx_cmd  = command;             tx_data = data;        endendtask//outassign 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 = read2idle;endmodule

其它模块

然后将温湿度数值按照上述格式转换:

temp_data_r<= (((temp_data*2000)>>12) - (500)); hum_data_r<= ((hum_data *1000) >>12);

再将数据转换成ASCII码:

always@(posedge sys_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;          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;    endcaseend

最后将数值通过FIFO缓存完整的22字节数据后输出即可;

同时还可以将数据转换成bcd码显示到数码管上;

温馨提示:以上内容整理于网络,仅供参考,如果对您有帮助,留下您的阅读感言吧!
相关阅读
本类排行
相关标签
本类推荐

CPU | 内存 | 硬盘 | 显卡 | 显示器 | 主板 | 电源 | 键鼠 | 网站地图

Copyright © 2025-2035 诺佳网 版权所有 备案号:赣ICP备2025066733号
本站资料均来源互联网收集整理,作品版权归作者所有,如果侵犯了您的版权,请跟我们联系。

关注微信