(1)设计要求
传感器检测技术与数字电子系统设计两门课程联合完成对温度传感器DS18B20的温度采集并在开发板的数码管上实时显示温度值。
(2)设计原理(介绍DS18B20的工作原理、工作时序);
温度传感器(temperature transducer)是指能感受温度并转换成可用输出信号的传感器,是各种传感器中最常用的一种。早期使用的是模拟温度传感器,如热敏电阻,随着环境温度的变化,其阻值也随之发生变化,用处理器采集电阻两端的电压,然后根据给定的公式就可计算出当前环境温度。随着科技的进步,现代的温度传感器已经走向数字化,外形小,接口简单,广泛应用在生产实践的各个领域,为我们的生活提供便利。随着现代仪器的发展,微型化、集成化、数字化正成为传感器发展的一个重要方向。美国DALLAS半导体公司推出的数字化温度传感器DS18B20采用单总线协议,即与FPGA接口仅需占用一个I/O端口,无须任何外部元件,直接将环境温度转化成数字信号,以数字码方式串行输出,从而大大简化了传感器与FPGA的接口设计。DS18B20测量温度范围为-55~+125℃,精度为±0.5℃。现场(实时) 温度直接以“单总线”的数字方式传输,大大提高了系统的抗干扰性。它能直接读出被测温度,并且可根据实际要求通过简单的编程实现9~l2位的数字值读数方式。它工作在3~5.5V的电压范围,采用多种封装形式,从而使系统设计灵活、方便,设定分辨率及用户设定的报警温度存储在EEPROM中,掉电后依然保存。其内部结构如图 所示:
DS18B20中的温度传感器可以完成对温度的测量。其温度转换可由用户自定义(改变配置寄存器中R0与R1的值)为 9、 10、11、 12 位,精度分别为 0.5℃、 0.25℃、 0.125℃、 0.0625℃分辨率,若不设置则默认为 12位的转换精度。符号标志位(S)温度的正负极性:若 S=0,则为正数;若 S=1,则为负数。
若要测量温度,主设备必须向 DS18B20 发送温度转换命令[44h]才能开始温度转换。温度转换后,转换的温度值将会保存在高速缓存器的温度寄存器中。只有通过读高速缓存器命令[BEh]才能将数据读出,数据通过 1-Wire 总线传输,传输顺序为低位到高位依次传输。如果 DS18B20 被定义为 12 位的转换精度,温度寄存器中所有位都将包含有效数据。若定义为 11 位转换精度,则 bit 0(最低位)为未定义的。若定义为 10 位转换精度,则 bit 0 和 bit 1 为未定义的。若定义 9 位转换精度,则 bit 0、 bit 1 和 bit 2 为未定义的。
待输出的数据是以二进制补码的形式存储在温度寄存器中的,所以要求温度值先得求原码再转化为十进制后乘以精度。正数的原码反码补码都是一样的,而负数的补码就是对反码加一。当温度为负数时,需先求得其原码(补码先减一再取反即为其原码)然后转换为十进制后乘以精度。例如数据输出为1111_1111_0101_1110,高六位全为1故判断此数为负数,将11_0101_1110减一后取反得到00_10100010,再将其转换为十进制得到162,乘以0.0625,填上负号最后算的温度为 -10.125摄氏度。 这里特别说明的是符号位只代表数据的正负,无论是取反还是求值它都是不算在里面的。另外上电复位时寄存器中的初始值为+85℃。
DS18B20 采用严谨的 1-Wire 总线通信协议来保证数据的完整性。该协议定义多个信号形式:复位脉冲,存在脉冲,写 0,写 1,读 0,读 1 。除了存在脉冲由从设备发出,其他信号都由主设备控制。
step 1: 初始化时序
由上图可知,主机通过判断有无存在脉冲响应来判断有无传感器可以进行数据交互。
step 2: 写时序
主设备通过写时隙将命令写入 DS18B20 中,写时隙有两种:写“1”和写“0”时隙。主设备通过写 1 时隙来向 DS18B20 中写入逻辑 1,通过写 0 时隙来向 DS18B20 中写入逻辑 0。
当主设备将总线从高电平拉至低电平时,启动写时隙,所有的写时隙持续时间最少为 60us,每个写时隙间的恢复时间最少为 1us。
如果要产生写 1 时隙,必须先将总线拉至逻辑低电平然后释放总线,允许总线在写时隙开始后 15us 内上拉至高电平。若要产生写 0 时隙,必须将总线拉至逻辑低电平并保持不变最少 60us。当总线(DQ)拉低后, DS18B20 在 15us 至 60us 之间对总线进行采样,如果采的 DQ为高电平则发生写 1,如果为低电平则发生写 0,如下图所示。
step 3: 读时序
当我们发送完读取供电模式[B4h]或读高速缓存器[BEh]命令时,必须及时地生成读时隙,只有在读时隙 DS18B20 才能向主设备传送数据。
每个读时隙最小必须有 60us 的持续时间以及每个读时隙间至少要有 1us 的恢复时间。当主设备将总线从高电平拉至低电平超过 1us,启动读时隙
当启动读时隙后, DS18B20 将会向主设备发送“0”或者“1”。 DS18B20 通过将 DQ 引脚上的上拉电阻将总线在开始的15us内拉高发送“1”,不拉高发送“0”。主设备在读时隙开始后的 15us 内必须释放总线,并且对总线进行采样。时序图如下。
(3)状态图
跳过 ROM 命令和温度跳转命令都是写 0,写 1 的时序,过程是一样的所以我们让其在同一个状态进行写入。同理可以让跳过 ROM 和读温度命令也在同一个状态写入,相关的状态描述,如下表所示。
(4)RTL图;
(5)仿真分析(数码管模块的时序仿真图);
(6)引脚锁定图;
(7)验证和调试;
本实验工程文件包括ds18b20.v,ds18b20-ctrl.v,led.v和led.-tb.v,四个文件
ds18b20.v 是 ds18b20 的顶层模块;ds18b20_ctrl.v 是 ds18b20 的控制
模块;led.v 是数码管动态显示模块;led_tb.v 是 led_tb.v的仿真测试模块。
实验结果如下
室内温度
手的温度
附页
顶层文件、底层文件和TB 文件的Verilog程序代码放在最后的附录里。
TOP文件
module ds18b20(
input sys_clk , //输入的系统时钟
input sys_rst_n , //输入的复位信号
inout dq , //ds18b20温度传感器单总线
output [2:0] sel , //输出数码管位选信号
output [7:0] seg_led //输出数码管段选信号
);
//parameter define
parameter POINT = 6'b000100; // 数码管小数点的位置
//wire define
wire [19:0] temp_data; // 温度数值
wire sign; // 符号位
//*****************************************************
//** main code
//*****************************************************
//例化动态数码管驱动模块
led u_led(
//module clock
.clk (sys_clk ), // 时钟信号
.rst_n (sys_rst_n), // 复位信号
//seg_led interface
.seg_sel (sel ), // 位选
.seg_led (seg_led ), // 段选
//user interface
.data (temp_data), // 显示的数值
.point (POINT ), // 小数点具体显示的位置,从高到低,高电平有效
.en (1'b1 ), // 数码管使能信号
.sign (sign ) // 符号位(高电平显示“-”号)
);
//例化DS18B20驱动模块
ds18b20_ctrl u1_ds18b20_ctrl(
//module clock
.clk (sys_clk ), // 时钟信号(50MHz)
.rst_n (sys_rst_n), // 复位信号
//user interface
.dq (dq ), // DS18B20的DQ引脚数据
.temp_data (temp_data), // 转换后得到的温度值
.sign (sign ) // 符号位
);
endmodule
module ds18b20_ctrl(
//module clock
input clk , // 时钟信号(50MHz)
input rst_n , // 复位信号
//user interface
inout dq , // DS18B20的DQ引脚数据
output reg [19:0] temp_data , // 转换后得到的温度值
output reg sign // 符号位
);
//parameter define
localparam ROM_SKIP_CMD = 8'hcc; // 跳过 ROM 命令
localparam CONVERT_CMD = 8'h44; // 温度转换命令
localparam READ_TEMP = 8'hbe; // 读 DS1820 温度暂存器命令
//state define
localparam init = 3'd1 ; // 初始化状态
localparam rom_skip = 3'd2 ; // 加载跳过ROM命令
localparam wr_byte = 3'd3 ; // 写字节状态
localparam temp_convert = 3'd4 ; // 加载温度转换命令
localparam delay = 3'd5 ; // 延时等待温度转换结束
localparam rd_temp = 3'd6 ; // 加载读温度命令
localparam rd_byte = 3'd7 ; // 读字节状态
//reg define
reg [ 4:0] cnt ; // 分频计数器
reg clk_1us ; // 1MHz时钟
reg [19:0] cnt_1us ; // 微秒计数
reg [ 2:0] cur_state ; // 当前状态
reg [ 2:0] next_state ; // 下一状态
reg [ 3:0] flow_cnt ; // 流转计数
reg [ 3:0] wr_cnt ; // 写计数
reg [ 4:0] rd_cnt ; // 读计数
reg [ 7:0] wr_data ; // 写入DS18B20的数据
reg [ 4:0] bit_width ; // 读取的数据的位宽
reg [15:0] rd_data ; // 采集到的数据
reg [15:0] org_data ; // 读取到的原始温度数据
reg [10:0] data1 ; // 对原理温度进行符号处理
reg [ 3:0] cmd_cnt ; // 发送命令计数
reg init_done ; // 初始化完成信号
reg st_done ; // 完成信号
reg cnt_1us_en ; // 使能计时
reg dq_out ; // DS18B20的dq输出
//wire define
wire [19:0] data2 ; // 对处理后的进行转换处理
assign dq = dq_out;
//分频生成1MHz的时钟信号
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 5'b0;
clk_1us <= 1'b0;
end
else if(cnt < 5'd24) begin
cnt <= cnt + 1'b1;
clk_1us <= clk_1us;
end
else begin
cnt <= 5'b0;
clk_1us <= ~clk_1us;
end
end
//微秒计时
always @ (posedge clk_1us or negedge rst_n) begin
if (!rst_n)
cnt_1us <= 20'b0;
else if (cnt_1us_en)
cnt_1us <= cnt_1us + 1'b1;
else
cnt_1us <= 20'b0;
end
//状态跳转
always @ (posedge clk_1us or negedge rst_n) begin
if(!rst_n)
cur_state <= init;
else
cur_state <= next_state;
end
//组合逻辑状态判断转换条件
always @( * ) begin
case(cur_state)
init: begin // 初始化状态
if (init_done)
next_state = rom_skip;
else
next_state = init;
end
rom_skip: begin // 加载跳过ROM命令
if(st_done)
next_state = wr_byte;
else
next_state = rom_skip;
end
wr_byte: begin // 发送命令
if(st_done)
case(cmd_cnt) // 根据命令序号,判断下个状态
4'b1: next_state = temp_convert;
4'd2: next_state = delay;
4'd3: next_state = rd_temp;
4'd4: next_state = rd_byte;
default:
next_state = temp_convert;
endcase
else
next_state = wr_byte;
end
temp_convert: begin // 加载温度转换命令
if(st_done)
next_state = wr_byte;
else
next_state = temp_convert;
end
delay: begin // 延时等待温度转换结束
if(st_done)
next_state = init;
else
next_state = delay;
end
rd_temp: begin // 加载读温度命令
if(st_done)
next_state = wr_byte;
else
next_state = rd_temp;
end
rd_byte: begin // 读数据线上的数据
if(st_done)
next_state = init;
else
next_state = rd_byte;
end
default: next_state = init;
endcase
end
//整个操作步骤为初始化、发送跳过ROM操作命令、发送温度转换指令、
//再初始化、再发送跳过ROM操作指令、发送读数据指令。
always @ (posedge clk_1us or negedge rst_n) begin
if(!rst_n) begin
flow_cnt <= 4'b0;
init_done <= 1'b0;
cnt_1us_en <= 1'b1;
dq_out <= 1'bZ;
st_done <= 1'b0;
rd_data <= 16'b0;
rd_cnt <= 5'd0;
wr_cnt <= 4'd0;
cmd_cnt <= 3'd0;
end
else begin
st_done <= 1'b0;
case (next_state)
init:begin //初始化
init_done <= 1'b0;
case(flow_cnt)
4'd0:
flow_cnt <= flow_cnt + 1'b1;
4'd1: begin //发出500us复位脉冲
cnt_1us_en <= 1'b1;
if(cnt_1us < 20'd500)
dq_out <= 1'b0;
else begin
cnt_1us_en <= 1'b0;
dq_out <= 1'bz;
flow_cnt <= flow_cnt + 1'b1;
end
end
4'd2:begin //释放总线,等待30us
cnt_1us_en <= 1'b1;
if(cnt_1us < 20'd30)
dq_out <= 1'bz;
else
flow_cnt <= flow_cnt + 1'b1;
end
4'd3: begin //检测响应信号
if(!dq)
flow_cnt <= flow_cnt + 1'b1;
else
flow_cnt <= flow_cnt;
end
4'd4: begin //等待初始化结束
if(cnt_1us == 20'd500) begin
cnt_1us_en <= 1'b0;
init_done <= 1'b1; //初始化完成
flow_cnt <= 4'd0;
end
else
flow_cnt <= flow_cnt;
end
default: flow_cnt <= 4'd0;
endcase
end
rom_skip: begin //加载跳过ROM操令
wr_data <= ROM_SKIP_CMD;
flow_cnt <= 4'd0;
st_done <= 1'b1;
end
wr_byte: begin //写字节状态(发送令)
if(wr_cnt <= 4'd7) begin
case (flow_cnt)
4'd0: begin
dq_out <= 1'b0; //拉低数据线,开始写作
cnt_1us_en <= 1'b1; //启动计时器
flow_cnt <= flow_cnt + 1'b1;
end
4'd1: begin //数据线拉低1us
flow_cnt <= flow_cnt + 1'b1;
end
4'd2: begin
if(cnt_1us < 20'd60) //发送数据
dq_out <= wr_data[wr_cnt];
else if(cnt_1us < 20'd63)
dq_out <= 1'bz; //释放总线(发送间隔)
else
flow_cnt <= flow_cnt + 1'b1;
end
4'd3: begin //发送1位数据完成
flow_cnt <= 0;
cnt_1us_en <= 1'b0;
wr_cnt <= wr_cnt + 1'b1;//写计数器加1
end
default : flow_cnt <= 0;
endcase
end
else begin //发送指令(1Byte)结束
st_done <= 1'b1;
wr_cnt <= 4'b0;
cmd_cnt <= (cmd_cnt == 3'd4) ? //标记当前发送的指令序号
3'd1 : (cmd_cnt+ 1'b1);
end
end
temp_convert: begin //加载温度转换命令
wr_data <= CONVERT_CMD;
st_done <= 1'b1;
end
delay: begin //延时500ms等待温度转换结束
cnt_1us_en <= 1'b1;
if(cnt_1us == 20'd500000) begin
st_done <= 1'b1;
cnt_1us_en <= 1'b0;
end
end
rd_temp: begin //加载读温度命令
wr_data <= READ_TEMP;
bit_width <= 5'd16; //指定读数据个数
st_done <= 1'b1;
end
rd_byte: begin //接收16位温度数据
if(rd_cnt < bit_width) begin
case(flow_cnt)
4'd0: begin
cnt_1us_en <= 1'b1;
dq_out <= 1'b0; //拉低数据线,开始读操作
flow_cnt <= flow_cnt + 1'b1;
end
4'd1: begin
dq_out <= 1'bz; //释放总线并在15us内接收数据
if(cnt_1us == 20'd14) begin
rd_data <= {dq,rd_data[15:1]};
flow_cnt <= flow_cnt + 1'b1 ;
end
end
4'd2: begin
if (cnt_1us <= 20'd64) //读1位数据结束
dq_out <= 1'bz;
else begin
flow_cnt <= 4'd0;
rd_cnt <= rd_cnt + 1'b1;//读计数器加1
cnt_1us_en <= 1'b0;
end
end
default : flow_cnt <= 4'd0;
endcase
end
else begin
st_done <= 1'b1;
org_data <= rd_data;
rd_cnt <= 5'b0;
end
end
default: ;
endcase
end
end
//判断符号位
always @(posedge clk_1us or negedge rst_n) begin
if(!rst_n) begin
sign <= 1'b0;
data1 <= 11'b0;
end
else if(org_data[15] == 1'b0) begin
sign <= 1'b0;
data1 <= org_data[10:0];
end
else if(org_data[15] == 1'b1) begin
sign <= 1'b1;
data1 <= ~org_data[10:0] + 1'b1;
end
end
//对采集到的温度进行转换
assign data2 = (data1 * 11'd625)/ 7'd100;
//温度输出
always @(posedge clk_1us or negedge rst_n) begin
if(!rst_n)
temp_data <= 20'b0;
else
temp_data <= data2;
end
endmodule
led模块
module led(
input clk , // 时钟信号
input rst_n , // 复位信号
input [19:0] data , // 6位数码管要显示的数值
input [5:0] point , // 小数点具体显示的位置,从高到低,高电平有效
input en , // 数码管使能信号
input sign , // 符号位(高电平显示“-”号)
output reg [2:0] seg_sel, // 数码管位选,最左侧数码管为最高位
output reg [7:0] seg_led // 数码管段选
);
//parameter define
localparam CLK_DIVIDE = 4'd10 ; // 时钟分频系数
localparam MAX_NUM = 13'd5000 ; // 对数码管驱动时钟(5MHz)计数1ms所需的计数值
//reg define
reg [ 3:0] clk_cnt ; // 时钟分频计数器
reg dri_clk ; // 数码管的驱动时钟,5MHz
reg [23:0] num ; // 24位bcd码寄存器
reg [12:0] cnt0 ; // 数码管驱动时钟计数器
reg flag ; // 标志信号(标志着cnt0计数达1ms)
reg [2:0] cnt_sel ; // 数码管位选计数器
reg [3:0] num_disp ; // 当前数码管显示的数据
reg dot_disp ; // 当前数码管显示的小数点
//wire define
wire [3:0] data0 ; // 个位数
wire [3:0] data1 ; // 十位数
wire [3:0] data2 ; // 百位数
wire [3:0] data3 ; // 千位数
wire [3:0] data4 ; // 万位数
wire [3:0] data5 ; // 十万位数
//提取显示数值所对应的十进制数的各个位
assign data0 = data % 4'd10; // 个位数
assign data1 = data / 4'd10 % 4'd10 ; // 十位数
assign data2 = data / 7'd100 % 4'd10 ; // 百位数
assign data3 = data / 10'd1000 % 4'd10 ; // 千位数
assign data4 = data / 14'd10000 % 4'd10; // 万位数
assign data5 = data / 17'd100000; // 十万位数
//对系统时钟10分频,得到的频率为5MHz的数码管驱动时钟dri_clk
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
clk_cnt <= 4'd0;
dri_clk <= 1'b1;
end
else if(clk_cnt == CLK_DIVIDE/2 - 1'd1) begin
clk_cnt <= 4'd0;
dri_clk <= ~dri_clk;
end
else begin
clk_cnt <= clk_cnt + 1'b1;
dri_clk <= dri_clk;
end
end
//将20位2进制数转换为8421bcd码(即使用4位二进制数表示1位十进制数)
always @ (posedge dri_clk or negedge rst_n) begin
if (!rst_n)
num <= 24'b0;
else begin
if (data5 || point[5]) begin //如果显示数据为6位十进制数,
num[23:20] <= data5; //则依次给6位数码管赋值
num[19:16] <= data4;
num[15:12] <= data3;
num[11:8] <= data2;
num[ 7:4] <= data1;
num[ 3:0] <= data0;
end
else begin
if (data4 || point[4]) begin //如果显示数据为5位十进制数,则给低5位数码管赋值
num[19:0] <= {data4,data3,data2,data1,data0};
if(sign)
num[23:20] <= 4'd11; //如果需要显示负号,则最高位(第6位)为符号位
else
num[23:20] <= 4'd10; //不需要显示负号时,则第6位不显示任何字符
end
else begin //如果显示数据为4位十进制数,则给低4位数码管赋值
if (data3 || point[3]) begin
num[15: 0] <= {data3,data2,data1,data0};
num[23:20] <= 4'd10; //第6位不显示任何字符
if(sign) //如果需要显示负号,则最高位(第5位)为符号位
num[19:16] <= 4'd11;
else //不需要显示负号时,则第5位不显示任何字符
num[19:16] <= 4'd10;
end
else begin //如果显示数据为3位十进制数,则给低3位数码管赋值
if (data2 || point[2]) begin
num[11: 0] <= {data2,data1,data0};
//第6、5位不显示任何字符
num[23:16] <= {2{4'd10}};
if(sign) //如果需要显示负号,则最高位(第4位)为符号位
num[15:12] <= 4'd11;
else //不需要显示负号时,则第4位不显示任何字符
num[15:12] <= 4'd10;
end
else begin //如果显示数据为2位十进制数,则给低2位数码管赋值
if (data1 || point[1]) begin
num[ 7: 0] <= {data1,data0};
//第6、5、4位不显示任何字符
num[23:12] <= {3{4'd10}};
if(sign) //如果需要显示负号,则最高位(第3位)为符号位
num[11:8] <= 4'd11;
else //不需要显示负号时,则第3位不显示任何字符
num[11:8] <= 4'd10;
end
else begin //如果显示数据为1位十进制数,则给最低位数码管赋值
num[3:0] <= data0;
//第6、5位不显示任何字符
num[23:8] <= {4{4'd10}};
if(sign) //如果需要显示负号,则最高位(第2位)为符号位
num[7:4] <= 4'd11;
else //不需要显示负号时,则第2位不显示任何字符
num[7:4] <= 4'd10;
end
end
end
end
end
end
end
//每当计数器对数码管驱动时钟计数时间达1ms,输出一个时钟周期的脉冲信号
always @ (posedge dri_clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
cnt0 <= 13'b0;
flag <= 1'b0;
end
else if (cnt0 < MAX_NUM - 1'b1) begin
cnt0 <= cnt0 + 1'b1;
flag <= 1'b0;
end
else begin
cnt0 <= 13'b0;
flag <= 1'b1;
end
end
//cnt_sel从0计数到5,用于选择当前处于显示状态的数码管
always @ (posedge dri_clk or negedge rst_n) begin
if (rst_n == 1'b0)
cnt_sel <= 3'b0;
else if(flag) begin
if(cnt_sel < 3'd5)
cnt_sel <= cnt_sel + 1'b1;
else
cnt_sel <= 3'b0;
end
else
cnt_sel <= cnt_sel;
end
//控制数码管位选信号,使6位数码管轮流显示
always @ (posedge dri_clk or negedge rst_n) begin
if(!rst_n) begin
seg_sel <= 3'd7; //位选信号低电平有效
num_disp <= 4'b0;
dot_disp <= 1'b0; //共阳极数码管,低电平导通
end
else begin
if(en) begin
case (cnt_sel)
3'd0 :begin
seg_sel <= 3'd6; //显示数码管最低位
num_disp <= num[3:0] ; //显示的数据
dot_disp <= point[0]; //显示的小数点
end
3'd1 :begin
seg_sel <= 3'd5; //显示数码管第1位
num_disp <= num[7:4] ;
dot_disp <= point[2];
end
3'd2 :begin
seg_sel <= 3'd4; //显示数码管第2位
num_disp <= num[11:8];
dot_disp <= point[1];
end
3'd3 :begin
seg_sel <= 3'd3; //显示数码管第3位
num_disp <= num[15:12];
dot_disp <= point[3];
end
3'd4 :begin
seg_sel <= 3'd2; //显示数码管第4位
num_disp <= num[19:16];
dot_disp <= point[4];
end
3'd5 :begin
seg_sel <= 3'd1; //显示数码管最高位
num_disp <= num[23:20];
dot_disp <= point[5];
end
default :begin
seg_sel <= 3'd0;
num_disp <= 4'b0;
dot_disp <= 1'b0;
end
endcase
end
else begin
seg_sel <= 3'd0; //使能信号为0时,所有数码管均不显示
num_disp <= 4'b0;
dot_disp <= 1'b0;
end
end
end
//控制数码管段选信号,显示字符
always @ (posedge dri_clk or negedge rst_n) begin
if (!rst_n)
seg_led <= 8'hc0;
else begin
case (num_disp)
4'd0 : seg_led <= {dot_disp,7'b1000000}; //显示数字 0
4'd1 : seg_led <= {dot_disp,7'b1111001}; //显示数字 1
4'd2 : seg_led <= {dot_disp,7'b0100100}; //显示数字 2
4'd3 : seg_led <= {dot_disp,7'b0110000}; //显示数字 3
4'd4 : seg_led <= {dot_disp,7'b0011001}; //显示数字 4
4'd5 : seg_led <= {dot_disp,7'b0010010}; //显示数字 5
4'd6 : seg_led <= {dot_disp,7'b0000010}; //显示数字 6
4'd7 : seg_led <= {dot_disp,7'b1111000}; //显示数字 7
4'd8 : seg_led <= {dot_disp,7'b0000000}; //显示数字 8
4'd9 : seg_led <= {dot_disp,7'b0010000}; //显示数字 9
4'd10: seg_led <= 8'b11111111; //不显示任何字符
4'd11: seg_led <= 8'b10111111; //显示负号(-)
default:
seg_led <= {dot_disp,7'b1000000};
endcase
end
end
endmodule
TB文件
`timescale 1ns / 1ns
module seg_595_dynamic_tb;
// 输入
reg clk;
reg rst_n;
reg [19:0] data;
reg [5:0] point;
reg en;
reg sign;
// 输出
wire [5:0] seg_sel;
wire [7:0] seg_led;
// 实例化
seg_595_dynamic dut (
.clk(clk),
.rst_n(rst_n),
.data(data),
.point(point),
.en(en),
.sign(sign),
.seg_sel(seg_sel),
.seg_led(seg_led)
);
// 生成时钟
always begin
#5 clk = ~clk;
end
// 初始化输入
initial begin
clk = 0;
rst_n = 0;
data = 20'd0;
point = 6'b000000;
en = 0;
sign = 0;
#10 rst_n = 1;
#10 en = 1;
// 测试实例1
data = 20'd12345;
point = 6'b001000;
sign = 0;
#100;
// 测试实例2
data = 20'd9876;
point = 6'b000010;
sign = 1;
#100;
#100;
$finish;
end
endmodule