总结:
模块例化思想: 例化2个定时器模块 + 数码管驱动
根据自己思路编写代码,调试仿真代码,同时熟悉环境.加深细节理解
刚开始写得很乱,代码完全就是用modelsim仿真调试出来的
注意:
一. 实现的功能
使用FPGA开发板上的6位数码管以动态方式从0开始计数,每100ms计数值
增加一,当计数值从0增加到999999后重新从0开始计数。
二. 功能框图
定时器模块1:加计数器,计数100ms到了产生overflow信号
定时器模块2:加计数器,计数1ms到了产生overflow信号
segled控制模块:
根据en使能信号,增加显示值0~999999;
根据en2使能信号,切换sel位选信号,并输出段选信号
打开方法: tools -> netlist viewers -> rtl viewer
三. RTL代码
segled_dy_top.v
与segled_dynamic_top.v基本相同。
COUNT_OVERFLOW = 26'd5_000_000,即闪烁周期为100ms。
module segled_dy_top (
input clk,
input reset,
output [5:0] sel,
output [7:0] seg_led
);
parameter COUNT_OVERFLOW = 26'd5_000_000; //1s
parameter COUNT_OVERFLOW2 = 26'd50_000_000/10'd1000; //1ms
wire [1:0] overflow;
timer #(
.COUNT_OVERFLOW (COUNT_OVERFLOW)
)
u_timer(
.clk (clk),
.reset (reset),
.overflow (overflow[0])
);
timer #(
.COUNT_OVERFLOW (COUNT_OVERFLOW2)
)
u2_timer(
.clk (clk),
.reset (reset),
.overflow (overflow[1])
);
segled_dy_driver u_segled_dy_driver(clk,reset,overflow[0],overflow[1],sel,seg_led);
endmodule
timer.v
除将overflow <=修改为非阻塞赋值外,其它与led点灯工程的相同。
/*
********************************************************************************
*Copyright ©, 2018, CunXin_ All Rights Reserved
*文 件 名: key_led.v
*说 明:
程序功能无按键按下时, LED灯全灭;
按键1按下时, LED灯显示自右向左的流水效果;
按键2按下时, LED灯显示自左向右的流水效果;
按键3按下时,四个LED灯同时闪烁;
按键4按下时, LED灯全亮
*版 本: V000B00D00
*创建日期: 2018年4月3日 下午12:16:49
*创 建 人: wansaiyon
*修改信息:
================================================================================
*修 改 人 修改日期 修改内容
*<作者/修改者> <YYYY/MM/DD> <修改内容>
********************************************************************************
*/
module timer #(
parameter COUNT_OVERFLOW = 26'd10_000_000
) (
input clk,
input reset,
output reg overflow
);
localparam period = COUNT_OVERFLOW;
reg [25:0] cnt;
always @(posedge clk,negedge reset) begin
if(!reset) begin
cnt <= 26'd0;
overflow <= 1'b0;
end
else if(cnt < period) begin
cnt <= cnt + 1'd1;
overflow <= 1'b0;
end
else begin
cnt <= 26'd0;
overflow <= 1'b1;
end
end
endmodule
segled_dy_driver.v
主要功能点:
定义16个字符、
0~999999计数,执行周期100ms
连续赋值计算个位、十位、百位....
计算待显示数字的位数,执行周期100ms
位选控制 sel_ctrl 切换,执行周期1ms
输出位选信号sel(采用独热码)和段选信号 seg_led, 执行周期1ms
/*
6位数码管
sel[0] output N16 第一个数码管位选信号
sel[1] output N15 第二个数码管位选信号
sel[2] output P16 第三个数码管位选信号
sel[3] output P15 第四个数码管位选信号
sel[4] output R16 第五个数码管位选信号
sel[5] output T15 第六个数码管位选信号
seg_led[0] output M11 数码管段选a
seg_led[1] output N12 数码管段选b
seg_led[2] output C9 数码管段选c
seg_led[3] output N13 数码管段选d
seg_led[4] output M10 数码管段选e
seg_led[5] output N11 数码管段选f
seg_led[6] output P11 数码管段选g
seg_led[7] output D9 数码管段选h
*/
module segled_dy_driver (
input clk,
input reset,
input en,
input en2,
output reg [5:0] sel,
output reg [7:0] seg_led
);
// localparam define
/*
//显示.号
localparam CHAR_0 = 8'b0100_0000;
localparam CHAR_1 = 8'b0111_1001;
localparam CHAR_2 = 8'b0010_0100;
localparam CHAR_3 = 8'b0011_0000;
localparam CHAR_4 = 8'b0001_1001;
localparam CHAR_5 = 8'b0001_0010;
localparam CHAR_6 = 8'b0000_0010;
localparam CHAR_7 = 8'b0111_1000;
localparam CHAR_8 = 8'b0000_0000;
localparam CHAR_9 = 8'b0001_0000;
localparam CHAR_a = 8'b0000_1000;
localparam CHAR_b = 8'b0000_0011;
localparam CHAR_c = 8'b0100_0110;
localparam CHAR_d = 8'b0010_0001;
localparam CHAR_e = 8'b0000_0110;
localparam CHAR_f = 8'b0000_1110;
*/
localparam CHAR_0 = 8'b1100_0000;
localparam CHAR_1 = 8'b1111_1001;
localparam CHAR_2 = 8'b1010_0100;
localparam CHAR_3 = 8'b1011_0000;
localparam CHAR_4 = 8'b1001_1001;
localparam CHAR_5 = 8'b1001_0010;
localparam CHAR_6 = 8'b1000_0010;
localparam CHAR_7 = 8'b1111_1000;
localparam CHAR_8 = 8'b1000_0000;
localparam CHAR_9 = 8'b1001_0000;
localparam CHAR_a = 8'b1000_1000;
localparam CHAR_b = 8'b1000_0011;
localparam CHAR_c = 8'b1100_0110;
localparam CHAR_d = 8'b1010_0001;
localparam CHAR_e = 8'b1000_0110;
localparam CHAR_f = 8'b1000_1110;
// reg define
reg [19:0] disp_num; /* 0~999999 */
reg [2:0] disp_num_bit; /* disp_num的位数 */
reg [2:0] sel_ctrl; /* sel[0]~sel[5] */
// 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; /* 十万位 */
// continues assignment
assign data0 = disp_num%4'd10;
assign data1 = (disp_num/4'd10)%4'd10;
assign data2 = (disp_num/7'd100)%4'd10;
assign data3 = (disp_num/10'd1000)%4'd10;
assign data4 = (disp_num/14'd10000)%4'd10;
assign data5 = (disp_num/17'd100000)%4'd10;
/*
循环计数 0~999999,en产生执行周期1s
*/
always @(posedge clk,negedge reset ) begin
if(!reset) begin
disp_num <= 20'd0;
end
else if(en && (disp_num < 20'd999999)) begin
disp_num <= disp_num + 1'b1;
end
else if(en && (disp_num == 20'd999999))begin
disp_num <= 20'd0;
end
else begin
disp_num <= disp_num;
end
end
/*
计算待显示数字的位数
*/
always @(posedge clk,negedge reset ) begin
if(!reset) begin
disp_num_bit <= 3'd0;
end
else if(en && (disp_num < 4'd10)) begin
disp_num_bit <= 3'd0;
end
else if(en && (disp_num < 7'd100)) begin
disp_num_bit <= 3'd1;
end
else if(en && (disp_num < 10'd1000)) begin
disp_num_bit <= 3'd2;
end
else if(en && (disp_num < 14'd10000)) begin
disp_num_bit <= 3'd3;
end
else if(en && (disp_num < 17'd100000)) begin
disp_num_bit <= 3'd4;
end
else if(en && (disp_num < 20'd1000000)) begin
disp_num_bit <= 3'd5;
end
else if(en && (disp_num == 20'd999999))begin
disp_num_bit <= 3'd0;
end
else begin
disp_num_bit <= disp_num_bit;
end
end
/*
位选控制 sel_ctrl 切换,en2产生周期1ms
sel_ctrl = 0, 位选信号 = 6'b11_1110,即选择第1个数码管
sel_ctrl = 1, 位选信号 = 6'b11_1101,即选择第2个数码管
sel_ctrl = 2, 位选信号 = 6'b11_1011,即选择第3个数码管
sel_ctrl = 3, 位选信号 = 6'b11_0111,即选择第4个数码管
sel_ctrl = 4, 位选信号 = 6'b10_1111,即选择第5个数码管
sel_ctrl = 5, 位选信号 = 6'b01_1111,即选择第6个数码管
*/
/*
always @(posedge clk,negedge reset ) begin
if(!reset) begin
sel_ctrl <= 3'd0;
end
else if(en2 && (sel_ctrl < 3'd5)) begin
sel_ctrl <= sel_ctrl + 1'b1;
end
else if(en2 && (sel_ctrl == 3'd5)) begin
sel_ctrl <= 3'd0;
end
else begin
sel_ctrl <= sel_ctrl;
end
end
*/
always @(posedge clk,negedge reset ) begin
if(!reset) begin
sel_ctrl <= 3'd0;
end
else if(en2 && (sel_ctrl < disp_num_bit)) begin
sel_ctrl <= sel_ctrl + 1'b1;
end
else if(en2 && (sel_ctrl == disp_num_bit)) begin
sel_ctrl <= 3'd0;
end
else begin
sel_ctrl <= sel_ctrl;
end
end
/*
输出位选信号sel(采用独热码)和段选信号 seg_led, 执行周期1ms
设计思路
举例:如何显示数字 “654321”
第1ms,显示第1个数码管“1”
第2ms,显示第2个数码管“2”
第3ms,显示第3个数码管“3”
第4ms,显示第4个数码管“4”
第5ms,显示第5个数码管“5”
第6ms,显示第6个数码管“6”
由于切换周期1ms,即6ms可以将6个数字 “654321”依次显示出来,
同时由于视觉暂留的影响,显示数字“6”时,数字“1”、“2”、“3”、“4”、“5”仍然还能看到
我们人眼看到的效果跟6个数字同时显示出来的效果是一样的。
因此我们需要做的就是:
第0ms,显示第1个数码管“1”, 位选信号 sel = 6'b11_1110,段选信号 seg_led = “data0对应的字符”
第1ms,显示第2个数码管“2”, 位选信号 sel = 6'b11_1101,段选信号 seg_led = “data1对应的字符”
第2ms,显示第3个数码管“3”, 位选信号 sel = 6'b11_1011,段选信号 seg_led = “data2对应的字符”
第3ms,显示第4个数码管“4”, 位选信号 sel = 6'b11_0111,段选信号 seg_led = “data3对应的字符”
第4ms,显示第5个数码管“5”, 位选信号 sel = 6'b10_1111,段选信号 seg_led = “data4对应的字符”
第5ms,显示第6个数码管“6”, 位选信号 sel = 6'b01_1111,段选信号 seg_led = “data5对应的字符”
*/
always @(posedge clk ,negedge reset) begin
if(!reset) begin
sel <= 6'b11_1110;
seg_led <= CHAR_0;
end
else if(en2) begin
casex (sel_ctrl)
3'd0: begin
sel <= 6'b11_1110;
if(data0==0)
seg_led <= CHAR_0;
else if(data0==1)
seg_led <= CHAR_1;
else if(data0==2)
seg_led <= CHAR_2;
else if(data0==3)
seg_led <= CHAR_3;
else if(data0==4)
seg_led <= CHAR_4;
else if(data0==5)
seg_led <= CHAR_5;
else if(data0==6)
seg_led <= CHAR_6;
else if(data0==7)
seg_led <= CHAR_7;
else if(data0==8)
seg_led <= CHAR_8;
else if(data0==9)
seg_led <= CHAR_9;
else
seg_led <= CHAR_0;
end
3'd1: begin
sel <= 6'b11_1101;
if(data1==0)
seg_led <= CHAR_0;
else if(data1==1)
seg_led <= CHAR_1;
else if(data1==2)
seg_led <= CHAR_2;
else if(data1==3)
seg_led <= CHAR_3;
else if(data1==4)
seg_led <= CHAR_4;
else if(data1==5)
seg_led <= CHAR_5;
else if(data1==6)
seg_led <= CHAR_6;
else if(data1==7)
seg_led <= CHAR_7;
else if(data1==8)
seg_led <= CHAR_8;
else if(data1==9)
seg_led <= CHAR_9;
else
seg_led <= CHAR_0;
end
3'd2: begin
sel <= 6'b11_1011;
if(data2==0)
seg_led <= CHAR_0;
else if(data2==1)
seg_led <= CHAR_1;
else if(data2==2)
seg_led <= CHAR_2;
else if(data2==3)
seg_led <= CHAR_3;
else if(data2==4)
seg_led <= CHAR_4;
else if(data2==5)
seg_led <= CHAR_5;
else if(data2==6)
seg_led <= CHAR_6;
else if(data2==7)
seg_led <= CHAR_7;
else if(data2==8)
seg_led <= CHAR_8;
else if(data2==9)
seg_led <= CHAR_9;
else
seg_led <= CHAR_0;
end
3'd3: begin
sel <= 6'b11_0111;
if(data3==0)
seg_led <= CHAR_0;
else if(data3==1)
seg_led <= CHAR_1;
else if(data3==2)
seg_led <= CHAR_2;
else if(data3==3)
seg_led <= CHAR_3;
else if(data3==4)
seg_led <= CHAR_4;
else if(data3==5)
seg_led <= CHAR_5;
else if(data3==6)
seg_led <= CHAR_6;
else if(data3==7)
seg_led <= CHAR_7;
else if(data3==8)
seg_led <= CHAR_8;
else if(data3==9)
seg_led <= CHAR_9;
else
seg_led <= CHAR_0;
end
3'd4: begin
sel <= 6'b10_1111;
if(data4==0)
seg_led <= CHAR_0;
else if(data4==1)
seg_led <= CHAR_1;
else if(data4==2)
seg_led <= CHAR_2;
else if(data4==3)
seg_led <= CHAR_3;
else if(data4==4)
seg_led <= CHAR_4;
else if(data4==5)
seg_led <= CHAR_5;
else if(data4==6)
seg_led <= CHAR_6;
else if(data4==7)
seg_led <= CHAR_7;
else if(data4==8)
seg_led <= CHAR_8;
else if(data4==9)
seg_led <= CHAR_9;
else
seg_led <= CHAR_0;
end
3'd5: begin
sel <= 6'b01_1111;
if(data5==0)
seg_led <= CHAR_0;
else if(data5==1)
seg_led <= CHAR_1;
else if(data5==2)
seg_led <= CHAR_2;
else if(data5==3)
seg_led <= CHAR_3;
else if(data5==4)
seg_led <= CHAR_4;
else if(data5==5)
seg_led <= CHAR_5;
else if(data5==6)
seg_led <= CHAR_6;
else if(data5==7)
seg_led <= CHAR_7;
else if(data5==8)
seg_led <= CHAR_8;
else if(data5==9)
seg_led <= CHAR_9;
else
seg_led <= CHAR_0;
end
default: begin
sel <= 6'b11_1110;
if(data0==0)
seg_led <= CHAR_0;
else if(data0==1)
seg_led <= CHAR_1;
else if(data0==2)
seg_led <= CHAR_2;
else if(data0==3)
seg_led <= CHAR_3;
else if(data0==4)
seg_led <= CHAR_4;
else if(data0==5)
seg_led <= CHAR_5;
else if(data0==6)
seg_led <= CHAR_6;
else if(data0==7)
seg_led <= CHAR_7;
else if(data0==8)
seg_led <= CHAR_8;
else if(data0==9)
seg_led <= CHAR_9;
else
seg_led <= CHAR_0;
end
endcase
end
else begin
sel <= sel;
seg_led <= seg_led;
end
end
endmodule
四.modelsim前仿真(功能仿真)
每次打开modelsim时需要切换工作空间,否则可能创建工程添加的源码会是上一个仿真工程的代码,导致出错 。
使用vscode打开segled_static_top.v, ctrl+shift+p,输入testbench,在terminal生成了segled_static_top_tb.v代码,需要安装插件verilog_testbench, 初始生成时可能提示失败,需要安装 python 库(根据错误提示自行百度)。
生成的代码有点错误,需要自己再做点修改。
tb_segled_dy_top.v,
为了方便减小了计数器周期为4'd10,2'd2。
`timescale 1ns / 1ns
module tb_segled_dy_top;
// segled_static_top Parameters
parameter PERIOD = 10 ;
parameter COUNT_OVERFLOW = 4'd10;
parameter COUNT_OVERFLOW2 = 2'd2;
// segled_static_top Inputs
reg clk = 0 ;
reg reset = 0 ;
// segled_static_top Outputs
wire [5:0] sel ;
wire [7:0] seg_led ;
initial
begin
forever #(PERIOD/2) clk=~clk;
end
initial
begin
#(PERIOD*2) reset = 1;
end
segled_dy_top #(
.COUNT_OVERFLOW ( COUNT_OVERFLOW ),
.COUNT_OVERFLOW2 ( COUNT_OVERFLOW2 ))
u_segled_dy_top (
.clk ( clk ),
.reset ( reset ),
.sel ( sel [5:0] ),
.seg_led ( seg_led [7:0] )
);
endmodule
仿真波形
五.下载验证
FPGA开发板上的6位数码管以动态方式从0开始计数,每100ms计数值增加一,当计数值从0增加到999999后重新从0开始计数。
但是发现4个LED有微弱的亮度,需要配置好未使用管脚的状态为高阻输入,同时将nCEO管脚修改为Use as regular I/0,修改后LED变为熄灭状态了。
在 Quartus 软件中默认未使用管脚的状态为弱上拉输入,所以未使用到的管脚上也是有电压的,只是驱动能力很弱,这往往会导致一些不安全的隐患,所以我们需要将未使用管脚的状态设置为三态输入。
在左侧Category一栏中选择Dual-Purpose Pin。对于需要使用EPCS器件的引脚时,需要将下图页面中所有的引脚都改成Use as regular IO,如果大家不确定工程中是否用到EPCS器件时,可以全部修改。本次实验只修改了nCEO一栏中, 将Use as programming pin修改为Use as regular I/0。文章来源:https://www.toymoban.com/news/detail-495218.html
文章来源地址https://www.toymoban.com/news/detail-495218.html
到了这里,关于开发板实战篇3 6位数码管动态显示的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!