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

描述

概念

传感器输出经过标定的数字信号输出,通过标准的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通信通过设备地址与从机进行通信,

温湿度传感器

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

温湿度传感器

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

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

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

读取温湿度数据构成

温湿度传感器

相对湿度和温度转换公式

将接收到的湿度值转换成%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).

设计框架

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

温湿度传感器

I2C模块

温湿度传感器

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

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

 

include"param.v"module i2c_master(    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 ornegedge rst_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"module aht_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,                  WAIT =       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@(posedge sys_clk ornegedge rst_n)beginif(!rst_n) begin driver_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码显示到数码管上;

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分