前言:
上一篇文章的话是介绍了关于数码管的基础知识和静态数码管的verilog代码,那本章的话将去介绍如何实现动态数码管以及它的verilog代码和展示
动态数码管:
简易计数器:
这里的话主要去介绍一个简易的计数器,由按键控制开始和结束,每秒自加1,从0计到9999,计满后清零
动态数码管显示:
运用余晖效应和数码管特性的结合。通过位选信号去控制数码管点亮,每位点亮的数码管显示的段选位字符都是独立,互不影响的。
余晖效应:
又称视觉暂留现象。简单可以理解成在观察高速移动的物体时,视网膜的图像不会完全在人脑消失,而会驻留一定时间才会消失
数码管特性:
数码管在点亮或熄灭时,往往不会一瞬间就点亮或熄灭,这是一个存在短暂延迟的过程。
思路分析:
为了代码逻辑更加清晰明了,将以三段式状态机来进行RTL的编写,可以分成三个模块,首先有一个顶层模块,其次是一个数码管的显示模块,最后一个是译码模块,还需要再加一个按键模块,可以使用之前的IP核
38译码器:
verilog代码:
数码管动态:
module SMG_DT (
input wire sysclk ,//系统时钟
input wire rst_n ,//复位
input wire key_flag ,//按键控制是否开始
output reg [3:0] num ,//输出的数据
output reg [3:0] sel //四位位选位
);
parameter IDLE = 5'b00001;//空闲态
parameter GE = 5'b00010;//个位
parameter SHI = 5'b00100;//十位
parameter BAI = 5'b01000;//百位
parameter QIAN = 5'b10000;//千位
reg [4:0] c_state , n_state;//现态和次态
reg en;//使能信号,长信号
parameter delay_1ms = 50_000;//1ms内部晶振次数
parameter delay = 50_000_000;//1s内部晶振次数
reg [26:0] cnt_1s;//1s计时器
reg [15:0] cnt_1ms;//1ms计时器
reg [13:0] number;//计数器,每1s加一次,记到9999清零
wire [3:0] ge = number%10;
wire [3:0] shi = number/10%10;
wire [3:0] bai = number/100%10;
wire [3:0] qian = number/1000;
//对en赋值
always @(posedge sysclk) begin
if(!rst_n)
en <= 0;
else if(key_flag)
en <= ~en;
else
en <= en;
end
//1ms计时器
always @(posedge sysclk) begin
if(!rst_n)
cnt_1ms <= 0;
else if(en)begin
if(cnt_1ms == delay_1ms - 1)
cnt_1ms <= 0;
else
cnt_1ms <= cnt_1ms + 1;
end
else
cnt_1ms <= 0;
end
//1s计时器
always @(posedge sysclk) begin
if(!rst_n)
cnt_1s <= 0;
else if(en)begin
if(cnt_1s == delay - 1)
cnt_1s <= 0;
else
cnt_1s <= cnt_1s + 1;
end
else
cnt_1s <= 0;
end
//对number进行赋值
always @(posedge sysclk) begin
if(!rst_n)
number <= 0;
else if(cnt_1s == delay - 1 && en)begin
if(number == 9999)
number <= 0;
else
number <= number + 1;
end
else
number <= number;
end
//状态机第一段
always @(posedge sysclk) begin
if(!rst_n)
c_state <= IDLE;
else
c_state <= n_state;
end
//状态机第二段
always @(*) begin
if(!rst_n)
n_state = IDLE;
else
case (c_state)
IDLE:begin
if(en && cnt_1ms == delay_1ms - 1)
n_state = GE;
else
n_state = IDLE;
end
GE :begin
if(en && cnt_1ms == delay_1ms - 1)
n_state = SHI;
else
n_state = GE;
end
SHI :begin
if(en && cnt_1ms == delay_1ms - 1)
n_state = BAI;
else
n_state = SHI;
end
BAI :begin
if(en && cnt_1ms == delay_1ms - 1)
n_state = QIAN;
else
n_state = BAI;
end
QIAN:begin
if(en && cnt_1ms == delay_1ms - 1)
n_state = GE;
else
n_state = QIAN;
end
default:n_state = IDLE;
endcase
end
//状态机第三段
always @(posedge sysclk) begin
if(!rst_n)begin
num <= 0;
sel <= 4'b1111;
end
else
case (c_state)
IDLE:begin
num <= 0;
sel <= 4'b1111;
end
GE :begin
num <= ge;
sel <= 4'b0001;
end
SHI :begin
num <= shi;
sel <= 4'b0010;
end
BAI :begin
num <= bai;
sel <= 4'b0100;
end
QIAN:begin
num <= qian;
sel <= 4'b1000;
end
default:begin
num <= 0;
sel <= 4'b1111;
end
endcase
end
endmodule
译码模块:
module yima (
input wire [3:0] num ,
output reg [7:0] seg
);
always @(*) begin
case (num)
0:seg = 8'b1100_0000;
1:seg = 8'b1111_1001;
2:seg = 8'b1010_0100;
3:seg = 8'b1011_0000;
4:seg = 8'b1001_1001;
5:seg = 8'b1001_0010;
6:seg = 8'b1000_0010;
7:seg = 8'b1111_1000;
8:seg = 8'b1000_0000;
9:seg = 8'b1001_0000;
default:seg = 8'b1111_1111;
endcase
end
endmodule
顶层模块:
module SMG_TOP (
input wire sysclk ,
input wire rst_n ,
input wire key ,
output wire [3:0] sel ,
output wire [7:0] seg
);
wire [3:0] num;
wire key_flag;
SMG_DT SMG_DT_u(
. sysclk (sysclk ) ,//系统时钟
. rst_n (rst_n ) ,//复位
. key_flag (key_flag) ,//按键控制是否开始
. num (num ) ,//输出的数据
. sel (sel ) //四位位选位
);
yima yima_u(
. num (num) ,
. seg (seg)
);
key_flag_0 key_flag_0_u (
.clk(sysclk), // input wire clk
.rst_n(rst_n), // input wire rst_n
.key(key), // input wire key
.key_flag(key_flag) // output wire key_flag
);
endmodule
现象展示:
<iframe allowfullscreen="true" data-mediaembed="csdn" frameborder="0" id="i8lUMbdy-1730939907353" src="https://live.csdn.net/v/embed/432905"></iframe>数码管简易计数器
总结:
通过以上的模块等就可以去实现一个数码管的简易计数器,它主要的话是通过余晖效应和根据数码管的一些特征,运用三段式状态机去实现该实验
简易计时器:
计时器和计数器的话其实大致都差不多,最主要的就是再计时器模块需要去考虑如何在计时满59的时候向前进位,只需要修改我们的数码管显示模块,其它的可以保持不动
verilog代码:
module SMG_time (
input wire sysclk ,
input wire rst_n ,
input wire key_flag ,
output reg [3:0] num ,
output reg [3:0] sel
);
parameter IDLE = 5'b00001;
parameter GE = 5'b00010;
parameter SHI = 5'b00100;
parameter BAI = 5'b01000;
parameter QIAN = 5'b10000;
reg [4:0] c_state , n_state;
parameter delay_1ms = 50_000;
parameter delay_1s = 50_000_000;
reg [25:0] cnt_1s;//1s内部时钟晶振
reg [15:0] cnt_1ms;//1ms内部时钟晶振
reg [6:0] miao;
reg [6:0] fen;
reg en;//按键使能信号
//按键使能信号en
always @(posedge sysclk) begin
if(!rst_n)
en <= 0;
else if(key_flag)
en <= ~en;
else
en <= en;
end
//1s计时器
always @(posedge sysclk) begin
if(!rst_n)
cnt_1s <= 0;
else if(en)begin
if(cnt_1s == delay_1s - 1)
cnt_1s <= 0;
else
cnt_1s <= cnt_1s + 1;
end
else
cnt_1s <= 0;
end
//1ms计时器
always @(posedge sysclk) begin
if(!rst_n)
cnt_1ms <= 0;
else if(en)begin
if(cnt_1ms == delay_1ms - 1)
cnt_1ms <= 0;
else
cnt_1ms <= cnt_1ms + 1;
end
else
cnt_1ms <= 0;
end
//对miao和fen赋值
always @(posedge sysclk) begin
if(!rst_n)begin
miao <= 0;
fen <= 0;
end
else if(cnt_1s == delay_1s - 1 && en)begin
if(miao < 59)
miao <= miao + 1;
else begin
miao <= 0;
if(fen < 59)
fen <= fen + 1;
else
fen <= 0;
end
end
else begin
miao <= miao;
fen <= fen;
end
end
//状态机第一段
always @(posedge sysclk) begin
if(!rst_n)
c_state <= IDLE;
else
c_state <= n_state;
end
//状态机第二段
always @(*) begin
if(!rst_n)
n_state = IDLE;
else
case (c_state)
IDLE :begin
if(en && cnt_1ms == delay_1ms - 1)
n_state = GE;
else
n_state = IDLE;
end
GE :begin
if(en && cnt_1ms == delay_1ms - 1)
n_state = SHI;
else
n_state = GE;
end
SHI :begin
if(en && cnt_1ms == delay_1ms - 1)
n_state = BAI;
else
n_state = SHI;
end
BAI :begin
if(en && cnt_1ms == delay_1ms - 1)
n_state = QIAN;
else
n_state = BAI;
end
QIAN :begin
if(en && cnt_1ms == delay_1ms - 1)
n_state = GE;
else
n_state = QIAN;
end
default:n_state = IDLE ;
endcase
end
//状态机第三段
always @(posedge sysclk) begin
if(!rst_n)begin
num <= 0;
sel <= 4'b1111;
end
case (c_state)
IDLE :begin
num <= 0;
sel <= 4'b1111;
end
GE :begin
num <= miao%10;
sel <= 4'b0001;
end
SHI :begin
num <= miao/10;
sel <= 4'b0010;
end
BAI :begin
num <= fen%10;
sel <= 4'b0100;
end
QIAN :begin
num <= fen/10;
sel <= 4'b1000;
end
default:begin
num <= 0;
sel <= 4'b1111;
end
endcase
end
endmodule
这个的话就是我们计时器的一个数码管显示代码,将它给例化到之前的那个数码管顶层文件里去替换掉计数器代码,然后绑定管脚上板,管脚的绑定方法在上一章节里已经提过了,有需要的可以自行去查看
现象展示:
<iframe allowfullscreen="true" data-mediaembed="csdn" frameborder="0" id="iqFHbMsi-1730939936636" src="https://live.csdn.net/v/embed/432906"></iframe>数码管简易计时器
总结:
本次的话主要是以两个小的实例去展示了动态数码管的一个思路和方法,关于数码管的实例还有很多,感兴趣的朋友可以自行去研究。数码管的知识到这里就结束了,有错误的地方希望大家可以指出,谢谢!
标签:wire,FPGA,seg,数码管,实例,key,parameter,reg From: https://blog.csdn.net/joker0518/article/details/143571465