基于verilog驱动ATH10温湿度传感器

一、实验原理

1、IIC原理

        IIC串行总线由两根信号线组成,一根是双向的数据线SDA,另一根是单向的时钟线SCL,在空闲状态时,SDA和SCL线都置‘1’,IIC为同步的半双工通信方式为高电平。

(1)开始信号

       IIC开始条件,在SCL高电平期间,主机将SDA线拉低,表示数据传输的开始信号。

(2)结束信号

      IIC结束条件,在SCL为高电期间,主机将SDA线拉高,表示数据传输的结束信号。

(3)数据保持

        IIC在数据传输的过程中,数据在SCL的低电平期间发生改变,在高电平期间数据应该保持,此时认为在SCL的高电平期间数据有效。

(4)IIC写时序过程

        在写数据的过程中,先要发送从机的设备地址和读写位,等待从机应答后再发送需要写入数据的寄存器地址,再等到从机应答后才开始写数据。每写完一个数据都需要等到从机的应答表示从机接收到了(可以写一个数据,也可以连续写数据)。

(5)IIC读时序过程

        在读数据的过程中,要先写入从机的设备地址(读写位W = 0)和需要读取的寄存器地址,此过程为虚写过程,都需要从机应答。然后再发送开始信号,从机设备地址,读写位为R = 1,等待从机应答后,开始接收从机发来的数据。主机每接收到一个数据都需要系那个从机发送一个应答信号,若主机停止接收数据后,需要向从机发送一个非应答信号(可以读一个数据,也可以连续读多个数据)。

2、AHT10硬件原理

        AHT10采取标准的IIC协议进行通讯,每一个传输序列都以START状态作为开始并且以STOP状态作为结束。

        在启动传输后,随后传输IIC的首字节包括7位的IIC设备地址0x38和读写位(W--0、R--1),每次主机写数据完成后,传感器都会将SDA线拉低表示应答主机数据传输正常。

(1)发送命令

        在启动传输后,随后传输的I2C首字节包括7位的I2C设备地址0x38和一个SDA方向位x(读R:‘1',写W:‘0)。在第8个SCL时钟下降沿之后,通过拉低SDA引脚(ACK位),指示传感器数据接收正常。在发出初始化命令之后(‘1110'0001’代表初始化,‘1010’1100'代表温湿度测量),MCU必须等待测量完成。基本的命令在表9中进行概述。表10为从机返回的状态位说明。

 

(2)传感器读取流程

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

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

        3.等待80ms待测量完成,忙状态Bit[7]为0,然后可以读取六个字节(发0X71即可以读取)

        注:传感器在采集时需要时间,主机发出测量指令(0xAC)后,延时80毫秒以上再读取转换后的数据并判断返回的状态位是否正常。若状态比特位[Bit7]为0代表数据可正常读取,为1时传感器为忙状态,主机需要等待数据处理完成。

(3)相对湿度转换

(4)温度转换

二、系统架构设计

系统框图

IIC状态机

uart_tx状态机

三、模块说明

1、模块端口信号列表

(1)顶层模块端口信号

信号

I/O

类型

位宽

clk

input

wire

1

rst_n

input

wire

1

sda

inout

wire

1

sclk

output

wire

1

uart_tx

output

wire

1

sel

output

wire

6

dig

output

wire

8

(2)IIC模块端口信号

信号

I/O

类型

位宽

clk

input

wire

1

rst_n

input

wire

1

sda

output

reg

1

sclk

inout

wire

1

humi

output

reg

20

temp

output

reg

20

rd_done

output

reg

1

        该模块主要的功能时实现对AHT10温湿度传感器的操作,对AHT10的相关指令的操作,包括初始化、温湿度测量、温湿度数据的读取。其中,humi为AHT10采集到的湿度数据,temp为采集到的温度数据。rd_done为读取温湿度数据过程的结束信号。

(3)data_handle模块端口信号

信号

I/O

类型

位宽

clk

input

wire

1

rst_n

input

wire

1

humi

input

wire

20

temp

input

wire

20

rd_start

input

wire

1

tx_start

output

reg

1

data_out

output

reg

8

        本模块主要是对IIC模块读取到AHT10中温湿度数据进行数据转换处理,并将处理好的数据发送给串口发送模块uart_tx,将读取到的温湿度数据发送给上位机。

(4)uart_tx模块端口信号

信号

I/O

类型

位宽

clk

input

wire

1

rst_n

input

wire

1

data_in

input

wire

8

tx_start

intput

wire

1

uart_tx

output

reg

1

       在本模块中,data_in接收来自data_handle模块处理完的温湿度数据,并将读到的温度数据发送给上位机电脑。

(5)nieix_tube模块端口信号

信号

I/O

类型

位宽

clk

input

wire

1

rst_n

input

wire

1

humi

input

wire

20

temp

intput

wire

20

sel

output

reg

6

dig

output

reg

8

该模块主要将IIC读取到的温湿度数据先用数码管显示出来。

2、时序图

 

        i2c_clk的时序图,将clk系统时钟进行分频,得到一个时钟周期为1us的i2c_clk时钟,该时钟作为i2c模块相关信号的时钟信号。

 

        以分频得到的i2c_clk时钟作为时钟源,cnt_sclk为sclk的分频计数器,sclk的时钟周期为4us,频率为250k。sclk在cnt_bit为1和2时为高,在0和3时为低,sda在sclk的低电平期间,也就是cnt_sclk为0或3时发生数据的改变;在sclk为高电平期间,也就是cnt_sclk为1和2时数据保持。

四、代码片段

1、AHT10顶层模块

module AHT10(
    input               clk     ,//系统时钟
    input               rst_n   ,//复位

    inout               sda     ,//SDA线
    output              sclk    ,//SCLK线
    output      [5:0]   sel     ,//数码管位选
    output      [7:0]   dig     ,//数码管段选
    output              uart_tx  //Tx端
);

wire [19:0] humi    ;
wire [19:0] temp    ;
wire        rd      ;
wire [07:0] data    ;
wire        tx_start;

 i2c i2c_inst(
    /* input                    */.clk    (clk    ),//时钟   
    /* input                    */.rst_n  (rst_n  ),//复位   
    /* inout                    */.sda    (sda    ),//sda
    /* output  reg              */.sclk   (sclk   ),//sclk
    /* output  reg     [19:0]   */.humi   (humi   ),//湿度
    /* output  reg     [19:0]   */.temp   (temp   ),//温度
    /* output  reg              */.rd_done(rd     ) //读数据结束
);

nixie_tube nixie_tube_inst(
    /* input                     */.clk   (clk   ),//时钟
    /* input                     */.rst_n (rst_n ),//复位
    /* input           [19:0]    */.humi  (humi  ),//读取到的温度数据输出
    /* input           [19:0]    */.temp  (temp  ),//读取到的湿度数据输出
    /* output  reg     [05:0]    */.sel   (sel   ),//数码管位选
    /* output  reg     [07:0]    */.dig   (dig   ) //数码管段选
);

data_handle data_handle_inst(
    /* input                      */.clk     (clk     ),//时钟
    /* input                      */.rst_n   (rst_n   ),//复位
    /* input                      */.rd_start(rd      ),//与rd_done连接
    /* input           [19:0]     */.temp    (temp    ),//湿度数据
    /* input           [19:0]     */.humi    (humi    ),//温度数据
    /* output  reg                */.tx_start(tx_start),//Tx端开始传输使能信号
    /* output  reg     [07:0]     */.data_out(data    ) //数据处理完成后的数据 
);

uart_tx uart_tx_inst(
    /* input                      */.clk     (clk     ),//时钟
    /* input                      */.rst_n   (rst_n   ),//复位
    /* input           [07:0]     */.data_in (data    ),//接收数据处理好的数据
    /* input                      */.tx_start(tx_start),//Tx开始传输的使能信号
    /* output  reg                */.uart_tx (uart_tx ) //Tx端发送的温湿度数据 
);

endmodule

2、i2c模块

module i2c (
    input                   clk     ,   //时钟
    input                   rst_n   ,   //复位

    inout                   sda     ,   //sda双向数据线
    output  reg             sclk    ,   //sclk时钟线
    output  reg     [19:0]  humi    ,   //20位湿度数据
    output  reg     [19:0]  temp    ,   //20位温度数据
    output  reg             rd_done     //读取温湿度数据结束
);

/************************************参数变量定义*********************************
*********************************************************************************/
parameter   CLK_CNT = 25; //i2c_clk时钟周期为1000ns = 1us sclk时钟周期为4000ns = 4us 频率为250k
parameter   IDLE    = 'd1,//初始状态,此状态下等待40ms,进入WAIT_40
            WAIT_40 = 'd2,//此状态下,发送0x71,读取状态数据Bit[3]的数据值
            CHECK   = 'd3,//发送0x70,Bit[3]为1,发送0xAC、0x33、0x00跳转到MEASURE;为0,发送0xE1、0x08、0x00跳转到INIT
            INIT    = 'd4,//发送ox70\0xAC、0x33、0x00跳转到MEASURE
            MEASURE = 'd5,//此状态下,等待80ms,温湿度数据测量完成,跳转到READ
            READ    = 'd6,//发送0x71,读取7字节数据,第一个字节为状态数据,后6个字节为温湿度数据
            DONE    = 'd7;//此状态下等待1s,进入下一轮的温湿度数据的测量与采集

parameter   IDWR    =  8'h70,
            CMD_71  =  8'h71,
            CMD_AC  =  8'hAC,
            CMD_33  =  8'h33,
            CMD_00  =  8'h00,
            CMD_E1  =  8'hE1,
            CMD_08  =  8'h08,
            IDRD    =  8'h71;

reg [05:0]  cnt_clk     ;//分频时钟计数器
wire        add_cnt_clk ;
wire        end_cnt_clk ;
reg         i2c_clk     ;//分频时钟

reg [01:0]  cnt_sclk    ;//sclk时钟计数器,在i2c_clk的时钟下进行计数
wire        add_cnt_sclk;
wire        end_cnt_sclk;

reg [16:0]  cnt_wait1   ;//延迟40ms计数器
reg [16:0]  cnt_wait2   ;//延迟80ms计数器
reg [19:0]  cnt_wait3   ;//延迟1s再次测量读取
reg [06:0]  cnt_bit     ;//最大65 start + cmd + ack + data + ack/nack + stop
wire        add_cnt_bit;
wire        end_cnt_bit;
reg [06:0]  BIT_CNT    ;//cnt_bit计数到最大的值 

reg [03:0]  state_c     ;
reg [03:0]  state_n     ;

reg         skip_en     ;//状态跳转条件
reg         flag        ;//flag 判断是否初始化
reg [19:0]  data_reg1   ;//缓存湿度数据
reg [19:0]  data_reg2   ;//缓存温度数据

wire        sda_in      ;
reg         sda_en      ;
reg         sda_out     ;

/************************************i2c_clk设计*********************************
*********************************************************************************/
always @(posedge clk or negedge rst_n)begin //计数25次,周期25x20 500ns
    if(!rst_n) begin
        cnt_clk <= 'd0;
    end 
    else if(add_cnt_clk) begin 
        if(end_cnt_clk) begin 
            cnt_clk <= 'd0;
        end
        else begin 
            cnt_clk <= cnt_clk + 'd1;
        end 
    end
end 
assign add_cnt_clk = 1;
assign end_cnt_clk = add_cnt_clk && cnt_clk == CLK_CNT - 1;

always @(posedge clk or negedge rst_n) begin //每500ns取反,i2c_clk周期为1ms
    if(!rst_n) begin
        i2c_clk <= 1'd1;
    end 
    else if(end_cnt_clk) begin 
        i2c_clk <= ~i2c_clk;
    end 
    else begin 
        i2c_clk <= i2c_clk;
    end 
end

/***********************************延迟计数器设计********************************
*********************************************************************************/
always @(posedge i2c_clk or negedge rst_n) begin //在IDLE状态下等待40ms进入WAIT_40
    if(!rst_n) begin
        cnt_wait1 <= 'd0;
    end 
    else if(cnt_wait1 == 40000 - 1) begin 
        cnt_wait1 <= 'd0;
    end 
    else if(state_c == IDLE)begin 
        cnt_wait1 <= cnt_wait1 + 'd1;
    end 
    else begin
        cnt_wait1 <= cnt_wait1;
    end
end

always @(posedge i2c_clk or negedge rst_n) begin //在MEASURE状态下等待80ms温湿度转换完成,进入READ
    if(!rst_n) begin
        cnt_wait2 <= 'd0;
    end 
    else if(cnt_wait2 == 80000 - 1) begin 
        cnt_wait2 <= 'd0;
    end 
    else if(state_c == MEASURE)begin 
        cnt_wait2 <= cnt_wait2 + 'd1;
    end 
    else begin
        cnt_wait2 <= cnt_wait2;
    end
end

always @(posedge i2c_clk or negedge rst_n) begin //在DONE状态下计数1s进入下一次数据的测量与读取
    if(!rst_n) begin
        cnt_wait3 <= 'd0;
    end 
    else if(cnt_wait3 == 1000000 - 1) begin 
        cnt_wait3 <= 'd0;
    end 
    else if(state_c == DONE)begin 
        cnt_wait3 <= cnt_wait3 + 'd1;
    end 
    else begin
        cnt_wait3 <= cnt_wait3;
    end
end

/***********************************cnt_bit设计*********************************
*********************************************************************************/
always @(posedge i2c_clk or negedge rst_n)begin //只有在WAIT_40、CHECK、INIT、READ才进行数据的发送或者接收
    if(!rst_n) begin
        cnt_bit <= 'd0;
    end 
    else if(add_cnt_bit) begin 
        if(end_cnt_bit) begin 
            cnt_bit <= 'd0;
        end
        else begin 
            cnt_bit <= cnt_bit + 'd1;
        end 
    end
end 
assign add_cnt_bit = end_cnt_sclk && (state_c == WAIT_40 || state_c == CHECK || state_c == INIT || state_c == READ);    
assign end_cnt_bit = add_cnt_bit && cnt_bit == BIT_CNT - 1;

always @(posedge i2c_clk or negedge rst_n) begin 
    if(!rst_n) begin
        BIT_CNT <= 'd0;
    end 
    else if(state_c == IDLE && skip_en) begin //WAIT_40状态下发送设备ID、0x71 接收数据查看Bit[3]
        BIT_CNT <= 'd20;
    end 
    else if((state_c == WAIT_40 || state_c == CHECK) && skip_en) begin //Bit[3] = 1,CHECK状态下发送0xAC(0x33、0x00)进入MEASURE
        BIT_CNT <= 'd38;                                               //Bie[3] = 0,CHECK状态下发送0xE1(0x08、0x00)进入INIT
    end                                                                //INIT状态下发送0xAC(0x33、0x00)进入MEASURE
    else if(state_c == MEASURE && skip_en) begin //读数据        
        BIT_CNT <= 'd65;
    end
    else begin 
        BIT_CNT <= BIT_CNT;
    end 
end

/*************************************sda设计************************************
*********************************************************************************/
assign  sda_in = sda;
assign  sda = sda_en ? sda_out : 'hz;

always @(*) begin 
    if(!rst_n) begin
        sda_en  = 'd0;
        sda_out = 'd1;
    end
    else begin
        case(state_c)
            WAIT_40:begin
                    if(cnt_bit == 0) begin  //起始位
                        if(cnt_sclk == 'd0 || cnt_sclk == 'd1) begin
                            sda_out = 'd1;
                            sda_en = 'd1;
                        end
                        else begin
                            sda_out = 'd0;
                            sda_en = 'd1;
                        end
                    end
                    else if(cnt_bit >= 1 && cnt_bit <= 8) begin //主机发送CMD_71
                        sda_out = CMD_71[8 - cnt_bit];
                        sda_en  = 'd1; 
                    end
                    else if(cnt_bit == 9) begin //接收从机应答
                        sda_out = 'd1;
                        sda_en  = 'd0;
                    end
                    else if(cnt_bit >= 10 && cnt_bit <= 17) begin //接收从机数据
                        sda_out = 'd1;
                        sda_en  = 'd0;
                    end
                    else if(cnt_bit == 18) begin //主机发送nack
                        sda_out = 'd1;
                        sda_en  = 'd1;
                    end
                    else begin  //停止位
                        if(cnt_sclk == 'd0 || cnt_sclk == 'd1) begin
                            sda_out = 'd0;
                            sda_en  = 'd1;
                        end
                        else begin
                            sda_out = 'd1;
                            sda_en  = 'd1;
                        end
                    end
            end
            CHECK  :begin
                    if(cnt_bit == 0) begin  //起始位
                        if(cnt_sclk == 'd0 || cnt_sclk == 'd1) begin
                            sda_out = 'd1;
                            sda_en = 'd1;
                        end
                        else begin
                            sda_out = 'd0;
                            sda_en = 'd1;
                        end
                    end
                    else if(cnt_bit >= 1 && cnt_bit <= 8) begin //主机发送IDWR 
                        sda_out = IDWR[8 - cnt_bit];
                        sda_en  = 'd1; 
                    end
                    else if(cnt_bit >= 10 && cnt_bit <= 17) begin //flag为1主机发送0xAC,为0发送0xE1
                        if(flag) begin //flag寄存Bit[3]的数据状态
                            sda_out = CMD_AC[17 - cnt_bit];
                            sda_en  = 'd1; 
                        end
                        else begin
                            sda_out = CMD_E1[17 - cnt_bit];
                            sda_en  = 'd1;
                        end
                    end
                    else if(cnt_bit == 9 || cnt_bit == 18 || cnt_bit == 27 || cnt_bit == 36) begin //接收从机应答
                        sda_out = 'd1;
                        sda_en  = 'd0;
                    end
                    else if(cnt_bit >= 19 && cnt_bit <= 26) begin //flag为1主机发送0x33,为0发送0x08
                        if(flag) begin
                            sda_out = CMD_33[26 - cnt_bit];
                            sda_en  = 'd1;
                        end
                        else begin
                            sda_out = CMD_08[26 - cnt_bit];
                            sda_en  = 'd1;
                        end
                    end
                    else if(cnt_bit >= 28 && cnt_bit <= 35) begin //主机发送0x00
                        sda_out = CMD_00[35 - cnt_bit];
                        sda_en  = 'd1;
                    end
                    else begin  //停止位
                        if(cnt_sclk == 'd0 || cnt_sclk == 'd1) begin
                            sda_out = 'd0;
                            sda_en  = 'd1;
                        end
                        else begin
                            sda_out = 'd1;
                            sda_en  = 'd1;
                        end
                    end
            end
            INIT   :begin
                    if(cnt_bit == 0) begin  //起始位
                        if(cnt_sclk == 'd0 || cnt_sclk == 'd1) begin
                            sda_out = 'd1;
                            sda_en = 'd1;
                        end
                        else begin
                            sda_out = 'd0;
                            sda_en = 'd1;
                        end
                    end
                    else if(cnt_bit >= 1 && cnt_bit <= 8) begin //主机发送IDWR
                        sda_out = IDWR[8 - cnt_bit];
                        sda_en  = 'd1; 
                    end
                    else if(cnt_bit >= 10 && cnt_bit <= 17) begin //主机发送0xAC
                        sda_out = CMD_AC[17 - cnt_bit];
                        sda_en  = 'd1; 
                    end
                    else if(cnt_bit == 9 || cnt_bit == 18 || cnt_bit == 27 || cnt_bit == 36) begin //接收从机应答
                        sda_out = 'd1;
                        sda_en  = 'd0;
                    end
                    else if(cnt_bit >= 19 && cnt_bit <= 26) begin //主句发送0x33
                        sda_out = CMD_33[26 - cnt_bit];
                        sda_en  = 'd1;
                    end
                    else if(cnt_bit >= 28 && cnt_bit <= 35) begin //主机发送0x00
                        sda_out = CMD_00[35 - cnt_bit];
                        sda_en  = 'd1;
                    end
                    else begin  //停止位
                        if(cnt_sclk == 'd0 || cnt_sclk == 'd1) begin
                            sda_out = 'd0;
                            sda_en  = 'd1;
                        end
                        else begin
                            sda_out = 'd1;
                            sda_en  = 'd1;
                        end
                    end
            end
            READ   :begin
                    if(cnt_bit == 0) begin  //起始位
                        if(cnt_sclk == 'd0 || cnt_sclk == 'd1) begin
                            sda_out = 'd1;
                            sda_en = 'd1;
                        end
                        else begin
                            sda_out = 'd0;
                            sda_en = 'd1;
                        end
                    end
                    else if(cnt_bit >= 1 && cnt_bit <= 8) begin //主机发送IDRD
                        sda_out = IDRD[8 - cnt_bit];
                        sda_en  = 'd1; 
                    end
                    else if(cnt_bit == 9) begin //从机应答
                        sda_out = 'd1;
                        sda_en  = 'd0;
                    end
                    else if(cnt_bit == 18 || cnt_bit == 27 || cnt_bit == 36 || cnt_bit == 45 || cnt_bit == 54) begin
                        sda_out = 'd0;  //主机发送ack
                        sda_en  = 'd1;
                    end
                    else if(cnt_bit == 63) begin  //主机发送nack
                        sda_out = 'd1;
                        sda_en  = 'd1;
                    end
                    else if(cnt_bit == 64) begin  //结束位  
                        if(cnt_sclk == 'd0 || cnt_sclk == 'd1) begin
                            sda_out = 'd0;
                            sda_en  = 'd1;
                        end
                        else begin
                            sda_out = 'd1;
                            sda_en  = 'd1;
                        end
                    end
                    else begin  //接收数据 6个字节
                        sda_out = 'd1;
                        sda_en  = 'd0;
                    end

            end
            default:begin
                        sda_out = 'd1;
                        sda_en  = 'd1;
            end         
        endcase
    end
end

always @(posedge i2c_clk or negedge rst_n) begin 
    if(!rst_n) begin
        flag <= 'd0;
    end 
    else if(state_c == WAIT_40) begin //当flag为1时,不用初始化,为0时,进行初始化
        if(cnt_bit == 14) begin //状态数据 Bit[3]数据在这个状态下的第14个bit数据
            if(cnt_sclk == 'd1 || cnt_sclk == 'd2)
                flag <= sda_in;
            else
                flag <= flag;
        end
    end
    else if(state_c == READ) begin
        flag <= 'd0;
    end
    else begin 
        flag <= flag;
    end 
end

/********************************humi、temp设计**********************************
*********************************************************************************/
always @(posedge i2c_clk or negedge rst_n) begin 
    if(!rst_n) begin
        data_reg1 <= 'd0;
    end 
    else if(state_c == READ) begin //对湿度数据进行逐位寄存
        if(cnt_bit >= 19 && cnt_bit <= 26 || cnt_bit >= 28 && cnt_bit <= 35 || cnt_bit >= 37 && cnt_bit <= 40) begin
            if(cnt_sclk == 'd2)
                data_reg1 <= {data_reg1[18:0],sda_in};
            else begin
                data_reg1 <= data_reg1;
            end
        end
        else if(cnt_bit == 64) begin //读数据结束清零
            data_reg1 <= 'd0;
        end
        else begin
            data_reg1 <= data_reg1;
        end
    end 
    else begin 
        data_reg1 <= 'd0;
    end 
end

always @(posedge i2c_clk or negedge rst_n) begin 
    if(!rst_n) begin
        humi <= 'd0;
    end 
    else if(state_c == READ && cnt_bit == 63 && cnt_sclk == 'd2) begin //主机发送非应答输出湿度数据
        humi <= data_reg1;
    end 
    else begin 
        humi <= humi;
    end 
end

always @(posedge i2c_clk or negedge rst_n) begin 
    if(!rst_n) begin
        data_reg2 <= 'd0;
    end 
    else if(state_c == READ) begin //对温度数据进行逐位寄存
        if(cnt_bit >= 41 && cnt_bit <= 44 || cnt_bit >= 46 && cnt_bit <= 53 || cnt_bit >= 55 && cnt_bit <= 62) begin
            if(cnt_sclk == 'd2)
                data_reg2 <= {data_reg2[18:0],sda_in};
            else begin
                data_reg2 <= data_reg2;
            end
        end
        else if(cnt_bit == 64) begin //读数据结束清零
            data_reg2 <= 'd0;
        end
        else begin
            data_reg2 <= data_reg2;
        end
    end 
    else begin 
        data_reg2 <= 'd0;
    end 
end

always @(posedge i2c_clk or negedge rst_n) begin 
    if(!rst_n) begin
        temp <= 'd0;
    end 
    else if(state_c == READ && cnt_bit == 63 && cnt_sclk == 'd2) begin //主机发送非应答输出温度数据
        temp <= data_reg2;
    end 
    else begin 
        temp <= temp;
    end 
end

always @(posedge clk or negedge rst_n) begin //在READ读数据状态结束时,rd_done拉高
    if(!rst_n) begin
        rd_done <= 'd0;
    end 
    else if(state_c == READ && cnt_bit == 63 && cnt_sclk == 'd2) begin 
        rd_done <= 'd1;
    end 
    else begin 
        rd_done <= 'd0;
    end 
end

/*************************************sclk设计***********************************
*********************************************************************************/
always @(posedge i2c_clk or negedge rst_n)begin 
    if(!rst_n) begin
        cnt_sclk <= 'd0;
    end 
    else if(add_cnt_sclk) begin 
        if(end_cnt_sclk) begin 
            cnt_sclk <= 'd0;
        end
        else begin 
            cnt_sclk <= cnt_sclk + 'd1;
        end 
    end
end                         //只有在以下几个状态,i2c总线才处于工作状态
assign add_cnt_sclk = state_c == WAIT_40 || state_c == CHECK || state_c == INIT || state_c == READ;
assign end_cnt_sclk = add_cnt_sclk && cnt_sclk == 4 - 1;

always @(posedge i2c_clk or negedge rst_n) begin 
    if(!rst_n) begin
        sclk <= 'd1;
    end 
    else begin 
        case(state_c)
            WAIT_40:begin
                    if(cnt_bit == 0) begin //起始位
                        if(cnt_sclk == 'd2 || cnt_sclk == 'd3)
                            sclk <= 'd0;
                        else
                            sclk <= 'd1;
                    end
                    else if(cnt_bit == 20 - 1) begin //结束位
                        sclk <= 'd1;
                    end
                    else begin //进行指令的发送,应答,数据的接收
                        if(cnt_sclk == 'd0 || cnt_sclk == 'd1)
                            sclk <= 'd1;
                        else
                            sclk <= 'd0;
                    end
            end
            CHECK  :begin
                    if(cnt_bit == 0) begin //起始位
                        if(cnt_sclk == 'd2 || cnt_sclk == 'd3)
                            sclk <= 'd0;
                        else
                            sclk <= 'd1;
                    end
                    else if(cnt_bit == 38 - 1) begin //结束位
                        sclk <= 'd1;
                    end
                    else begin //进行指令的发送,应答
                        if(cnt_sclk == 'd0 || cnt_sclk == 'd1)
                            sclk <= 'd1;
                        else
                            sclk <= 'd0;
                    end
            end
            INIT   :begin
                    if(cnt_bit == 0) begin //起始位
                        if(cnt_sclk == 'd2 || cnt_sclk == 'd3)
                            sclk <= 'd0;
                        else
                            sclk <= 'd1;
                    end
                    else if(cnt_bit == 38 - 1) begin //结束位
                        sclk <= 'd1;
                    end
                    else begin //进行指令的发送,应答
                        if(cnt_sclk == 'd0 || cnt_sclk == 'd1)
                            sclk <= 'd1;
                        else
                            sclk <= 'd0;
                    end
            end
            READ   :begin
                    if(cnt_bit == 0) begin //起始位
                        if(cnt_sclk == 'd2 || cnt_sclk == 'd3)
                            sclk <= 'd0;
                        else
                            sclk <= 'd1;
                    end
                    else if(cnt_bit == 65 - 1) begin //结束位
                        sclk <= 'd1;
                    end
                    else begin //进行指令的发送,应答
                        if(cnt_sclk == 'd0 || cnt_sclk == 'd1)
                            sclk <= 'd1;
                        else
                            sclk <= 'd0;
                    end
            end
            default:sclk <= 'd1;
        endcase
    end 
end

/*************************************状态机设计**********************************
*********************************************************************************/
always @(posedge i2c_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   :begin
                if(skip_en) begin
                    state_n = WAIT_40;
                end
                else begin
                    state_n = state_c;
                end
        end
        WAIT_40:begin
                if(skip_en) begin
                    state_n = CHECK;
                end
                else begin
                    state_n = state_c;
                end
        end
        CHECK  :begin
                if(skip_en && flag == 'd0) begin
                    state_n = INIT;
                end
                else if(skip_en && flag == 'd1) begin
                    state_n = MEASURE;
                end
                else begin
                    state_n = state_c;
                end
        end
        INIT   :begin
                if(skip_en) begin
                    state_n = MEASURE;
                end
                else begin
                    state_n = state_c;
                end
        end
        MEASURE:begin
                if(skip_en) begin
                    state_n = READ;
                end
                else begin
                    state_n = state_c;
                end
        end
        READ   :begin
                if(skip_en) begin
                    state_n = DONE;
                end
                else begin
                    state_n =state_c;
                end
        end
        DONE   :begin
                if(skip_en) begin
                    state_n = IDLE;
                end
                else begin
                    state_n = state_c;
                end
        end
    endcase
end

/**********************************skip_en信号设计********************************
*********************************************************************************/
always @(posedge i2c_clk or negedge rst_n) begin 
    if(!rst_n) begin
        skip_en <= 'd0;
    end 
    else begin 
        case(state_c)
            IDLE   :begin
                    if(cnt_wait1 == 40 - 2)
                        skip_en <= 'd1;
                    else 
                        skip_en <= 'd0;
            end
            WAIT_40:begin
                    if(cnt_sclk == 'd2 && cnt_bit == 20 - 1)
                        skip_en <= 'd1;
                    else 
                        skip_en <= 'd0;
            end
            CHECK  :begin
                    if(cnt_sclk == 'd2 && cnt_bit == 38 - 1)
                        skip_en <= 'd1;
                    else 
                        skip_en <= 'd0;
            end
            INIT   :begin
                    if(cnt_sclk == 'd2 && cnt_bit == 38 - 1)
                        skip_en <= 'd1;
                    else
                        skip_en <= 'd0;
            end
            MEASURE:begin
                    if(cnt_wait2 == 80 - 2)
                        skip_en <= 'd1;
                    else
                        skip_en <= 'd0;
            end
            READ:begin
                    if(cnt_sclk == 'd2 && cnt_bit == 65 - 1)
                        skip_en <= 'd1;
                    else
                        skip_en <= 'd0;
            end
            DONE   :begin
                    if(cnt_wait3 == 1000 - 2)
                        skip_en <= 'd1;
                    else 
                        skip_en <= 'd0;
            end
        endcase
    end 
end

endmodule

3、data_handle模块

module data_handle(
    input               clk     ,
    input               rst_n   ,
    input               rd_start,//开始接收读取温湿度标志信号
    input       [19:0]  temp    ,//温度
    input       [19:0]  humi    ,//湿度

    output  reg         tx_start,//uart_tx开始发送数据的使能信号
    output  reg [07:0]  data_out //将读取到的温度进行温度转化后,转为ASCII后发送给uart_tx
);

/**********************************相关常量定义***********************************
*********************************************************************************/
parameter   TIME_10ms = 50_000;
parameter   ASCII0  = 8'h30,//0
            ASCII1  = 8'h31,//1
            ASCII2  = 8'h32,//2
            ASCII3  = 8'h33,//3
            ASCII4  = 8'h34,//4
            ASCII5  = 8'h35,//5
            ASCII6  = 8'h36,//6
            ASCII7  = 8'h37,//7
            ASCII8  = 8'h38,//8
            ASCII9  = 8'h39,//9
            ASCIIM  = 8'h3A,//冒号
            ASCIIK  = 8'h20,//空格
            ASCIID  = 8'h2E,//小数点
            ASCIIX  = 8'h0A,//换行
            ASCIIT  = 8'h54,//T
            ASCIIR  = 8'h52,//R
            ASCIIH  = 8'h48,//H
            ASCIIB  = 8'h25,//%
            ASCIIS  = 8'hA1,//℃
            ASCIIC  = 8'hE6;//

/**********************************相关变量定义***********************************
*********************************************************************************/
reg [07:0]  ascii    ;
reg [04:0]  data_cnt ;  //发送一个温湿度,需要发送22个8bit的数据(8位中文、10个精度为百分位的数据、1位换行符位、1和空格位、2个冒号位)
reg [19:0]  cnt_10ms ;  //发送1个8bit的数据设计为10ms
reg         tran_en  ;  //进行数据精度转换、ASCII转换的使能信号

wire[13:0]  humi_wr  ;  //对湿度数据进行精度转换
wire[13:0]  temp_wr  ;  //对温度数据进行精度转换

/********************对需要uart_tx发送的温湿度数据进行数据处理**********************
*********************************************************************************/
//uart_tx开始使能信号tx_start在每次发送数据的中间位置拉高
always @(posedge clk or negedge rst_n) begin 
    if(!rst_n) begin
        tx_start <= 'd0;
    end 
    else if(tran_en && cnt_10ms == TIME_10ms / 2) begin 
        tx_start <= 'd1;
    end 
    else begin 
        tx_start <= 'd0;
    end 
end

//tran_en 数据转换的使能信号
always @(posedge clk or negedge rst_n) begin 
    if(!rst_n) begin
        tran_en <= 'd0;
    end 
    else if(data_cnt == 21 - 1 && cnt_10ms == TIME_10ms - 1) begin
        tran_en <= 'd0;
    end
    else if(rd_start) begin 
        tran_en <= 'd1;
    end 
    else begin 
        tran_en <= tran_en;
    end 
end

//10ms计数器,转换1个8bit的数据的时间
always @(posedge clk or negedge rst_n) begin 
    if(!rst_n) begin
        cnt_10ms <= 'd0;
    end 
    else if(cnt_10ms == TIME_10ms - 1 || !tran_en) begin 
        cnt_10ms <= 'd0;
    end 
    else if(tran_en)begin 
        cnt_10ms <= cnt_10ms + 'd1;
    end 
    else begin
        cnt_10ms <= cnt_10ms;
    end
end

//所需要转换的8个8bit数据计数器
always @(posedge clk or negedge rst_n) begin 
    if(!rst_n) begin
        data_cnt <= 'd0;
    end 
    else if(data_cnt == 21 - 1 && cnt_10ms == TIME_10ms - 1 || !tran_en) begin 
        data_cnt <= 'd0;
    end 
    else if(cnt_10ms == TIME_10ms - 1)begin 
        data_cnt <= data_cnt + 'd1;
    end 
    else begin
        data_cnt <= data_cnt;
    end
end

//在对应的data_cnt转换成相对应的ACSII
assign humi_wr = humi[19:0] * 625 >> 16; //精度保留两位小数
assign temp_wr = (temp[19:0] * 625 >> 15) - 5000;

always @(posedge clk or negedge rst_n) begin 
    if(!rst_n) begin
        ascii <= 'hB;
    end 
    else if(tran_en)begin 
        case(data_cnt)
            00:ascii <= 8'h0F;//R
            01:ascii <= 8'h10;//H
            02:ascii <= 8'h0C;//冒号
            03:ascii <= humi_wr / 1000 % 10;
            04:ascii <= humi_wr / 0100 % 10;
            05:ascii <= 8'h0D;//小数点 
            06:ascii <= humi_wr / 0010 % 10;
            07:ascii <= humi_wr / 0001 % 10;
            08:ascii <= 8'h11;//%
            09:ascii <= 8'h0A;//空格
            10:ascii <= 8'h0E;//T
            11:ascii <= 8'h10;//H
            12:ascii <= 8'h0C;//冒号
            13:ascii <= temp_wr / 1000 % 10;
            14:ascii <= temp_wr / 0100 % 10;
            15:ascii <= 8'h0D;//小数点
            16:ascii <= temp_wr / 0010 % 10;
            17:ascii <= temp_wr / 0001 % 10;
            18:ascii <= 8'h12;//。
            19:ascii <= 8'h13;//C
            20:ascii <= 8'h0B;//换行
        endcase
    end 
    else begin
        ascii <= 'hB;
    end
end

//将转换为ASCII的温度数据发送出去,给uart_tx
always @(posedge clk or negedge rst_n) begin 
    if(!rst_n) begin
        data_out <= ASCIIX;
    end 
    else if(tran_en) begin 
        case(ascii)
            'h00:data_out <= ASCII0;
            'h01:data_out <= ASCII1;
            'h02:data_out <= ASCII2;
            'h03:data_out <= ASCII3;
            'h04:data_out <= ASCII4;
            'h05:data_out <= ASCII5;
            'h06:data_out <= ASCII6;
            'h07:data_out <= ASCII7;
            'h08:data_out <= ASCII8;
            'h09:data_out <= ASCII9;
            'h0A:data_out <= ASCIIK;//空格
            'h0B:data_out <= ASCIIX;//换行
            'h0C:data_out <= ASCIIM;//冒号
            'h0D:data_out <= ASCIID;//小数点
            'h0E:data_out <= ASCIIT;//T
            'h0F:data_out <= ASCIIR;//R
            'h10:data_out <= ASCIIH;//H
            'h11:data_out <= ASCIIB;//%
            'h12:data_out <= ASCIIS;//。
            'h13:data_out <= ASCIIC;//C
        endcase
    end 
    else begin 
        data_out <= ASCIIX;
    end 
end

endmodule

4、nieix_tube模块

module nixie_tube(
    input               clk     ,
    input               rst_n   ,
    input       [19:0]  humi    ,//读取到的温度数据输出
    input       [19:0]  temp    ,//读取到的湿度数据输出

    output  reg [05:0]  sel     ,
    output  reg [07:0]  dig
);

/**********************************相关常量定义***********************************
*********************************************************************************/
parameter   TIME_1ms  = 50_000  ;
parameter	N0 = 7'b100_0000,
            N1 = 7'b111_1001,
            N2 = 7'b010_0100,
            N3 = 7'b011_0000,
            N4 = 7'b001_1001,
            N5 = 7'b001_0010,
            N6 = 7'b000_0010,
            N7 = 7'b111_1000,
            N8 = 7'b000_0000,
            N9 = 7'b001_0000;

/**********************************相关变量定义***********************************
*********************************************************************************/
reg [15:0]  cnt0     ;//SEL刷新率计数器
wire        add_cnt0 ;
wire        end_cnt0 ;

wire[13:0]  humi_wr  ;//对湿度数据进行精度转换
wire[13:0]  temp_wr  ;//对温度数据进行精度转换

reg         dot      ;//小数点
reg [19:0]  data     ;//读取到的温度精度转换后的温度数据

/**********************************数码管位选SEL**********************************
*********************************************************************************/
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cnt0 <= 0;
    end 
    else if(add_cnt0)begin 
        if(end_cnt0)begin 
            cnt0 <= 0;
        end
        else begin 
            cnt0 <= cnt0 + 1;
        end 
    end
end 
assign add_cnt0 = 1;
assign end_cnt0 = add_cnt0 && cnt0 == TIME_1ms - 1;

always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        sel <= 6'b011_111;
    end 
    else if(end_cnt0)begin 
        sel <= {sel[4:0],sel[5]};
    end 
    else begin 
        sel <= sel;
    end 
end

/***********************************数码管位选DIG*********************************
*********************************************************************************/
assign humi_wr = humi[19:0] * 125 >> 17; //精度保留一位小数
assign temp_wr = (temp[19:0] * 125 >> 16) - 500;

always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        data <= 'd0;
        dot  <= 'd0;
    end 
    else begin   //种类
        case(sel)
            6'b011_111:begin dot <= 1'b1; data <= temp_wr / 001 % 10;end
            6'b101_111:begin dot <= 1'b0; data <= temp_wr / 010 % 10;end
            6'b110_111:begin dot <= 1'b1; data <= temp_wr / 100 % 10;end
            6'b111_011:begin dot <= 1'b1; data <= humi_wr / 001 % 10;end
            6'b111_101:begin dot <= 1'b0; data <= humi_wr / 010 % 10;end
            6'b111_110:begin dot <= 1'b1; data <= humi_wr / 100 % 10;end
        endcase
    end
end

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        dig = {dot,N0};
    end
    else begin
        case(data)
            'h0:dig <= {dot,N0};
            'h1:dig <= {dot,N1};
            'h2:dig <= {dot,N2};
            'h3:dig <= {dot,N3};
            'h4:dig <= {dot,N4};
            'h5:dig <= {dot,N5};
            'h6:dig <= {dot,N6};
            'h7:dig <= {dot,N7};
            'h8:dig <= {dot,N8};
            'h9:dig <= {dot,N9};
        endcase
    end
end

endmodule

5、uart_tx模块

//UART发送端  无校验位 结束为1位
module uart_tx(
    input           clk     ,//时钟信号
    input           rst_n   ,//复位信号
    input  [07:0]   data_in ,//需要发送的数据
    input           tx_start,

    output  reg     uart_tx  //发送信号
);

/**********************************相关常量定义***********************************
*********************************************************************************/
parameter   COUNT = 50_000_000; //系统时钟频率 周期20ns
parameter   BPS   = 115200;     //波特率
parameter   CNT   = COUNT / BPS;//传输1bit所需计数次数 COUNT / BPS; 
parameter   BIT   = 10  ;       //传输一个字节(1位起始位,8位数据位,1位结束为) 从低位开始发
parameter   IDLE  = 4'b0001,
            START = 4'b0010,
            DATA  = 4'b0100,
            DONE  = 4'b1000;

/**********************************相关信号定义***********************************
*********************************************************************************/
reg [15:0]  cnt_clk;
wire        add_cnt_clk;
wire        end_cnt_clk;

reg [3:0]   cnt_bit;
wire        add_cnt_bit;
wire        end_cnt_bit;

reg [3:0]   state_c;
reg [3:0]   state_n;

reg [7:0]   data_in_reg ; 
reg [7:0]   tx_data; 
wire        tx_done ;

/*****************************对要输出的数据进行寄存*******************************
*********************************************************************************/
//data_in_reg 对要输出的数据进行打拍
always @(posedge clk or negedge rst_n) begin 
    if(!rst_n) begin
        data_in_reg <= 'd0;
    end 
    else if(tx_start) begin 
        data_in_reg <= data_in;
    end 
    else begin 
        data_in_reg <= 'd0;
    end 
end

//tx_data 对打拍后的数据进行寄存,为后续数据的输出做准备
always @(posedge clk or negedge rst_n) begin 
    if(!rst_n) begin
        tx_data <= 'd0;
    end
    else if(state_c == DONE) begin
        tx_data <= 'd0;
    end 
    else if(state_c == START)begin 
        tx_data <= data_in_reg;
    end 
    else begin
        tx_data <= tx_data;
    end
end

/**********************************相关计数器设计*********************************
*********************************************************************************/
//发送1bit所需要的周期数cnt_clk
always @(posedge clk  or negedge rst_n) begin
    if(!rst_n) begin
        cnt_clk <= 13'b0;
    end
    else if(add_cnt_clk) begin
        if(end_cnt_clk) begin
            cnt_clk <= 13'b0;
        end
        else begin
            cnt_clk <= cnt_clk + 13'b1;
        end
    end
    else begin
        cnt_clk <= 13'b0;
    end
end
assign add_cnt_clk = state_c == DATA;
assign end_cnt_clk = add_cnt_clk && cnt_clk == CNT - 1;

//发送一字节所需要的bit数cnt_bit
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt_bit <= 4'b0;
    end
    else if(add_cnt_bit) begin
        if(end_cnt_bit) begin
            cnt_bit <= 4'b0;
        end
        else begin
            cnt_bit <= cnt_bit + 4'b1;
        end
    end
    else begin
        cnt_bit <= cnt_bit;
    end
end
assign add_cnt_bit = end_cnt_clk;
assign end_cnt_bit = add_cnt_bit && cnt_bit == BIT - 1;

//tx_done
assign tx_done = end_cnt_bit;

/**********************************Tx状态机设计***********************************
*********************************************************************************/
//状态机第一段
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:   begin
                    if(tx_start) begin
                        state_n = START;
                    end
                    else begin
                        state_n =IDLE;
                    end
                end 
        START:  begin
                    state_n = DATA;
                end
        DATA :  begin
                    if(end_cnt_bit) begin
                        state_n = DONE;
                    end
                    else begin
                        state_n = state_c;
                    end
                end
        DONE :  begin
                    state_n = IDLE;
                end
        default:state_n <= IDLE;
    endcase
end

//第三段 uart_tx输出
always @(posedge clk or negedge rst_n) begin 
    if(!rst_n) begin
        uart_tx <= 'd1;
    end 
    else if(state_c == DATA) begin 
        case(cnt_bit) 
            0:uart_tx <= 1'b0      ;
            1:uart_tx <= tx_data[0];
            2:uart_tx <= tx_data[1];
            3:uart_tx <= tx_data[2];
            4:uart_tx <= tx_data[3];
            5:uart_tx <= tx_data[4];
            6:uart_tx <= tx_data[5];
            7:uart_tx <= tx_data[6];
            8:uart_tx <= tx_data[7];
            9:uart_tx <= 1'b1      ;
            default:uart_tx <= 1'b1;
        endcase
    end 
    else begin 
        uart_tx <= 1'b1;
    end 
end

endmodule

五、板级验证(Signal Tap)

六、上板

采集到的温湿度数据,前面为湿度数据,后面为温度数据。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值