一、实验原理
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)
六、上板
采集到的温湿度数据,前面为湿度数据,后面为温度数据。