【FPGA】基于FPGA实现AHT10温湿度传感器数据采集

目录

  • 一、需求分析
  • 二、AHT10简介
    • (一)AHT10特性
    • (二)AHT10基本指令及测量步骤
    • (三)数据转换
  • 三、系统架构设计
  • 四、模块划分及信号说明
    • (一)模块划分
    • (二)端口信号说明
  • 五、状态转移描述
  • 六、代码实现
  • 七、仿真测试
  • 八、板级验证

写在前面:
相关参考文章:【FPGA】FPGA实现IIC协议读写EEPROM
在本项目中所使用的开发板型号:Cyclone IV E (EP4CE6F17C8),温湿度传感器型号:AHT10。

一、需求分析

  1. 使用C4开发板实现控制AHT10温湿度传感器进行数据采集。
  2. 温度值以十进制形式的摄氏温度打印到终端,保留一位小数,显示形式例如xx.x℃。
  3. 湿度值以百分数形式打印到终端,保留一位小数,显示形式例如:xx.x%。
  4. 使用按键设置温湿度报警值,当采集的温湿度值超过预设的报警值时,触发蜂鸣器报警。

二、AHT10简介

(一)AHT10特性

AHT10是一款标准I2C接口的温湿度传感器。供电范围为1.8-3.6V,推荐电压为3.3V。电源( VDD)和接地(GND)之间须连接一个 10uF的去耦电容,器件引脚接口图如下:

在这里插入图片描述
SDA引脚用于传感器的数据输入和输出。当向传感器发送命令时,SDA在串行时钟( SCL)的上升沿有效,且当SCL为高电平时,SDA 必须保持稳定。在 SCL下降沿之后,SDA值可被改变。

AHT10读写遵循I2C协议,可参考IIC协议工程,时序图如下:
在这里插入图片描述
在这里插入图片描述
本工程中使用时钟频率为200KHz。

(二)AHT10基本指令及测量步骤

1、AHT10基本指令集:
在这里插入图片描述

2、AHT10测量步骤

(1)上电后等待40ms ,读取温湿度值之前,首先看状态字的校准使能位Bit[3]是否为1(通过发送0x71可以获取一个字节的状态字),如果不为1,要发送0xEi命令(初始化),此命令参数有两个字节,第一个字节为0x08,第二个字节为0x00。状态位说明如下:
在这里插入图片描述
(2)直接发送0xAC命令(触发测量),此命令参数有两个字节,第一个字节为0x33,第二个字节为0x00。
在这里插入图片描述
(3)等待80ms测量数据完成,发送0x71读取6字节温湿度数据。
在这里插入图片描述

(三)数据转换

在这里插入图片描述

按照此公式进行转换数据不正确,所以在数据转换代码中做了修改。

三、系统架构设计

本工程系统框图如下:
在这里插入图片描述

四、模块划分及信号说明

(一)模块划分

本工程将系统功能划分为8个模块,各模块功能描述如下:

  1. top:顶层设计模块。
  2. aht10_ctrl:控制驱动AHT10命令的发送、驱动i2c_interface接口模块与AHT10传感器进行数据交互。
  3. i2c_interface:AHT10使用标准的I2C接口协议,该模块实现aht10_ctrl控制模块通过I2C协议时序读取温湿度数据。
  4. data_process:温湿度数据处理模块,将aht10_ctrl控制模块读回的温湿度数据根据转换公式进行转换,并将转换的数据再次转换成ASCII码通过串口进行发送。
  5. uart_tx:串口数据发送模块,将data_process模块转换后的ASCII格式数据通过串口发送给上位机,通过串口调试助手打印温湿度数据信息。
  6. key_filter:按键消抖模块,将按键输入的进行进行消抖处理。
  7. set_limit:温湿度报警值设置模块,通过按键设置温湿度报警值。
  8. seg_driver:数码管显示模块,显示温湿度设置模式及温湿度设置的报警值。

(二)端口信号说明

  1. top模块
    在这里插入图片描述
  2. aht10_ctrl模块
    在这里插入图片描述
  3. i2c_interface模块
    在这里插入图片描述
  4. data_process模块
    在这里插入图片描述
  5. uart_tx模块
    在这里插入图片描述
    6 key_filter模块
    在这里插入图片描述
  6. set_limit模块
    在这里插入图片描述
  7. seg_driver模块
    在这里插入图片描述

五、状态转移描述

  1. aht10_ctrl控制模块状态转移图
    在这里插入图片描述
    状态说明:
    WAIT:上电等待40ms,等待结束后进入INIT状态。
    INIT:初始化,发送初始化指令,进入WAIT_INIT状态。
    WAIT_INIT:等待初始化,检查校准使能位是否为1,不为1则继续进行初始化,初始化完成后进入IDLE状态。
    IDLE:空闲状态,等待读请求。
    RD_REQ:发送读取温湿度数据指令,发送完成后进入WAIT_MES状态。
    WAIT_MES:等待80ms测量温湿度数据完成,进入READ状态。
    READ:读取温度数据,读取完成后进入IDLE状态,等待下一次读请求。
  2. i2c_interface接口模块状态转移图
    在这里插入图片描述
    状态说明:
    IDLE:空闲状态,等待读写请求。
    START:发送起始位,当控制命令cmd[5:0]中含有起始命令时,进行该状态发送起始位。
    WR_DATA:写数据状态,在写数据状态期间,将wr_din[7:0]按照IIC协议时序写入AHT10。
    RD_DATA:读数据状态,在读数据状态期间,按照IIC协议时序从AHT10中读取数据。
    REC_ACK:接收从机发送的ACK应答信号。
    SEND_ACK:发送ACK或者NACK信号。
    STOP:发送停止位,当控制命令cmd[5:0]中含有停止命令时,进行该状态发送停止位。

六、代码实现

1. aht10读写顶层设计模块

//  **************************************************************
//  Author: Zhang JunYi
//  Create Date: 2022.11.15                        
//  Design Name: AHT10    
//  Module Name: top         
//  Target Device: Cyclone IV E (EP4CE6F17C8), 温湿度传感器(AHT10)             
//  Tool versions: Quartus Prime 18.1             
//  Description: aht10读写顶层设计模块
//  **************************************************************module top (input           clk             ,input           rst_n           ,//  keyinput   [2:0]   key_in          ,//  AHT10inout           sda             ,output          scl             ,//  UARToutput          uart_txd        ,//  beepoutput          beep            ,//  数码管output  [7:0]   seg_dig         ,output  [5:0]   seg_sel         
);wire            sda_in          ;wire            sda_out         ;wire            sda_out_en      ;assign sda = sda_out_en ? sda_out : 1'bz ;assign sda_in = sda ;wire            req             ;wire    [7:0]   wrdata          ;wire    [3:0]   cmd             ;wire    [7:0]   rddata          ;wire            rddata_vld      ;wire            done            ;wire    [19:0]  hum_data        ; wire    [19:0]  tem_data        ; wire            data_vld        ; wire            ready           ;  wire    [7:0]   tx_data         ;wire            tx_data_vld     ;wire            tem_add         ;wire            hum_add         ;wire            set             ;wire            modle           ;wire    [7:0]   tem_lim         ;wire    [7:0]   hum_lim         ;key_filter u_key_filter1 (/*input           */.clk         (clk           ),/*input           */.rst_n       (rst_n         ),/*input           */.key_in      (key_in[0]     ),/*output    reg   */.key_out     (set           ));key_filter u_key_filter2 (/*input           */.clk         (clk           ),/*input           */.rst_n       (rst_n         ),/*input           */.key_in      (key_in[1]     ),/*output    reg   */.key_out     (tem_add       ));key_filter u_key_filter3 (/*input           */.clk         (clk           ),/*input           */.rst_n       (rst_n         ),/*input           */.key_in      (key_in[2]     ),/*output    reg   */.key_out     (hum_add       ));//  模块例化aht10_ctrl u_aht10_ctrl (/*input               */.clk                 (clk       ),/*input               */.rst_n               (rst_n     ),/*//  i2c_interface*//*output              */.req                 (req       ),       //  读写请求/*output  [7:0]       */.wr_dout             (wrdata    ),       //  发送指令数据/*output  [3:0]       */.cmd                 (cmd       ),       //  发送读写控制命令/*input   [7:0]       */.rdin                (rddata    ),       //  读回的数据/*input               */.rdin_vld            (rddata_vld),/*input               */.rw_done             (done      ),        //  读写一字节数据完成标志//  data_process/*output  [19:0]      */.hum_data            (hum_data  ),       //  湿度数据/*output  [19:0]      */.tem_data            (tem_data  ),       //  温度数据/*output              */.data_vld            (data_vld  )        //  温湿度数据有效标志);i2c_interface u_i2c_interface (/*input               */.clk                 (clk           ),/*input               */.rst_n               (rst_n         ),/*//  eeprom_ctrl*//*input               */.req                 (req           ),       //  读写请求/*input   [7:0]       */.wr_din              (wrdata        ),       //  需要发送的一字节数据/*input   [3:0]       */.cmd                 (cmd           ),       //  控制命令组合/*output  [7:0]       */.rdout               (rddata        ),       //  读取的数据/*output              */.rdout_vld           (rddata_vld    ),       //  读取数据有效标志/*output              */.rw_done             (done          ),       //  读写一字节完成标志/*//  EEPROM*//*input               */.sda_in              (sda_in        ),/*output              */.sda_out             (sda_out       ),/*output              */.sda_out_en          (sda_out_en    ),.scl                 (scl           ));data_process u_data_process (/*input           */.clk             (clk           ),/*input           */.rst_n           (rst_n         ),/*//  aht10_ctrl*//*input           */.din_vld         (data_vld      ),/*input   [19:0]  */.tem_data        (tem_data      ),/*input   [19:0]  */.hum_data        (hum_data      ),/*//  set_limit*//*input   [7:0]   */.tem_lim         (tem_lim       ),/*input   [7:0]   */.hum_lim         (hum_lim       ),/*//  uart_tx*//*input           */.ready           (ready         ),/*output  [7:0]   */.tx_data         (tx_data       ),/*output          */.tx_data_vld     (tx_data_vld   ),/*//  beep*//*output          */.beep            (beep          )    );set_limit u_set_limit (/*input           */.clk             (clk       ),/*input           */.rst_n           (rst_n     ),/*//  key_filter*//*input           */.tem_add         (tem_add   ),       //  设置温度报警值标志,每按一次+5摄氏度/*input           */.hum_add         (hum_add   ),       //  设置湿度报警值标志,每按一次+5%/*input           */.set             (set       ),       //  设置模式,每按一次在温度和湿度设置之间切换/*//  seg_driver*//*output          */.modle           (modle     ),       //  当前设置模式  0:设置温度   1:设置湿度/*output  [7:0]   */.tem_lim         (tem_lim   ),       //  温度报警值/*output  [7:0]   */.hum_lim         (hum_lim   )        //  湿度报警值);seg_driver u_seg_driver(/*input                   */.clk          (clk      ),/*input                   */.rst_n        (rst_n    ),/*// set_limit*//*input   [7:0]           */.tem_lim      (tem_lim  ),/*input   [7:0]           */.hum_lim      (hum_lim  ),/*input                   */.modle        (modle    ),/*output  reg   [7:0]     */.seg_dig      (seg_dig  ),      //  段选信号/*output  reg   [5:0]     */.seg_sel      (seg_sel  )      //  片选信号);uart_tx u_uart_tx (/*input               */.clk             (clk           ),/*input               */.rst_n           (rst_n         ),/*//  control*/ /*input   [7:0]       */.tx_din          (tx_data       ),/*input               */.tx_din_vld      (tx_data_vld   ),/*output              */.ready           (ready         ),       //  给control模块的握手信号,表示可以接收数据进行发送 /*//  上位机*//*output              */.uart_tx         (uart_txd      ));endmodule

2. I2C接口驱动模块

//  **************************************************************
//  Author: Zhang JunYi
//  Create Date: 2022.11.15                        
//  Design Name: AHT10    
//  Module Name: i2c_interface         
//  Target Device: Cyclone IV E (EP4CE6F17C8), 温湿度传感器(AHT10)          
//  Tool versions: Quartus Prime 18.1             
//  Description: I2C接口驱动模块
//  **************************************************************
`include "param.v"module i2c_interface (input               clk                 ,input               rst_n               ,//  eeprom_ctrlinput               req                 ,       //  读写请求input   [7:0]       wr_din              ,       //  需要发送的一字节数据input   [3:0]       cmd                 ,       //  控制命令组合output  [7:0]       rdout               ,       //  读取的数据output              rdout_vld           ,       //  读取数据有效标志output              rw_done             ,       //  读写一字节完成标志//  EEPROMinput               sda_in              ,output              sda_out             ,output              sda_out_en          ,output              scl
);//  参数定义localparam  IDLE    =   7'b000_0001 ,START   =   7'b000_0010 ,WR_DATA =   7'b000_0100 ,RD_DATA =   7'b000_1000 ,REC_ACK =   7'b001_0000 ,SEND_ACK=   7'b010_0000 ,STOP    =   7'b100_0000 ;//  信号定义reg     [6:0]       state_c         ;reg     [6:0]       state_n         ;reg     [7:0]       cnt_scl         ;       //  scl周期计数器wire                add_cnt_scl     ;wire                end_cnt_scl     ;reg     [3:0]       bit_num         ;       //  bit数reg     [3:0]       cnt_bit         ;       //  bit计数器wire                add_cnt_bit     ;wire                end_cnt_bit     ;reg     [7:0]       dout_data       ;       //  发送数据寄存reg     [3:0]       command         ;       //  控制命令寄存reg                 scl_dout         ;       //  SDA寄存     reg                 sda_dout        ;reg                 sda_dout_en     ;reg     [7:0]       rx_data         ;       //  接收读回的数据reg                 ack_flag        ;       //  ack响应标志//  状态转移条件wire                idle2start      ;wire                idle2wrdata     ;wire                idle2rddata     ;wire                start2wrdata    ;wire                start2rddata    ;wire                wrdata2recack   ;wire                rddata2sendack  ;wire                recack2idle     ;wire                recack2stop     ;wire                sendack2idle    ;wire                sendack2stop    ;wire                stop2idle       ;//  状态机always @(posedge clk or negedge rst_n)beginif(!rst_n)beginstate_c <= IDLE ;endelse beginstate_c <= state_n ;endendalways @(*)begincase (state_c)IDLE: beginif(idle2start)state_n = START ;else if(idle2wrdata)state_n = WR_DATA ;else if(idle2rddata)state_n = RD_DATA ;elsestate_n = state_c ;endSTART: beginif(start2wrdata)state_n = WR_DATA ;else if(start2rddata)state_n = RD_DATA ;elsestate_n = state_c ;endWR_DATA: beginif(wrdata2recack)state_n = REC_ACK ;elsestate_n = state_c ;endRD_DATA: beginif(rddata2sendack)state_n = SEND_ACK ;elsestate_n = state_c ;endREC_ACK: beginif(recack2idle)state_n = IDLE ;else if(recack2stop)state_n = STOP ;elsestate_n = state_c ;endSEND_ACK: beginif(sendack2idle)state_n = IDLE ;else if(sendack2stop)state_n = STOP ;elsestate_n = state_c ;endSTOP: beginif(stop2idle)state_n = IDLE ;elsestate_n = state_c ;enddefault: state_n = IDLE ;endcaseend//  状态转移条件assign  idle2start      = state_c == IDLE       && req && (cmd & `STA)                      ;assign  idle2wrdata     = state_c == IDLE       && req && (cmd & `WRITE)                    ;assign  idle2rddata     = state_c == IDLE       && req && (cmd & `READ)                     ;assign  start2wrdata    = state_c == START      && end_cnt_bit && (command & `WRITE)        ;   assign  start2rddata    = state_c == START      && end_cnt_bit && (command & `READ)         ;   assign  wrdata2recack   = state_c == WR_DATA    && end_cnt_bit                              ;assign  rddata2sendack  = state_c == RD_DATA    && end_cnt_bit                              ;assign  recack2idle     = state_c == REC_ACK    && end_cnt_bit && (command & `STO) == 0     ;assign  recack2stop     = state_c == REC_ACK    && end_cnt_bit && (command & `STO)          ;assign  sendack2idle    = state_c == SEND_ACK   && end_cnt_bit && (command & `STO) == 0     ;assign  sendack2stop    = state_c == SEND_ACK   && end_cnt_bit && (command & `STO)          ;assign  stop2idle       = state_c == STOP       && end_cnt_bit                              ;//  cnt_scl  200KHz  一个SCL周期为250个系统时钟周期always @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_scl <= 0 ;endelse if(add_cnt_scl)beginif(end_cnt_scl)begincnt_scl <= 0 ;end				else begin	    cnt_scl <= cnt_scl + 1 ;end 		    endend                 assign	add_cnt_scl	= state_c != IDLE ;assign	end_cnt_scl	= add_cnt_scl && (cnt_scl == `SCL_PERIOD - 1) ;//  bit_numalways @(*)beginif(state_c == RD_DATA || state_c == WR_DATA)beginbit_num = 8 ;endelse beginbit_num = 1 ;endend//  cnt_bitalways @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_bit <= 0 ;endelse if(add_cnt_bit)beginif(end_cnt_bit)begincnt_bit <= 0 ;end				else begin	    cnt_bit <= cnt_bit + 1 ;end 		    endend           assign	add_cnt_bit	= end_cnt_scl ;assign	end_cnt_bit	= add_cnt_bit && (cnt_bit == bit_num - 1) ;//  dout_dataalways @(posedge clk or negedge rst_n)beginif(!rst_n)begindout_data <= 0 ;endelse if(req)begindout_data <= wr_din ;endend//  commandalways @(posedge clk or negedge rst_n)beginif(!rst_n)begincommand <= 0 ;endelse if(req)begincommand <= cmd ;endend//  scl_doutalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginscl_dout <= 1'b1 ;endelse if(idle2start | idle2wrdata | idle2rddata)beginscl_dout <= 1'b0 ;endelse if(add_cnt_scl && cnt_scl == `SCL_HALF)beginscl_dout <= 1'b1 ;endelse if(end_cnt_scl && ~stop2idle)beginscl_dout <= 1'b0 ;endend//  sda_dout_enalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginsda_dout_en <= 1'b0 ;endelse if(idle2start || idle2wrdata || rddata2sendack || sendack2stop || recack2stop)beginsda_dout_en <= 1'b1 ;endelse if(idle2rddata || start2rddata || wrdata2recack || stop2idle)beginsda_dout_en <= 1'b0 ;endend//  sda_doutalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginsda_dout <= 1'b1 ;endelse if(state_c == START)beginif(cnt_scl == `HIGH_HALF)beginsda_dout <= 1'b0 ;endelse if(cnt_scl == `LOW_HALF)beginsda_dout <= 1'b1 ;endendelse if(state_c == WR_DATA && cnt_scl == `LOW_HALF)beginsda_dout <= dout_data[7 - cnt_bit] ;endelse if(state_c == SEND_ACK && cnt_scl == `LOW_HALF)beginsda_dout <= (command & `STO) ? 1'b1 : 1'b0 ;endelse if(state_c == STOP)beginif(cnt_scl == `LOW_HALF)begin       sda_dout <= 1'b0;endelse if(cnt_scl == `HIGH_HALF)begin    sda_dout <= 1'b1;               end endelse if(wrdata2recack | rddata2sendack)beginsda_dout <= 1'b1;  endend//  rx_dataalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginrx_data <= 0 ;endelse if(state_c == RD_DATA && cnt_scl == `HIGH_HALF)beginrx_data[7 - cnt_bit] <= sda_in ;endend//  ack_flagalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginack_flag <= 1'b0 ;endelse if(state_c == REC_ACK && cnt_scl == `HIGH_HALF)beginack_flag <= ~sda_in ;endelse beginack_flag <= 1'b0 ;endend//  输出assign rdout = rx_data ;   assign rdout_vld = rddata2sendack ;assign rw_done = stop2idle | sendack2idle | recack2idle ;assign sda_out = sda_dout ; assign sda_out_en = sda_dout_en ;assign scl = scl_dout ;endmodule     

3. aht10读写控制模块

//  **************************************************************
//  Author: Zhang JunYi
//  Create Date: 2022.11.15                        
//  Design Name: AHT10    
//  Module Name: aht10_ctrl         
//  Target Device: Cyclone IV E (EP4CE6F17C8), 温湿度传感器(AHT10)             
//  Tool versions: Quartus Prime 18.1             
//  Description: aht10读写控制模块
//  **************************************************************
`include "param.v"
module aht10_ctrl (input               clk                 ,input               rst_n               ,//  i2c_interfaceoutput              req                 ,       //  读写请求output  [7:0]       wr_dout             ,       //  发送指令数据output  [3:0]       cmd                 ,       //  发送读写控制命令input   [7:0]       rdin                ,       //  读回的数据input               rw_done             ,       //  读写一字节数据完成标志input               rdin_vld            ,//  data_processoutput  [19:0]      hum_data            ,       //  湿度数据output  [19:0]      tem_data            ,       //  温度数据output              data_vld                    //  温湿度数据有效标志
);//  参数定义localparam  WAIT      =   8'b0000_0001 ,     //  上电等待40msINIT      =   8'b0000_0010 ,     //  初始化WAIT_INIT =   8'b0000_0100 ,     //  等待初始化完成IDLE      =   8'b0000_1000 ,     //  空闲RD_REQ    =   8'b0001_0000 ,     //  读数据请求WAIT_MES  =   8'b0010_0000 ,     //  等待测量完成80msREAD      =   8'b0100_0000 ,     //  读数据DONE      =   8'b1000_0000 ;     //  读操作完成//  信号定义reg     [7:0]       state_c             ;reg     [7:0]       state_n             ;reg     [27:0]      cnt_delay           ;       //  延时计数器wire                add_cnt_delay       ;wire                end_cnt_delay       ;reg     [27:0]      delay               ;reg     [3:0]       cnt_byte            ;       //  字节计数器wire                add_cnt_byte        ;wire                end_cnt_byte        ;reg                 init_flag           ;       //  初始化完成标志reg                 tx_req              ;       //  task发送请求reg     [3:0]       tx_cmd              ;       //  task发送控制命令reg     [7:0]       tx_data             ;       //  task发送数据reg     [47:0]      rd_data             ;       //  读回的温湿度数据寄存//  状态转移条件wire                wait2init           ;wire                init2waitinit       ;wire                waitinit2idle       ;wire                waitinit2init       ;wire                idle2rdreq          ;wire                rdreq2waitmes       ;wire                waitmes2read        ;wire                read2done           ;wire                done2idle           ;//  状态机always @(posedge clk or negedge rst_n)beginif(!rst_n)beginstate_c <= WAIT ;endelse beginstate_c <= state_n ;endendalways @(*)begincase (state_c)WAIT: beginif(wait2init)state_n = INIT ;elsestate_n = state_c ;endINIT: beginif(init2waitinit)state_n = WAIT_INIT ;elsestate_n = state_c ;endWAIT_INIT: beginif(waitinit2idle)state_n = IDLE ;else if(waitinit2init)state_n = INIT ;elsestate_n = state_c ;endIDLE: beginif(idle2rdreq)state_n = RD_REQ ;elsestate_n = state_c ;endRD_REQ: beginif(rdreq2waitmes)state_n = WAIT_MES ;elsestate_n = state_c ;endWAIT_MES: beginif(waitmes2read)state_n = READ ;elsestate_n = state_c ;endREAD: beginif(read2done)state_n = DONE ;elsestate_n = state_c ;endDONE: beginif(done2idle)state_n = IDLE ;elsestate_n = state_c ;enddefault: state_n = IDLE ;endcaseend//  状态转移描述assign  wait2init      = state_c == WAIT        && end_cnt_delay    ; assign  init2waitinit  = state_c == INIT        && end_cnt_byte     ; assign  waitinit2idle  = state_c == WAIT_INIT   && init_flag        ; assign  waitinit2init  = state_c == WAIT_INIT   && ~init_flag && end_cnt_byte       ; assign  idle2rdreq     = state_c == IDLE        && end_cnt_delay    ; assign  rdreq2waitmes  = state_c == RD_REQ      && end_cnt_byte     ; assign  waitmes2read   = state_c == WAIT_MES    && end_cnt_delay    ; assign  read2done      = state_c == READ        && end_cnt_byte     ; assign  done2idle      = state_c == DONE        && (1'b1)           ; //  delayalways @(posedge clk or negedge rst_n)beginif(!rst_n)begindelay <= `DELAY_40ms ;endelse if(state_c == WAIT)begindelay <= `DELAY_40ms ;endelse if(state_c == WAIT_MES)begindelay <= `DELAY_80ms ;endelse if(state_c == IDLE)begindelay <= `DELAY_500ms ;endend//  cnt_delayalways @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_delay <= 0 ;endelse if(add_cnt_delay)beginif(end_cnt_delay)begincnt_delay <= 0 ;end				else begin	    cnt_delay <= cnt_delay + 1 ;end 		    endend             assign	add_cnt_delay = (state_c == WAIT) || (state_c == WAIT_MES) || (state_c == IDLE) ;assign	end_cnt_delay = add_cnt_delay && (cnt_delay == delay - 1) ;//  cnt_bytealways @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_byte <= 0 ;endelse if(add_cnt_byte)beginif(end_cnt_byte)begincnt_byte <= 0 ;end				else begin	    cnt_byte <= cnt_byte + 1 ;end 		    endend             assign	add_cnt_byte = rw_done ;assign	end_cnt_byte = add_cnt_byte && (cnt_byte == ((state_c == READ) ? 7:4) - 1) ;//  使用task任务发送数据always  @(*)begincase (state_c)INIT : case(cnt_byte)0           :TX(1'b1,{`STA | `WRITE},{`AHT10_ADDR,1'b0}) ;       //  发起始位、写控制字1           :TX(1'b1,`WRITE,`GET_STATE) ;                        2           :TX(1'b1,`WRITE,`AHT10_INIT_1) ;  3           :TX(1'b1,{`WRITE | `STO} ,`AHT10_INIT_2) ;  default     :TX(1'b0,tx_cmd,tx_data) ;endcase WAIT_INIT:case(cnt_byte)0           :TX(1'b1,{`STA | `WRITE},{`AHT10_ADDR,1'b0}) ;       //  发起始位、写控制字1           :TX(1'b1,`WRITE,`GET_STATE) ;                        //  发读取状态字命令2           :TX(1'b1,{`STA | `WRITE},{`AHT10_ADDR,1'b1}) ;       //  发读控制字3           :TX(1'b1,{`READ | `STO},0) ;  default     :TX(1'b0,tx_cmd,tx_data) ;endcaseRD_REQ: case(cnt_byte)0           :TX(1'b1,{`STA | `WRITE},{`AHT10_ADDR,1'b0}) ;       //  发起始位、写控制字1           :TX(1'b1,`WRITE,`AHT10_MEAS_0) ;  2           :TX(1'b1,`WRITE,`AHT10_MEAS_1) ;  3           :TX(1'b1,{`WRITE | `STO},`AHT10_MEAS_2) ;  default     :TX(1'b0,tx_cmd,tx_data) ;endcase    READ :            case(cnt_byte)0           :TX(1'b1,{`STA | `WRITE},{`AHT10_ADDR,1'b1}) ;       //  发起始位、写控制字1           :TX(1'b1,`READ,0) ;  2           :TX(1'b1,`READ,0) ;  3           :TX(1'b1,`READ,0) ;  4           :TX(1'b1,`READ,0) ;  5           :TX(1'b1,`READ,0) ;  6           :TX(1'b1,{`READ | `STO},0) ;default     :TX(1'b0,tx_cmd,tx_data) ;endcasedefault: TX(1'b0,0,0) ;endcaseend//  init_flagalways @(posedge clk or negedge rst_n)beginif(!rst_n)begininit_flag <= 1'b0 ;endelse if(state_c == WAIT_INIT && rw_done && rdin[3])begininit_flag <= 1'b1 ;endend//  rd_dataalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginrd_data <= 0 ;endelse if(state_c == READ && rw_done && cnt_byte > 0)beginrd_data <= {rd_data[39:0],rdin} ;endend//  tasktask 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 wr_dout = tx_data ; assign hum_data  = rd_data[39:20] ;assign tem_data  = rd_data[19:0] ;assign data_vld  = read2done ;endmodule

4. aht10温湿度数据处理模块

//  **************************************************************
//  Author: Zhang JunYi
//  Create Date: 2022.11.15                        
//  Design Name: AHT10    
//  Module Name: data_process         
//  Target Device: Cyclone IV E (EP4CE6F17C8), 温湿度传感器(AHT10)             
//  Tool versions: Quartus Prime 18.1             
//  Description: aht10温湿度数据处理模块
//  **************************************************************
`include "param.v"module data_process (input           clk             ,input           rst_n           ,//  aht10_ctrlinput           din_vld         ,input   [19:0]  tem_data        ,input   [19:0]  hum_data        ,//  set_limitinput   [7:0]   tem_lim         ,input   [7:0]   hum_lim         ,//  uart_txinput           ready           ,output  [7:0]   tx_data         ,output          tx_data_vld     ,//  beepoutput          beep                
);//  信号定义reg     [19:0]      tem_data_r          ;       //  温湿度数据寄存reg     [19:0]      hum_data_r          ;reg     [5:0]       cnt_byte            ;       //  字节计数器wire                add_cnt_byte        ;wire                end_cnt_byte        ;reg                 process_flag        ;       //  数据处理标志reg     [7:0]       data                ;       //  串口显示数据reg                 flag                ;       //  蜂鸣器标志reg                 tem_alarm           ;reg                 hum_alarm           ;wire    [7:0]       tem_ten             ;       //  温度数据十位wire    [7:0]       tem_bit             ;       //  温度数据个位wire    [7:0]       tem_dot1            ;       //  温度数据小数位1wire    [7:0]       tem_dot2            ;       //  温度数据小数位2wire    [7:0]       hum_ten             ;       //  湿度数据十位wire    [7:0]       hum_bit             ;       //  湿度数据个位wire    [7:0]       hum_dot1            ;       //  湿度数据小数位1wire    [7:0]       hum_dot2            ;       //  湿度数据小数位2//  fifowire                wrreq               ;wire                rdreq               ;wire                empty               ;wire                full                ;wire    [7:0]       q                   ;wire    [7:0]       usedw               ;always @(posedge clk or negedge rst_n)beginif(!rst_n)begintem_data_r <= 0 ;hum_data_r <= 0 ;endelse if(din_vld)begin// tem_data_r <= ((tem_data * 200) >> 20) - 50 ;   // hum_data_r <= (hum_data * 100) >> 20 ;tem_data_r <= (((tem_data * 2000) >> 12) - (500)); //扩大10倍hum_data_r <= ((hum_data * 1000) >> 12);// tem_data_r = ((((tem_data << 10)//              + (tem_data << 9)//              + (tem_data << 8)//              + (tem_data << 7)//              + (tem_data << 6)//              + (tem_data << 4)) >> 20 ) - 500) * 10 ;// hum_data_r = (((hum_data << 9)//             + (hum_data << 8)//             + (hum_data << 7)//             + (hum_data << 6)//             + (hum_data << 5)//             + (hum_data << 3)) >> 20) * 10 ;endend//  process_flagalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginprocess_flag <= 1'b0 ;endelse if(din_vld)beginprocess_flag <= 1'b1 ;endelse if(end_cnt_byte)beginprocess_flag <= 1'b0 ;endend//  cnt_bytealways @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_byte <= 0 ;endelse if(add_cnt_byte)beginif(end_cnt_byte)begincnt_byte <= 0 ;end				else begin	    cnt_byte <= cnt_byte + 1 ;end 		    endend             assign	add_cnt_byte =  process_flag;assign	end_cnt_byte = add_cnt_byte && (cnt_byte == 24) ;//  flag// always @(*)begin//     if(!rst_n)begin//         flag <= 1'b0 ;//     end//     else if(add_cnt_byte && (cnt_byte == 6 && tem_ten >= (tem_lim / 10)) || (cnt_byte == 18 && hum_ten >= (hum_lim / 10)))begin//         flag <= 1'b1 ;//     end//     else if(add_cnt_byte && (cnt_byte == 6 && tem_ten < (tem_lim / 10)) || (cnt_byte == 18 && hum_ten < (hum_lim / 10)))begin//         flag <= 1'b0 ;//     end// end//  tem_alarmalways @(posedge clk or negedge rst_n)beginif(!rst_n)begintem_alarm <= 1'b0 ;endelse if(add_cnt_byte && tem_ten >= (tem_lim / 10))begintem_alarm <= 1'b1 ;endelse if(add_cnt_byte && tem_ten < (tem_lim / 10))begintem_alarm <= 1'b0 ;endend//  hum_alarmalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginhum_alarm <= 1'b0 ;endelse if(add_cnt_byte && hum_ten >= (hum_lim / 10))beginhum_alarm <= 1'b1 ;endelse if(add_cnt_byte && hum_ten < (hum_lim / 10))beginhum_alarm <= 1'b0 ;endend//  flagalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginflag <= 1'b0 ;endelse if(tem_alarm | hum_alarm)beginflag <= 1'b1 ;endelse if(~tem_alarm & ~hum_alarm)beginflag <= 1'b0 ;endendassign tem_ten = (tem_data_r / 100 % 10 ) ;assign tem_bit = (tem_data_r % 100 / 10  ) ;assign tem_dot1 = (tem_data_r % 10  ) ;assign tem_dot2 = 0 ;assign hum_ten = (hum_data_r / 100 % 10 ) ;assign hum_bit = (hum_data_r % 100 / 10  ) ;assign hum_dot1 = (hum_data_r % 10 ) ;assign hum_dot2 = 0 ;//  dataalways @(*)begincase (cnt_byte)1 : data <= 8'hce;      //  温度ASCII码2 : data <= 8'hc2;3 : data <= 8'hb6;4 : data <= 8'hc8;5 : data <= 8'h3a;      //   :ASCII码6 : data <= tem_ten + 48;       //  十位7 : data <= tem_bit + 48;       //  个位8 : data <= 8'h2e;      //   .ASCII码9 : data <= tem_dot1 + 48;            //  小数位110: data <= tem_dot2 + 48;            //  小数位211: data <= 8'ha1;      //   ℃ASCII码12: data <= 8'he6;13: data <= 9;          //   tab14: data <= 8'hca;      //   湿度ASCII码15: data <= 8'haa;16: data <= 8'hb6;17: data <= 8'hc8;18: data <= 8'h3a;      //   :ASCII码19: data <= hum_ten + 48;      //  十位20: data <= hum_bit + 48;       //  个位21: data <= 8'h2e;      //   .ASCII码22: data <= hum_dot1 + 48;           //  小数位123: data <= hum_dot2 + 48;           //  小数位224: data <= 8'h25;      //   %ASCII码default: data <= 0 ;endcaseend//  fifo例化fifo	fifo_inst (.aclr   ( ~rst_n        ),.clock  ( clk           ),.data   ( data          ),.rdreq  ( rdreq         ),.wrreq  ( wrreq         ),.empty  ( empty         ),.full   ( full          ),.q      ( q             ),.usedw  ( usedw         ));assign  wrreq = ~full && process_flag && cnt_byte > 0;assign  rdreq = ~empty && ready ;//  输出assign tx_data = q ;assign tx_data_vld = rdreq ;assign beep = ~flag ;endmodule

5. aht10设置温湿度报警值模块

//  **************************************************************
//  Author: Zhang JunYi
//  Create Date: 2022.11.15                        
//  Design Name: AHT10    
//  Module Name: set_limit         
//  Target Device: Cyclone IV E (EP4CE6F17C8), 温湿度传感器(AHT10)             
//  Tool versions: Quartus Prime 18.1             
//  Description: aht10设置温湿度报警值模块
//  **************************************************************module set_limit (input           clk             ,input           rst_n           ,//  key_filterinput           tem_add         ,       //  设置温度报警值标志,每按一次+5摄氏度input           hum_add         ,       //  设置湿度报警值标志,每按一次+5%input           set             ,       //  设置模式,每按一次在温度和湿度设置之间切换//  seg_driveroutput          modle           ,       //  当前设置模式  0:设置温度   1:设置湿度output  [7:0]   tem_lim         ,       //  温度报警值output  [7:0]   hum_lim                 //  湿度报警值
);//  信号定义reg     [7:0]   cnt_tem         ;wire            add_cnt_tem     ;wire            end_cnt_tem     ;reg     [7:0]   cnt_hum         ;wire            add_cnt_hum     ;wire            end_cnt_hum     ;reg             flag            ;       //  设置模式//  cnt_temalways @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_tem <= 0 ;endelse if(add_cnt_tem)beginif(end_cnt_tem)begincnt_tem <= 0 ;end				else begin	    cnt_tem <= cnt_tem + 10 ;end 		    endend             assign	add_cnt_tem = tem_add ;assign	end_cnt_tem = add_cnt_tem && (cnt_tem == 100) ;//  cnt_humalways @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_hum <= 0 ;endelse if(add_cnt_hum)beginif(end_cnt_hum)begincnt_hum <= 0 ;end				else begin	    cnt_hum <= cnt_hum + 10 ;end 		    endend             assign	add_cnt_hum = hum_add ;assign	end_cnt_hum = add_cnt_hum && (cnt_hum == 100) ;//  flagalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginflag = 1'b0 ;endelse if(set)beginflag = ~flag ;endend//  输出assign tem_lim = cnt_tem ;assign hum_lim = cnt_hum ;assign modle = flag ;endmodule

6. 参数文件

//  IIC时钟参数
`define  SCL_PERIOD  250
`define  SCL_HALF    125
`define  LOW_HALF    65 
`define  HIGH_HALF   190//  控制命令
`define  WRITE    4'b1000     //  写
`define  READ     4'b0100     //  读
`define  STA      4'b0010     //  起始位
`define  STO      4'b0001     //  停止位//  AHT10命令参数
`define AHT10_ADDR 7'b0111_000      //  设备地址`define GET_STATE    8'b1110_0001     //  获取状态字`define AHT10_INIT_0 8'b1110_0001     //  初始化命令序列
`define AHT10_INIT_1 8'b0000_1000
`define AHT10_INIT_2 8'b0000_0000`define AHT10_MEAS_0 8'b1010_1100       //  触发测量命令序列    
`define AHT10_MEAS_1 8'b0011_0011
`define AHT10_MEAS_2 8'b0000_0000`define AHT10_MEAS_4 8'b0111_0001     //  读数据命令`define DELAY_40ms  2_000_000        //  上电等待40ms
`define DELAY_80ms  4_000_000        //  测量数据80ms
`define DELAY_500ms 5000_0000       //  0.5s采集一次数据//  UART参数配置文件
`define BAUD_RATE_115200`define CLK_FREQ 50_000_000`ifdef BAUD_RATE_9600`define BAUD `CLK_FREQ / 9600
`elsif BAUD_RATE_19200`define BAUD `CLK_FREQ / 19200
`elsif BAUD_RATE_38400`define BAUD `CLK_FREQ / 38400
`elsif BAUD_RATE_57600`define BAUD `CLK_FREQ / 57600
`elsif BAUD_RATE_115200`define BAUD `CLK_FREQ / 115200
`endif 

7. 其他模块
按键消抖模块、数码管驱动模块、串口发送模块比较简单,这里不过多赘述。

七、仿真测试

由于没有AHT10的仿真模型,仿真模拟AHT10返回数据,下面只对比较重要的几个模块进行仿真测试。
仿真代码如下:

`timescale 1ns/1psmodule test_tb ();reg             clk         ;reg             rst_n       ;wire			sda			;wire			scl			;reg				sda_i	    ;top u_top(.clk        ( clk        ),.rst_n      ( rst_n      ),.sda        ( sda        ),.scl        ( scl        ),.uart_txd   ( uart_txd   ));assign sda = u_top.sda_out_en ? 1'bz:sda_i;localparam CYCLE = 20;always #(CYCLE/2) clk=~clk;reg			[ 10:0 ]			i			;reg			[ 10:0 ]			j			;initial beginrst_n = 1'b1;clk = 1'b1;#(CYCLE * 2);rst_n = 1'b0;#(CYCLE * 2);#2;rst_n = 1'b1;@(posedge u_top.u_aht10_ctrl.init2waitinit); //初始化for (i = 0; i<4 ; i = i+1 ) begin@(posedge u_top.u_aht10_ctrl.add_cnt_byte);endfor (i = 0; i<9 ; i = i+1 ) begin //模拟初始化完成@(posedge u_top.u_i2c_interface.add_cnt_bit)if(i == 3)sda_i = 1;else if(i == 8)sda_i = 0;endrepeat(5) begin@(posedge u_top.u_aht10_ctrl.waitmes2read);//等待控制模块到达读取状态@(posedge u_top.u_aht10_ctrl.add_cnt_byte);for (i = 0; i<6 ; i = i+1 ) begin//发送6个数据@(posedge u_top.u_aht10_ctrl.add_cnt_byte);for (j = 0; j<9 ; j = j+1 ) begin//模拟从机回数据@(posedge u_top.u_i2c_interface.end_cnt_scl)if(j == 8)sda_i = 0;elsesda_i = {$random};endendend$stop;
endendmodule

仿真结果:
(1)aht10_ctrl模块
在这里插入图片描述
(2)i2c_interface模块
在这里插入图片描述
(3)data_process模块
在这里插入图片描述
(4)uart_tx模块
在这里插入图片描述

八、板级验证

终端信息打印:
在这里插入图片描述
上板之后,温湿度报警值默认是0℃和0%,需要手动设置报警界限,所以一开始上板蜂鸣器就会开始工作。通过按键设置好温湿度报警值之后就会正常工作,当温度或者湿度超过预设报警值时蜂鸣器开始工作,当温度或湿度同时低于预设报警值时,蜂鸣器停止工作。

按键操作说明:

key1:设置温湿度报警值模式,初始状态为设置温度报警值模式(数码管显示“TE 温度值”),按下key1切换为设置湿度报警值模式(数码管显示“HU 湿度值”),在两种设置模式之间切换。
key2:当处于设置温度报警值模式时(数码管显示“TE 温度值”),按下key2,温度报警值+10%,最高设置不超过90%。
key3:当处于设置湿度报警值模式时(数码管显示“HU 湿度值”),按下key3:湿度报警值+10℃,最高设置不超过90℃。
key4:复位按键。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部