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

一、需求分析

  Aht10温湿度传感器的驱动,并将采集的温湿度数据通过串口发送到PC端

 AHT10有道云笔记

二、AHT10简介

(一)AHT10特性

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

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

开发板硬件管脚配置表:

Direction

Node Name

Location

Input

clk

PIN E1 

Input

rst n

PIN E15

Input

rx

PIN M2

Output

tx

PIN G1

Output

iic_scl

PIN D9

Bidir

iic_sda

PIN E9

AHT10读写遵循I2C协议,可参考IIC协议工程IIC有道云笔记,时序图如下:

每个传输序列都以 Start状态作为开始并以Stop状态作为结束,如图:

启动传输状态:在SCL 高电平期间,SDA 拉低。开始状态是由主机控制的一种特殊的总线状态,指示从机传输开始 (Start 之后,总线一般被认为处于占线状态)

停止传输状态:在SCL 高电平期间,SDA 释放。停止状态是由主机控制的一种特殊的总线状态,指示从机传输结束 (Stop 之后,总线一般被认为处于闲置状态)

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

1、AHT10基本指令集:

2、AHT10测量步骤

(1)上电后等待40ms ,读取温湿度值之前,首先看状态字的校准使能位Bit[3]是否为1(通过发送0x71可以获取一个字节的状态字),如果不为1,要发送0xEi命令(初始化),此命令参数有两个字节,第一个字节为0x08,第二个字节为0x00。状态位说明如下:

(2)直接发送0xAC命令(触发测量),此命令参数有两个字节,第一个字节为0x33,第二个字节为0x00。

(3)等待80ms测量数据完成,发送0x71读取6字节温湿度数据。

因此,将AHT10温度传感器进行模块划分,主要包括控制模块和i2c接口模块,由于之前已经学过并且完成了i2c接口模块的仿真,所以在AHT10控制模块中直接进行调用。

(三)数据转换

按照此公式进行转换数据不正确,所以在数据转换代码中做了修改。由于在FPGA中不能进行小数运算,所以我们对计算公式进行了打拍和扩大倍数的处理(流水线处理)http://t.csdnimg.cn/Qpmg5

/****************************************************************
                   接收数据处理
****************************************************************/ 
  //流水线处理数据
   reg    [33:0]  humi_data_r1;
   reg    [34:0]  temp_data_r1;
   
   reg    [14:0]  humi_data_r2;
   reg    [14:0]  temp_data_r2;

   always@(posedge clk or negedge rst_n)
       if(!rst_n)begin
           humi_data_r1 <= 0;
           temp_data_r1 <= 0;
       end
       elsebegin
           humi_data_r1 <= humi_data * 10000;  //保留两位小数
           temp_data_r1 <= temp_data * 20000;
       end

   always@(posedge clk or negedge rst_n)
       if(!rst_n)begin
           humi_data_r2 <= 0;
           temp_data_r2 <= 0;
       end
       elsebegin
           humi_data_r2 <= humi_data_r1 >> 20;
           temp_data_r2 <= (temp_data_r1 >> 20) - 5000;
       end

   assign tem_data = temp_data_r2;
   assign hum_data  = humi_data_r2;

三、系统架构设计

模块框架图:

       本工程将系统功能划分为5个子模块,对于数据处理的两个子模块下面包含各有一个次子模块(可调用多次),其中hex2ascii这个模块下面还包含了hex4bit2ascii模块(循环调用8次),各模块功能描述如下:

top         :顶层设计模块。

aht10_ctrl  :控制驱动AHT10命令的发送、驱动i2c_interface接口模块与AHT10传感器进行数据交互。

i2c_interface:AHT10使用标准的I2C接口协议,该模块实现aht10_ctrl控制模块通过I2C协议时序读取温湿度数据。

control     : 通过调用两次binary_bcd模块将aht10_ctrl控制模块读回的温湿度数据,将二进制数转为bcd码,tem_bcd_data与hum_bcd_data拼接后传入num_porcess模块,共 5byte 共 30bit 数据,高 15 位为温度,低 15 位为湿度。

binary_bcd :实现二进制转换为bcd码的模块;

num_process :温湿度数据处理模块,调用hex2ascii模块将转换的数据ASCII码通过串口进行发送。

hex2ascii    :利用generate语句对hex4bit2ascii循环调用八次,将接收到的bcd数据转换为ASCII码。

tx_fsm_ uart :串口数据发送模块,将data_process模块转换后的ASCII格式数据通过串口发送给上位机,通过串口调试助手,打印温湿度数据信息。

为例节约资源,将采集的温湿度数据进行二进制转bcd码后传入数据处理模块通过hex2ascii和hex4bit2ascii两个子模块的调用将数据处理后可在串口上显示为:

温度 :XX.XX ℃   湿度 : XX.XX RH 这样的格式。

四、模块说明

(一)主要模块端口信号列表

top模块

clk

时钟信号

rst_n

复位信号

tx

发送数据到PC端

iic_scl

SCL时钟总线

iic_sda

SDA数据总线

aht_ctrl模块

clk    

时钟信号

rst_n  

复位信号

tem_data

输出温度数据

hum_data

输出湿度数据

data_vld

输出数据使能

iic_scl

串行SCL时钟信号

iic_sda

iic三态门

iic_interface模块

clk       

时钟信号

rst_n     

复位信号

cmd       

接口命令

done      

输出读完一次数据结束使能

rev_ack   

输出接收到的响应信号

wr_data   

输入写数据

rd_data   

输出读数据

rd_data_vld

输出读使能

iic_scl   

串行SCL时钟信号

iic_sda   

iic三态门

control模块

clk

时钟信号

rst_n

复位信号

hum_data        

输入湿度数据

tem_data        

输入温度数据

tem_data_vld    

输入数据使能

tem_bcd         

输出温度数据bcd码

bcd_tem_data_vld

输出温度数据使能

hum_bcd         

输出湿度数据bcd码

bcd_hum_data_vld

输出湿度数据使能

 num_proces模块

clk

时钟信号

rst_n

复位信号

tx_ready       

接受串口反馈信号

tem_bcd_data

输入温度数据bcd码

bcd_tem_data_vld

温度使能

hum_bcd_data   

输入湿度数据bcd码

bcd_hum_data_vld

湿度使能信号

tx_data

输出数据发送到串口

tx_data_vld

串口使能信号

tx_fsm_uart

端口

说明

clk

时钟信号

rst_n

复位信号

tx_data 

输入转换后的数据(8bit)

tx_data_vld

接收模块使能信号

tx_ready  

输出反馈使能

tx        

输出数据发送到PC端(1bit)

(二)状态转移

通过对AHT10传感器读取数据流程的掌握,可将控制模块状态机梳理出来:

(三)时序图

五、完整代码

(1)top

/**************************************功能介绍***********************************
Date	: 
Author	: linxiaoxiao.
Version	: 
Description: 顶层文件
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module top( 
    input				clk		,
    input				rst_n	,
    output				iic_scl ,
    inout               iic_sda ,  
    output		        tx 	
);								 
//---------<参数定义>--------------------------------------------------------- 
    
//---------<内部信号定义>-----------------------------------------------------
    wire    [14:0]    hum_data         ;
    wire    [14:0]    tem_data         ;
    wire              ready            ;
    wire    [7:0]     tx_data	       ;
    wire              tx_data_vld      ;

    wire    [15:0]    tem_bcd          ;
    wire    [15:0]    hum_bcd          ;

    wire              data_vld         ;

    wire              bcd_tem_data_vld ;
    wire              bcd_hum_data_vld ;   


aht10_ctrl  aht10_ctrl_inst  (
    /* input               */.clk     ( clk      ),
    /* input               */.rst_n   ( rst_n    ),
    /* output  [14:0]      */.tem_data( tem_data ),
    /* output  [14:0]      */.hum_data( hum_data ),
    /* output              */.data_vld( data_vld ),
    /* output              */.iic_scl ( iic_scl  ),
    /* inout               */.iic_sda ( iic_sda  )
);		

 control control_inst( 
    /* input				 */.clk		            ( clk		       ),
    /* input				 */.rst_n	            ( rst_n	           ),
    /* input		[14:0]	 */.hum_data   	        ( hum_data   	   ),
    /* input		[14:0]	 */.tem_data   	        ( tem_data   	   ),
    /* input		    	 */.tem_data_vld        ( data_vld         ),
    /* output	    [15:0]	 */.tem_bcd 	        ( tem_bcd 	       ),   
    /* output                */.bcd_tem_data_vld    ( bcd_tem_data_vld ),
    /* output	    [15:0]	 */.hum_bcd 	        ( hum_bcd 	       ),
    /* output                */.bcd_hum_data_vld    ( bcd_hum_data_vld )
);			

num_proces num_proces_inst( 
    /* input				 */.clk		        ( clk	           ),
    /* input				 */.rst_n	        ( rst_n            ),
    /* input                 */.tx_ready        ( ready            ),
    /* input		[15:0]	 */.tem_bcd_data	( tem_bcd          ),
    /* input                 */.bcd_tem_data_vld( bcd_tem_data_vld ), 
    /* input       [15:0]    */.hum_bcd_data    ( hum_bcd          ),
    /* input                 */.bcd_hum_data_vld( bcd_hum_data_vld ),
    /* output	reg [7:0]	 */.tx_data	        ( tx_data	       ),
    /* output		         */.tx_data_vld     ( tx_data_vld      )
);		


tx_fsm_uart#(
        .CHECK_BIT (  "None"     ) ,  //校验方法,“None”无校验,“Odd”奇校验,“E
        .BPS       ( 115200     ) ,
        .CLK       ( 50_000_000 )
    )tx_fsm_uart_inst( 
    /* input				 */.clk		    ( clk	      ),
    /* input				 */.rst_n	    ( rst_n	      ),
    /* input				 */.tx_data_vld ( tx_data_vld ),
    /* input		[7:0]	 */.tx_data	    ( tx_data     ),  
    /* output	    	     */.ready	    ( ready	      ),
    /* output	reg	         */.tx          ( tx          )	
);		
	


    
endmodule

(2)aht10_ctrl  

/**************************************功能介绍***********************************
Date	: 
Author	: linxiaoxiao.
Version	: 
Description: 
*********************************************************************************/
//aht10_ctrl aht10_ctrl_inst  (
//    /* input               */.clk     (clk     ),
//    /* input               */.rst_n   (rst_n   ),
//    /* output  [19:0]      */.tem_data(tem_data),
//    /* output  [19:0]      */.hum_data(hum_data),
//    /* output              */.data_vld(data_vld),
//    /* input               */.iic_scl (iic_scl ),
//    /* inout               */.iic_sda (iic_sda )  
//);		

//---------<模块及端口声名>------------------------------------------------------
module aht10_ctrl    (
    input              clk     ,
    input              rst_n   ,
    output  [14:0]     tem_data,
    output  [14:0]     hum_data,
    output             data_vld,
    output             iic_scl ,
    inout              iic_sda
);						 
//---------<参数定义>--------------------------------------------------------- 
    //状态机参数定义
localparam  
            WAIT      = 0, //上电等待40ms,等待结束后进入INIT状态。
            INIT      = 1, //初始化,发送初始化指令,进入WAIT_INIT状态。
            IDLE      = 2, //空闲状态,等待读请求。
            RD_REQ    = 3, //发送读取温湿度数据指令,发送完成后进入WAIT_MES状态。
            WAIT_MES  = 4, //等待80ms测量温湿度数据完成,进入READ状态。
            READ      = 5, //读取温度数据,读取完成后进入IDLE状态,等待下一次读请求。
            DEALY     = 6; //延时两秒

parameter  WAIT_20MS  = 1_000_000; 

//接口模块控制命令
    `define     START_BIT   5'b00001
    `define     WRITE_BIT   5'b00010
    `define     READ_BIT    5'b00100
    `define     STOP_BIT    5'b01000
    `define     ACK_BIT     5'b10000

//  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     //  读数据命令

        
//---------<内部信号定义>-----------------------------------------------------
    
    wire        [7:0]       rd_data         ;          
    wire                    rd_data_vld     ;  
    wire                    done            ; 

    reg         [4:0]       cmd             ;
    reg         [7:0]       op_wr_data      ;


    reg 	    [2:0]	    state           ;//现态

    reg			[27:0]	    cnt	   	        ;
    reg         [27:0]      max             ;
    wire				    add_cnt,end_cnt	;
    reg			[3:0]	    cnt_byte	   	;
    wire					add_byte_cnt,end_byte_cnt	;
    reg         [2:0]       byte_max        ;

    wire                    wait2init       ;
    wire                    init2idle       ;
    wire                    idle2re_req     ;
    wire                    rd_req2wait_mes ;   
    wire                    wait_mes2read   ;
    wire                    read2delay      ;        
    wire                    dealy2idle      ;


/****************************************************************
                    fsm
****************************************************************/
// 时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        state <= WAIT;
    end 
    else case(state)
            WAIT    : if(wait2init)
                    state <= INIT;
            INIT    : if(init2idle)
                    state <= IDLE;
            IDLE    : if(idle2re_req)
                    state <= RD_REQ;
            RD_REQ  : if(rd_req2wait_mes)
                    state <= WAIT_MES;
            WAIT_MES: if(wait_mes2read)
                    state <= READ;
            READ    : if(read2delay)
                    state <= DEALY;
            DEALY   : if(dealy2idle)
                    state <= IDLE;
            default :  state <= WAIT;
        endcase
    end
            
    assign      wait2init       = state == WAIT      && end_cnt;
    assign      init2idle       = state == INIT      && end_byte_cnt;
    assign      idle2re_req     = state == IDLE;
    assign      rd_req2wait_mes = state == RD_REQ    && end_byte_cnt;
    assign      wait_mes2read   = state == WAIT_MES  && end_cnt;   
    assign      read2delay      = state == READ      && end_byte_cnt;        
    assign      dealy2idle      = state == DEALY     && end_cnt;

/****************************************************************
                    延时计数
****************************************************************/
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt <= 'd0;
        end 
        else if(add_cnt)begin 
            if(end_cnt)begin 
                cnt <= 'd0;
            end
            else begin 
                cnt <= cnt + 1'b1;
            end 
        end
    end 

    assign add_cnt = (state == WAIT || state == DEALY || state == WAIT_MES);
    assign end_cnt = add_cnt && cnt == max - 1;

    always @(*)begin 
      case (state)
            WAIT    : max = WAIT_20MS*4;
            DEALY   : max = WAIT_20MS*100;
            WAIT_MES: max = WAIT_20MS*2;
        default: max = 1 ;
      endcase
    end
/****************************************************************
                    cnt_byte
****************************************************************/
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_byte <= 'd0;
        end 
        else if(add_byte_cnt)begin 
            if(end_byte_cnt)begin 
                cnt_byte <= 'd0;
            end
            else begin 
                cnt_byte <= cnt_byte + 1'b1;
            end 
        end
    end 
    
    assign add_byte_cnt = done;
    assign end_byte_cnt = add_byte_cnt && cnt_byte == byte_max-1;
    always@(*)
        case(state)
            INIT    : byte_max = 4;
            RD_REQ  : byte_max = 4;
            READ    : byte_max = 7;
            default : byte_max = 1;
        endcase
    

//模块例化
iic_interface #(
         .SPEED  ("100K"    ),   //低速模式:"100K"  快速模式:"400K"
         .SYS_CLK(50_000_000)    //系统时钟频率
    )iic_interface_inst(
        /* input            */.clk          ( clk          ),
        /* input            */.rst_n        ( rst_n        ),
        /* input   [4:0]    */.cmd          ( cmd          ),
        /* output           */.done         ( done         ),   //操作结束
        /* output  reg      */.rev_ack      (              ),   //接收到的响应信号
        /* input   [7:0]    */.wr_data      ( op_wr_data   ),   //伴随cmd_vld信号一起接收写数据
        /* output  [7:0]    */.rd_data      ( rd_data      ),
        /* output           */.rd_data_vld  ( rd_data_vld  ),
        /* output reg       */.iic_scl      ( iic_scl      ),
        /* inout            */.iic_sda      ( iic_sda      ) 
 );     
    
 task TX;
        input   [4:0]   task_cmd        ;
        input   [7:0]   task_wr_data    ;
        begin
            cmd         = task_cmd     ;
            op_wr_data  = task_wr_data ;
        end
    endtask

    assign ready = state == IDLE;

  //  使用task任务发送数据
    always  @(*)begin
        case (state) 
            INIT : 
                case(cnt_byte)
                    0        :TX({`START_BIT | `WRITE_BIT},{`AHT10_ADDR,1'b0}) ;//  发起始位、写控制字
                    1        :TX(`WRITE_BIT,`GET_STATE) ;                        
                    2        :TX(`WRITE_BIT,`AHT10_INIT_1) ;  
                    3        :TX({`WRITE_BIT | `STOP_BIT} ,`AHT10_INIT_2) ;  
                    default  :TX(0,0) ;
                endcase  
            RD_REQ: 
                case(cnt_byte)
                    0       :TX({`START_BIT | `WRITE_BIT },{`AHT10_ADDR,1'b0}) ; //  发起始位、写控制字
                    1       :TX(`WRITE_BIT,`AHT10_MEAS_0) ;  
                    2       :TX(`WRITE_BIT,`AHT10_MEAS_1) ;  
                    3       :TX({`WRITE_BIT | `STOP_BIT},`AHT10_MEAS_2) ;  
                    default :TX(0,0) ;
                endcase    
            READ :            
                case(cnt_byte)
                    0       :TX({`START_BIT | `WRITE_BIT},{`AHT10_ADDR,1'b1}) ;//  发起始位、写控制字
                    1       :TX((`READ_BIT),8'h00);  
                    2       :TX((`READ_BIT),8'h00);  
                    3       :TX((`READ_BIT),8'h00);  
                    4       :TX((`READ_BIT),8'h00);  
                    5       :TX((`READ_BIT),8'h00);  
                    6       :TX({`ACK_BIT |`READ_BIT | `STOP_BIT},8'h00) ;
                    default :TX(0,0) ;
                endcase
            default: TX(0,0) ;
        endcase
    end

/**************************************************************
                        接收温湿度数据
**************************************************************/
    wire     [19:0]  humi_data;
    wire     [19:0]  temp_data;

    reg     [39:0]  aht10_data      ;
    wire            aht10_data_vld  ;

    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            aht10_data <= 0;
        else if(state == READ && cnt_byte > 0 && rd_data_vld)
            aht10_data <= {aht10_data[31:0],rd_data};

    assign humi_data = aht10_data[39:20];
    assign temp_data = aht10_data[19:0];

    assign aht10_data_vld = read2delay;
/****************************************************************
                    接收数据处理
****************************************************************/ 
   //流水线处理数据
    reg     [33:0]  humi_data_r1;
    reg     [34:0]  temp_data_r1;

    reg     [14:0]  humi_data_r2;
    reg     [14:0]  temp_data_r2;

    always@(posedge clk or negedge rst_n)
        if(!rst_n) begin
            humi_data_r1 <= 0;
            temp_data_r1 <= 0;
        end
        else begin
            humi_data_r1 <= humi_data * 10000;  //保留两位小数
            temp_data_r1 <= temp_data * 20000;
        end

    always@(posedge clk or negedge rst_n)
        if(!rst_n) begin
            humi_data_r2 <= 0;
            temp_data_r2 <= 0;
        end
        else begin
            humi_data_r2 <= humi_data_r1 >> 20;
            temp_data_r2 <= (temp_data_r1 >> 20) - 5000;
        end

    assign  tem_data = temp_data_r2;
    assign  hum_data  = humi_data_r2;
//有效信号打拍
    reg         data_vld_r1 ;
    reg         data_vld_r2 ;

    always@(posedge clk or negedge rst_n)
        if(!rst_n) begin
            data_vld_r1 <= 0;
            data_vld_r2 <= 0;
        end
        else begin
            data_vld_r1 <= aht10_data_vld;
            data_vld_r2 <= data_vld_r1;
        end

    assign  data_vld = data_vld_r2;


endmodule

(3)iic_interface 

/**************************************************************
@File    :   iic_interface.v
@Time    :   2023/08/28 15:48:01
@Author  :   WangHaodong 
@EditTool:   VS Code 
@Font    :   UTF-8 
@Function:   IIC接口模块
             接口命令列表(cmd):
                                0           1
             bit0(起始位)     NO         YES
             bit1(写数据)     NO         YES
             bit2(读数据)     NO         YES
             bit3(停止位)     NO         YES
             bit4(响应位)     ACK        NO_ACK       此位只针对读数据
            
            以上命令位可以实现任意组合,需要注意读写数据不能同时有效!!!
    
**************************************************************/
    // iic_interface #(
    //         .SPEED  ("100K"    ),   //低速模式:"100K"  快速模式:"400K"
    //         .SYS_CLK(50_000_000)    //系统时钟频率
    // )iic_interface_inst(
    //     /* input            */.clk          (   ),
    //     /* input            */.rst_n        (   ),
    //     /* input   [4:0]    */.cmd          (   ),
    //     /* input            */.cmd_vld      (   ),
    //     /* output           */.done         (   ),   //操作结束
    //     /* output  reg      */.rev_ack      (   ),   //接收到的响应信号
    //     /* input   [7:0]    */.wr_data      (   ),   //伴随cmd_vld信号一起接收写数据
    //     /* output  [7:0]    */.rd_data      (   ),
    //     /* output           */.rd_data_vld  (   ),
    //     /* output reg       */.iic_scl      (   ),
    //     /* inout            */.iic_sda      (   ) 
    // );

module iic_interface #(
        parameter   SPEED   =   "100K"      ,   //低速模式:"100K"  快速模式:"400K"
        parameter   SYS_CLK =   50_000_000      //系统时钟频率
    )(
    input           clk         ,
    input           rst_n       ,

//控制接口  
    input   [4:0]   cmd         ,
    output          done        ,   //操作结束
    output  reg     rev_ack     ,   //接收到的响应信号
//数据接口
    input   [7:0]   wr_data     ,   //伴随cmd_vld信号一起接收写数据
    output  [7:0]   rd_data     ,
    output          rd_data_vld ,

    output reg      iic_scl     ,
    inout           iic_sda      
);
    parameter   DIV_CNT_MAX   =  (SPEED == "100K")? SYS_CLK/100_000
                                :(SPEED == "400K")? SYS_CLK/400_000
                                : SYS_CLK/100_000;

    localparam  SCL_HIGH_HALF   =   (DIV_CNT_MAX*3/4) - 1;
    localparam  SCL_LOW_HALF    =   (DIV_CNT_MAX*1/4) - 1;

    localparam      DIV_CNT_BIT = clog2(DIV_CNT_MAX - 1);

    `define     START_BIT   5'b00001
    `define     WRITE_BIT   5'b00010
    `define     READ_BIT    5'b00100
    `define     STOP_BIT    5'b01000
    `define     ACK_BIT     5'b10000

    `define     ACK         0
    `define     NO_ACK      1

    // reg     [4:0]           cmd_r       ;
    // reg     [7:0]           wr_data_r   ;

    reg     [3:0]   state;

    localparam  IDLE        =   4'd0    ,
                START       =   4'd1    ,
                WR_DATA     =   4'd2    ,
                CHECK_ACK   =   4'd3    ,
                RD_DATA     =   4'd4    ,
                SEND_ACK    =   4'd5    ,
                STOP        =   4'd6    ;

    wire        idle2start          ;
    wire        idle2wr_data        ;
    wire        idle2rd_data        ;
    wire        state2wr_data       ;
    wire        wr_data2check_ack   ;    
    wire        check_ack2stop      ;
    wire        check_ack2idle      ;
    wire        rd_data2send_ack    ;    
    wire        send_ack2stop       ;
    wire        send_ack2idle       ;
    wire        stop2idle           ;

    reg	    [DIV_CNT_BIT-1:0]   cnt_div     ;
    wire		                add_div_cnt ;
    wire                        end_div_cnt ;	

    reg	    [3:0]               cnt_bit;
    wire		                add_bit_cnt ;
    wire                        end_bit_cnt ;	
    reg     [3:0]               cnt_bit_max ;

    reg                         sda_en      ;
    reg                         sda_out     ;
    wire                        sda_in      ;

    reg     [7:0]               rev_data    ;



/**************************************************************
                    计算数值的二进制位数
**************************************************************/
//clog2对应计算数字的二进制位数
    function integer clog2(input integer caclnum);
        begin
            for(clog2 = 0; caclnum > 0; clog2 = clog2 + 1)
                caclnum = caclnum >> 1;
        end   
    endfunction

/**************************************************************
                      寄存端口信息
**************************************************************/
    // always@(posedge clk or negedge rst_n)
    //     if(!rst_n) begin
    //       cmd_r <= 0;
    //       wr_data_r <= 0;
    //     end
    //     else if(cmd_vld) begin
    //       cmd_r <= cmd;
    //       wr_data_r <= wr_data;
    //     end

/**************************************************************
                        状态机
**************************************************************/
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            state <= IDLE;
        else case (state)
            IDLE        :   if(idle2start)
                                state <= START;
                            else if(idle2wr_data)
                                state <= WR_DATA;
                            else if(idle2rd_data)
                                state <= RD_DATA;

            START       :   if(state2wr_data)
                                state <= WR_DATA;
            
            //写数据部分
            WR_DATA     :   if(wr_data2check_ack)
                                state <= CHECK_ACK;

            CHECK_ACK   :   if(check_ack2stop)
                                state <= STOP;
                            else if(check_ack2idle)
                                state <= IDLE;

            //读数据部分
            RD_DATA     :   if(rd_data2send_ack)
                                state <= SEND_ACK;

            SEND_ACK    :   if(send_ack2stop)
                                state <= STOP;
                            else if(send_ack2idle)
                                state <= IDLE;

            STOP        :   if(stop2idle)
                                state <= IDLE;
            default     :   state <= IDLE;
        endcase

    assign idle2start   =   state == IDLE /*  && cmd_vld */ && (cmd & `START_BIT);  //命令中包含起始位
    assign idle2wr_data =   state == IDLE /*  && cmd_vld */ && (cmd & `WRITE_BIT);  //命令中包含写数据
    assign idle2rd_data =   state == IDLE /*  && cmd_vld */ && (cmd & `READ_BIT );  //命令中包含读数据

    assign state2wr_data  =   state == START   &&  end_bit_cnt && (cmd & `WRITE_BIT);  //命令中包含写数据、1bit的起始位发送完成

    assign wr_data2check_ack    =   state == WR_DATA        &&  end_bit_cnt; 
    assign check_ack2stop       =   state == CHECK_ACK      &&  end_bit_cnt && (cmd & `STOP_BIT);
    assign check_ack2idle       =   state == CHECK_ACK      &&  end_bit_cnt && !(cmd & `STOP_BIT);

    assign rd_data2send_ack = state == RD_DATA  &&  end_bit_cnt;  
    assign send_ack2stop    = state == SEND_ACK &&  end_bit_cnt && (cmd & `STOP_BIT);
    assign send_ack2idle    = state == SEND_ACK &&  end_bit_cnt && !(cmd & `STOP_BIT);

    assign stop2idle        = state == STOP     &&  end_bit_cnt;


/**************************************************************
                        IIC工作时钟
**************************************************************/
    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_div <= 'd0;						
        else    if(add_div_cnt) begin				
            if(end_div_cnt)						
                cnt_div <= 'd0;  				
            else									
                cnt_div <= cnt_div + 1'b1;		
        end											
    assign add_div_cnt = state != IDLE;
    assign end_div_cnt = add_div_cnt && cnt_div == DIV_CNT_MAX - 1;

    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            iic_scl <= 1;
        else if(cnt_div == (DIV_CNT_MAX - 1) >> 1 || stop2idle)
            iic_scl <= 1;
        else if(end_div_cnt)
            iic_scl <= 0;

/**************************************************************
                        bit计数器
**************************************************************/
    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_bit <= 'd0;						
        else    if(add_bit_cnt) begin				
            if(end_bit_cnt)						
                cnt_bit <= 'd0;  				
            else									
                cnt_bit <= cnt_bit + 1'b1;		
        end											
    assign add_bit_cnt = end_div_cnt;
    assign end_bit_cnt = add_bit_cnt && cnt_bit == cnt_bit_max - 1;

    always@(*)
        case(state)
            IDLE        :   cnt_bit_max = 1;
            START       :   cnt_bit_max = 1;
            WR_DATA     :   cnt_bit_max = 8;
            CHECK_ACK   :   cnt_bit_max = 1;
            RD_DATA     :   cnt_bit_max = 8;
            SEND_ACK    :   cnt_bit_max = 1;
            STOP        :   cnt_bit_max = 1;
            default     :   cnt_bit_max = 1;   
        endcase

/**************************************************************
                        sda三态门控制
**************************************************************/
    assign iic_sda = sda_en? sda_out : 1'bz;
    assign sda_in  = iic_sda;

    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            sda_en <= 0;
        else if(idle2start || state2wr_data || idle2wr_data || check_ack2stop || rd_data2send_ack)
            sda_en <= 1;
        else if(idle2rd_data || wr_data2check_ack || stop2idle)
            sda_en <= 0;

/**************************************************************
                        数据发送
**************************************************************/
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            sda_out <= 1;
        else case(state)
                IDLE        :   sda_out <= 1;
                START       :   if(cnt_div == SCL_LOW_HALF)
                                    sda_out <= 1;
                                else if(cnt_div == SCL_HIGH_HALF)
                                    sda_out <= 0;
                WR_DATA     :   if(cnt_div == SCL_LOW_HALF)
                                    sda_out <= wr_data[7-cnt_bit];    //MSB 先发送高位数据
                SEND_ACK    :   if(cnt_div == SCL_LOW_HALF) begin
                                    if(cmd & `ACK_BIT)              //判断响应位是ACK还是NACK   结果是NACK
                                        sda_out <= `NO_ACK;
                                    else
                                        sda_out <= `ACK;
                                end
                STOP        :   if(cnt_div == SCL_LOW_HALF) //兼容在发送停止位之前sda为1的情况,避免stop位的上升沿无法生效
                                    sda_out <= 0;
                                else if(cnt_div == SCL_HIGH_HALF)
                                    sda_out <= 1;
                default     :   sda_out <= 1; 
            endcase

/**************************************************************
                        数据接收
**************************************************************/
    always@(posedge clk or negedge rst_n)
        if(!rst_n) begin
            rev_data <= 8'd0;
            rev_ack <= 0;
        end
        else case(state)
                CHECK_ACK   :   if(cnt_div == SCL_HIGH_HALF)    //采样从机应答信号
                                    rev_ack <= sda_in;
                RD_DATA     :   if(cnt_div == SCL_HIGH_HALF)    //采样从机的数据
                                    rev_data[7-cnt_bit] <= sda_in;
                default     :   ; 
             endcase

/**************************************************************
                        用户接口输出
**************************************************************/
    
    assign done = send_ack2idle || check_ack2idle || stop2idle;

    assign rd_data = rev_data;
    assign rd_data_vld = send_ack2idle || send_ack2stop;

endmodule


(4)binary2bcd 

// binary2bcd #(.binary_width(5),.bcd_width(8)) binary2bcd_inst(
//     /*input                               */.sys_clk     (sys_clk),
//     /*input                               */.sys_rst_n   (sys_rst_n),
//     /*input       [binary_width - 1 : 0]  */.binary      (),
//     /*input                               */.binary_vliad(),
//     /*output  reg [bcd_width - 1 : 0]     */.bcd         (),
//     /*output  reg                         */.bcd_vliad   ()
// );

module binary2bcd #(
        parameter binary_width = 5,
        parameter bcd_width = 8
    )(
    input                               sys_clk     ,
    input                               sys_rst_n   ,
    input       [binary_width - 1 : 0]  binary      ,
    input                               binary_vliad,
    output  reg [bcd_width - 1 : 0]     bcd         ,
    output  reg                         bcd_vliad   
);

parameter  SHIFT_MAX = binary_width;   //移位次数与二进制的位宽相等

parameter   IDLE    =   3'b001,
            SHIFT   =   3'b010,
            DONE    =   3'b100;

reg     [2:0]   state;

/* 6个BCD编码,对应输出的BCD编码 */
reg     [3:0]   bcd_data0,bcd_data1,bcd_data2,bcd_data3,bcd_data4,bcd_data5;
wire    [3:0]   bcd_temp0,bcd_temp1,bcd_temp2,bcd_temp3,bcd_temp4,bcd_temp5;


//移位次数控制
reg     [binary_width - 1 : 0]  shift_cnt;  //移位的次数,与二进制的位宽相同,可以保证位宽足够使用 
wire                            add_shift_cnt,end_shift_cnt;  
always@(posedge sys_clk or negedge sys_rst_n)
    if(!sys_rst_n)
        shift_cnt <= 'd0;
    else    if(add_shift_cnt) begin
        if(end_shift_cnt)
            shift_cnt <= 'd0;
        else
            shift_cnt <= shift_cnt + 1'b1;
    end

assign add_shift_cnt = state == SHIFT;
assign end_shift_cnt = add_shift_cnt && shift_cnt == SHIFT_MAX - 1;     //达到最大的移位次数

/* 状态机 */
always@(posedge sys_clk or negedge sys_rst_n)
    if(!sys_rst_n)
        state <= IDLE;
    else    case(state)
        IDLE    :   if(binary_vliad == 1'b1)
                        state <= SHIFT;
        SHIFT   :   if(end_shift_cnt)  //达到最大的移位次数
                        state <= DONE;
        DONE    :       state <= IDLE;
        default :       state <= IDLE;
    endcase

//二进制数据移位
reg     [binary_width - 1 : 0]  shitf_data;  
always@(posedge sys_clk or negedge sys_rst_n)
    if(!sys_rst_n)
        shitf_data <= 'd0;
    else    if(state == SHIFT)  //处于移位状态时,每个时钟左移对应计数值
        shitf_data <= shitf_data << 1'b1;
    else                        //其他状态,保持最新的二进制数据
        shitf_data <= binary;

always@(posedge sys_clk or negedge sys_rst_n)
    if(!sys_rst_n) begin
        bcd_data0 <= 4'd0;
        bcd_data1 <= 4'd0;
        bcd_data2 <= 4'd0;
        bcd_data3 <= 4'd0;
        bcd_data4 <= 4'd0;
        bcd_data5 <= 4'd0;
    end
    else    if(state == SHIFT) begin
        bcd_data0 <= {bcd_temp0[2:0],shitf_data[binary_width - 1]};     //更新二进制位宽的最高一位
        bcd_data1 <= {bcd_temp1[2:0],bcd_temp0[3]};     //保存上一个BCD缓冲区溢出的一位     
        bcd_data2 <= {bcd_temp2[2:0],bcd_temp1[3]};     //保存上一个BCD缓冲区溢出的一位
        bcd_data3 <= {bcd_temp3[2:0],bcd_temp2[3]};     //保存上一个BCD缓冲区溢出的一位
        bcd_data4 <= {bcd_temp4[2:0],bcd_temp3[3]};     //保存上一个BCD缓冲区溢出的一位
        bcd_data5 <= {bcd_temp5[2:0],bcd_temp4[3]};     //保存上一个BCD缓冲区溢出的一位    
    end
    else begin
        bcd_data0 <= 4'd0;
        bcd_data1 <= 4'd0;
        bcd_data2 <= 4'd0;
        bcd_data3 <= 4'd0;
        bcd_data4 <= 4'd0;
        bcd_data5 <= 4'd0;
    end

//如果当前的BCD缓冲区的数据大于4,就会将该缓冲区的数据加3
assign bcd_temp0 = (bcd_data0 > 4'd4)? (bcd_data0 + 4'd3) : bcd_data0;
assign bcd_temp1 = (bcd_data1 > 4'd4)? (bcd_data1 + 4'd3) : bcd_data1;
assign bcd_temp2 = (bcd_data2 > 4'd4)? (bcd_data2 + 4'd3) : bcd_data2;
assign bcd_temp3 = (bcd_data3 > 4'd4)? (bcd_data3 + 4'd3) : bcd_data3;
assign bcd_temp4 = (bcd_data4 > 4'd4)? (bcd_data4 + 4'd3) : bcd_data4;
assign bcd_temp5 = (bcd_data5 > 4'd4)? (bcd_data5 + 4'd3) : bcd_data5;

//BCD编码数据输出
always@(posedge sys_clk or negedge sys_rst_n)
    if(!sys_rst_n)
        bcd <= 'd0;
    else    if(state == DONE)
        bcd <= {bcd_data5,bcd_data4,bcd_data3,bcd_data2,bcd_data1,bcd_data0};

//输出一个时钟周期的有效信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(!sys_rst_n)
        bcd_vliad <= 1'b0;
    else    if(state == DONE)
        bcd_vliad <= 1'b1;
    else
        bcd_vliad <= 1'b0;

endmodule

(5)control

/**************************************功能介绍***********************************
Date	: 
Author	: linxiaoxiao.
Version	: 
Description: 实现温度数据转换至bcd码
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module control( 
    input		clk	         ,
    input		rst_n	         ,
    input	[14:0]	hum_data         ,
    input	[14:0]	tem_data         ,
    input		tem_data_vld     ,
    output	[15:0]  tem_bcd          ,   
    output              bcd_tem_data_vld , 
    output	[15:0]  hum_bcd          ,
    output              bcd_hum_data_vld
);								 
//---------<内部信号定义>-----------------------------------------------------
   

binary2bcd #(.binary_width(15),.bcd_width(16)) binary2bcd_inst1(
        /*input                               */.sys_clk     (clk              ),
        /*input                               */.sys_rst_n   (rst_n            ),
        /*input       [binary_width - 1 : 0]  */.binary      (tem_data         ),
        /*input                               */.binary_vliad(tem_data_vld     ),
        /*output  reg [bcd_width - 1 : 0]     */.bcd         (tem_bcd          ),
        /*output  reg                         */.bcd_vliad   (bcd_tem_data_vld )
); 
    
binary2bcd #(.binary_width(15),.bcd_width(16)) binary2bcd_inst2(
        /*input                               */.sys_clk     (clk              ),
        /*input                               */.sys_rst_n   (rst_n            ),
        /*input       [binary_width - 1 : 0]  */.binary      (hum_data         ),
        /*input                               */.binary_vliad(tem_data_vld     ),
        /*output  reg [bcd_width - 1 : 0]     */.bcd         (hum_bcd	       ),
        /*output  reg                         */.bcd_vliad   (bcd_hum_data_vld )
); 
    
    
endmodule

(6)hex2ascii

/**************************************功能介绍***********************************
Date	: 
Author	: linxiaoxiao.
Version	: 
Description: 实现16进制转换成ASCII码
*********************************************************************************/
//hex2ascii  #(
//   .hex_width   ( 24) ,
//   .ascii_width ( 48) 
//)hex2ascii_inst( 
//   /*  input				             */.clk		        (),
//   /*  input				             */.rst_n	        (),
//   /*  input		[hex_width-1:0]	     */.hex_din	        (),
//   /*  input		    	             */.hex_din_vld	    (),
//   /*  output		[ascii_width-1:0]    */.ascii_dout	    (),
//   /*  output		    	             */.ascii_dout_vld	()
//);		
//---------<模块及端口声名>------------------------------------------------------
module hex2ascii#(
    parameter       hex_width   = 24 ,
    parameter       ascii_width = 48 
)( 
    input				                clk		        ,
    input				                rst_n	        ,
    input		[hex_width-1:0]	        hex_din	        ,
    input		    	                hex_din_vld	    ,
    output		[ascii_width-1:0]	    ascii_dout	    ,
    output		    	                ascii_dout_vld	
);								 

//---------<内部信号定义>-----------------------------------------------------
    wire      [ascii_width-1:0]       ascii_data      ;
    wire      [hex_width/4-1:0]         ascii_data_vld  ;


genvar i;
generate
    for (i = 0;i < hex_width/4 ;i = i+1 ) begin : hex_n2ascii_nbyte
        hex4bit2ascii u_hex4bit2ascii (
        .clk		        ( clk		            ),
        .rst_n	            ( rst_n	                ),
        .hex_din	        ( hex_din[i*4+3:i*4]    ),
        .hex_din_vld	    ( hex_din_vld	        ),
        .ascii_dout	        ( ascii_data[i*8+7:i*8]	),
        .ascii_dout_vld     ( ascii_data_vld[i]     )
        );
    end 
endgenerate  


    assign      ascii_dout      = ascii_data     ;
    assign      ascii_dout_vld  = |ascii_data_vld;
 
endmodule


/****************************************************************
                    hex4bit2ascii
****************************************************************/

//---------<模块及端口声名>------------------------------------------------------
module hex4bit2ascii( 
    input               clk             ,
    input               rst_n           ,
    input       [3:0]   hex_din         ,
    input               hex_din_vld     ,
    output      [7:0]   ascii_dout      ,
    output              ascii_dout_vld
);								 

//---------<内部信号定义>-----------------------------------------------------
    reg         [7:0]   ascii_data    ;  
    reg                 ascii_data_vld;

always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        ascii_data <= 'd0;
    end 
    else if(hex_din >= 4'h0 && hex_din <= 4'h9 )begin 
        ascii_data <= hex_din + 8'h30;
    end 
end   
    
always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            ascii_data_vld <= 0;
        end 
        else begin 
            ascii_data_vld <= hex_din_vld;
        end 
    end

    assign ascii_dout = ascii_data;
    assign ascii_dout_vld = ascii_data_vld;

    
    
endmodule

(7)num_proces

/**************************************功能介绍***********************************
Date	: 
Author	: linxiaoxiao.
Version	: 
Description: 数据处理后发送到串口
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module num_proces( 
    input				clk		        ,
    input				rst_n	        ,
    input               tx_ready        ,
    input		[15:0]	tem_bcd_data	,
    input               bcd_tem_data_vld,
    input       [15:0]  hum_bcd_data    ,
    input               bcd_hum_data_vld   ,
    output	reg [7:0]	tx_data	,
    output		        tx_data_vld
);								 
//---------<参数定义>---------------------------------------------------------- 
    //状态机参数定义
    localparam  IDLE   = 'b01,//
                DATA   = 'b10;//

    parameter   NUM_MAX = 27;
//---------<内部信号定义>-----------------------------------------------------
    reg			[4:0]	    cnt_num	   	            ;
    wire					add_cnt_num,end_cnt_num	;
    wire        [63:0]      ascii_data              ;   
    wire                    ascii_data_vld          ;
    reg 	    [1:0]	    state                   ;//现态
/****************************************************************
                    例化
****************************************************************/
hex2ascii#(
   .hex_width   ( 32 ),
   .ascii_width ( 64 ) 
)hex2ascii_inst( 
          .clk		        ( clk		        ),
          .rst_n	        ( rst_n	            ),
          .hex_din	        ({tem_bcd_data,hum_bcd_data}),
          .hex_din_vld	    ( bcd_tem_data_vld  ),
          .ascii_dout	    ( ascii_data        ),
          .ascii_dout_vld	( ascii_data_vld    )
);		
/****************************************************************
                    状态机
****************************************************************/   
    // 时序逻辑描述状态转移
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            state <= IDLE;
        end 
        else case(state)
                IDLE    : if(ascii_data_vld /* && enable */) 
                           state <= DATA;
                DATA    : if(end_cnt_num) 
                           state <= IDLE;
                default :  state <= IDLE;
            endcase
        end
/****************************************************************
                    发送数据计数
****************************************************************/               
    
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_num <= 'd0;
        end 
        else if(add_cnt_num)begin 
            if(end_cnt_num)begin 
                cnt_num <= 'd0;
            end
            else begin 
                cnt_num <= cnt_num + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_num = state == DATA && tx_ready;
    assign end_cnt_num = add_cnt_num && cnt_num == NUM_MAX - 1;
    
 always@(*)
        case(cnt_num)
            0       :   tx_data = 8'h00;
            1       :   tx_data = 8'hce;   //温
            2       :   tx_data = 8'hc2;
            3       :   tx_data = 8'hb6;   //度
            4       :   tx_data = 8'hc8;
            5       :   tx_data = 8'h3A;      // :
            6       :   tx_data = ascii_data[56 +: 8];
            7       :   tx_data = ascii_data[48 +: 8];
            8       :   tx_data = 8'h2E;
            9       :   tx_data = ascii_data[40 +: 8];
            10      :   tx_data = ascii_data[32 +: 8];
            11      :   tx_data = 8'ha1;   //℃
            12      :   tx_data = 8'he6;
            13      :   tx_data = 9;       //\t
            14      :   tx_data = 8'hca;   //湿
            15      :   tx_data = 8'haa;
            16      :   tx_data = 8'hb6;   //度
            17      :   tx_data = 8'hc8;
            18      :   tx_data = 8'h3A;      // :
            19      :   tx_data = ascii_data[24 +: 8];
            20      :   tx_data = ascii_data[16 +: 8];
            21      :   tx_data = 8'h2E;
            22      :   tx_data = ascii_data[ 8 +: 8];
            23      :   tx_data = ascii_data[ 0 +: 8];
            24      :   tx_data = "R";     
            25      :   tx_data = "H";
            26      :   tx_data = 8'h0d;
            default :   tx_data = 0;
        endcase

    assign  tx_data_vld = state == DATA;

endmodule

(8)tx_fsm_uart

/**************************************功能介绍***********************************
Date	: 
Author	: linxiaoxiao.
Version	: 
Description: uart数据发送端
*********************************************************************************/
//tx_fsm_uart#(
//        .CHECK_BIT (  "Odd"     ) ,  //校验方法,“None”无校验,“Odd”奇校验,“Even”偶校验
//        .BPS       ( 115200     ) ,
//        .CLK       ( 50_000_000 )
//    )tx_fsm_uart_inst( 
//    /* input				 */.clk		    (clk	    ),
//    /* input				 */.rst_n	    (rst_n	    ),
//    /* input				 */.tx_data_vld (   ),
//    /* input		[7:0]	 */.tx_data	    (   ),  
//    /* output	    	     */.ready	    (ready	    ),
//    /* output	reg	         */.tx          (tx         )	
//);			

//---------<模块及端口声名>------------------------------------------------------
module tx_fsm_uart#(
    parameter           CHECK_BIT = "Odd",  //校验方法,“None”无校验,“Odd”奇校验,“Even”偶校验
    parameter           BPS = 115200,
    parameter           CLK = 50_000_000 
)( 
    input				clk		    ,
    input				rst_n	    ,
    input				tx_data_vld	,
    input		[7:0]	tx_data	    ,  
    output	    	    ready	    ,
    output	reg	        tx	
);								    
//---------<参数定义>--------------------------------------------------------- 
parameter   BPS_MAX = CLK/BPS;
//---------<内部信号定义>-----------------------------------------------------
//状态机参数定义
localparam  IDLE   = 5'b00001,//空闲
            STARE  = 5'b00010,//起始位
            DATA   = 5'b00100,//数据位
            CHECK  = 5'b01000,
            STOP   = 5'b10000;//停止位

wire            check_vld   ;

reg 	[4:0]	state_c     ;//现态
reg	    [4:0]	state_n     ;//次态

wire            idle2stare  ;
wire            stare2data  ;
wire            data2stop   ;
wire            data2check  ;
wire            check2stop  ;
wire            stop2idle   ;

reg		[12:0]	bps_cnt	   	;
wire			add_bps_cnt,end_bps_cnt	;

reg     [3:0]   bit_max     ;
reg     [7:0]   tx_data_r   ;

reg		[3:0]	bit_cnt	   	;
wire			add_bit_cnt,end_bit_cnt	;

//****************************************************************
//--fsm
//****************************************************************
//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        state_c <= IDLE;
    end 
    else begin 
        state_c <= state_n;
    end 
end

//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
    case(state_c)
        IDLE  : if (idle2stare) begin
                    state_n = STARE;
                end
                else begin
                    state_n = state_c;
                end
        STARE : if (stare2data) begin
                    state_n = DATA;
                end
                else begin
                    state_n = state_c;
                end
        DATA  : if (data2check) begin
                    state_n = CHECK;
                end
                else if (data2stop) begin
                    state_n = STOP;
                end
                else begin
                    state_n = state_c;
                end
        CHECK : if (check2stop) begin
                    state_n = STOP;
                end
                else begin
                    state_n = state_c;
                end
        STOP  : if (stop2idle) begin
                    state_n = IDLE;
                end
                else begin
                    state_n = state_c;
                end
        default : state_n = state_c;
    endcase
end

assign          idle2stare = state_c == IDLE  && tx_data_vld ;
assign          stare2data = state_c == STARE && end_bit_cnt ;
assign          data2check = state_c == DATA  && end_bit_cnt && CHECK_BIT != "None";
assign          data2stop  = state_c == DATA  && end_bit_cnt && CHECK_BIT == "None";
assign          check2stop = state_c == CHECK && end_bit_cnt ;   
assign          stop2idle  = state_c == STOP  && end_bit_cnt ;                     

//****************************************************************
//--波特计数器
//****************************************************************   
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            bps_cnt <= 'd0;
        end 
        else if(add_bps_cnt)begin 
            if(end_bps_cnt)begin 
                bps_cnt <= 'd0;
            end
            else begin 
                bps_cnt <= bps_cnt + 1'b1;
            end 
        end
    end 
    
    assign add_bps_cnt = state_c != IDLE;
    assign end_bps_cnt = add_bps_cnt && bps_cnt == BPS_MAX - 1;

//****************************************************************
//--比特计数器
//****************************************************************   
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            bit_cnt <= 'd0;
        end 
        else if(add_bit_cnt)begin 
            if(end_bit_cnt)begin 
                bit_cnt <= 'd0;
            end
            else begin 
                bit_cnt <= bit_cnt + 1'b1;
            end 
        end
    end 
    
    assign add_bit_cnt = end_bps_cnt;
    assign end_bit_cnt = add_bit_cnt && bit_cnt == bit_max - 1;

    always @(*) 
        case(state_c )
            IDLE    : bit_max = 1 ;
            STARE   : bit_max = 1 ;
            DATA    : bit_max = 8 ;
            CHECK   : bit_max = 1 ;
            STOP    : bit_max = 1 ;
            default : bit_max = 1 ;
        endcase
 /****************************************************************
                    数据校验
 ****************************************************************/  

//奇校验缩位同或
/*         assign      check_vld = ~^tx_data_r; */

//偶校验缩位异或
 /* assign        check_vld = ^tx_data_r; */
 assign     check_vld = (CHECK_BIT == "Odd")?~^tx_data_r:^tx_data_r;

//****************************************************************
//--输入数据寄存
//****************************************************************
    always @(posedge clk or negedge rst_n)
        if(!rst_n)begin
             tx_data_r <= 0;
        end 
        else if(tx_data_vld)
            tx_data_r <= tx_data;

/****************************************************************
                    串口时序   
****************************************************************/ 
    always @(*) 
    case(state_c )
        IDLE    : tx = 1 ;
        STARE   : tx = 0 ;
        DATA    : if(tx_data_r[bit_cnt])
                        tx = 1;
                  else
                        tx = 0; 
        CHECK   : tx <= check_vld ;
        STOP    : tx = 1 ;  
        default : tx = 1 ;
    endcase
 
    assign ready = state_c == IDLE;

endmodule

六、仿真波形图

   状态机上电进入WAIT状态,等待40ms后进入初始化状态,(如图一所示),进入初始化状态,发送 00011 接口状态机的跳转条件,然后发送70,e1.08.00,对 AHT10进行初始化操作,完成后进入空闲状态。空闲状态直接进入到RD_REQ状态测量指令发送,cmd 的数据为 00011,发送的数据为70,AC,33.00,这是(仿真波形图二的第二个区域),发送完测量指令以后等待 80ms测量的完成,然后进入数据的读取,发送 71 指今,并读取 6 个 8bit 的数据,完成以后状态机进入 DEALY状态,延时 2S 以后进入下一次的温度数据的读取,串口显示为2s更新一次数据。 

七、板级验证效果

串口每隔2s接受到一组温湿度数据,板级验证结果与实验要求一致

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AHT10湿度传感器是一种可以通过FPGA驱动的传感器,它可以测量环境中的度和湿度。通过使用Verilog语言,我们可以编写代码来控制AHT10传感器,并将测量结果显示在数码管上。前四个数码管显示湿度,后四个数码管显示度。 在使用AHT10传感器时,可能会遇到一些乱码的问题,特别是在处理中文和特殊符号时。不过,即使出现乱码,我们仍然可以正常显示湿度值。需要注意的是,为了模拟湿度值,可以使用随机数进行模拟。 AHT10传感器有几种状态,其中包括空闲状态和触发测量命令状态。在初始化完成后,传感器会进入空闲状态,持续0.5秒后,将进入触发测量命令状态,即开始测量湿度值。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [用FPGA驱动ATH10湿度传感器](https://download.csdn.net/download/weixin_51647652/14949582)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [基于FPGAAHT10传感器湿度读取](https://blog.csdn.net/qq_47281915/article/details/126147081)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值