本次实验我们主要进行数码管的显示,包括单个数码管的显示和多个数码管刷新显示。
在很多的实际中我们都会使用到数码管,在学习单片机的时候应该对数码管的基本原理有所了解,一个数码管有8个小LED组成,控制每一个LED的亮灭即可实现数码管显示不同的形状,这就是单个数码管显示数字的原理。多个数码管的显示有所不同,将每一个数码管所有的LED的负极连接在一起,只控制正极即可实现共阴级控制,同理实现共阳极控制。我们将多个共阳极数码管的所有对应的阴级连接到一起,对应的阳极单独控制,就实现了多个数码管的级联,多个数码管控制主要利用了视觉暂留,每一个时刻显示一个数码管,不断刷新就可以显示为多个数码管同时显示。
本次博主使用的开发板上共有6个共阳极数码管,电路使用PNP管来进行反向驱动,具体的电路如下所示。
一、单个数码管的显示
本次我们主要是实现单个数码管按秒显示0到9。
对于共阳极单个数码管,显示数字和字符编码如下。
我们来分析一下这个程序,程序输入有时钟信号,复位信号,一个计时器计时一秒,当计数到9的时候归零,按照计数器的数字在数码管上显示不同的数字。因此我们需要两个always结构,一个进行计数,另一个进行数码管的显示。输出为8位驱动信号,因此我们写出以下的程序。
`timescale 1n/1ps
module seg_test(
input clk,
input rst,
output seg_pi,
output [7:0] seg_data
);
reg[31:0]time_cnt;
reg[7:0]num_cnt;
always@(posedge clk or negedge rst)
begin
if(rst==1'b0)
begin
time_cnt<=32'd0;
end
else if(time_cnt==32'd49_000_000)
begin
time_cnt<=0;
if(num_cnt==8'd10)
begin
num_cnt<=0;
end
else
begin
num_cnt<=num_cnt+1;
end
end
else
begin
time_cnt<=time_cnt+32'd1;
end
end
reg[7:0] seg_get_data;
always@(posedge clk)
begin
if(num_cnt==8'd0)
begin
seg_get_data<=8'b1100_0000;
end
else if(num_cnt==8'd1)
begin
seg_get_data<=8'b1111_1001;
end
else if(num_cnt==8'd2)
begin
seg_get_data<=8'b1010_0100;
end
else if(num_cnt==8'd3)
begin
seg_get_data<=8'b1011_0000;
end
else if(num_cnt==8'd4)
begin
seg_get_data<=8'b1001_1001;
end
else if(num_cnt==8'd5)
begin
seg_get_data<=8'b1001_0010;
end
else if(num_cnt==8'd6)
begin
seg_get_data<=8'b1000_0010;
end
else if(num_cnt==8'd7)
begin
seg_get_data<=8'b1111_1000;
end
else if(num_cnt==8'd8)
begin
seg_get_data<=8'b1000_0000;
end
else if(num_cnt==8'd9)
begin
seg_get_data<=8'b1001_0000;
end
end
assign seg_data=seg_get_data;
endmodule
下载程序后发现实现了我们要求的结果,程序运行结果如下。
上述实验的工程下载地址:
二、多位数码管显示
本次试验主要实现多位数码管实现一个计数器,每秒增加1。
首先我们分析一下,多位数码管相对于单个数码管而言,多了一个数码管刷新频率,我们只需要新增加一个计数器,然后按照该计数器的计数值来对应控制我们某一位数码管即可。具体来说就是当该计数器为0,我们就接通第一个数码管,关闭其他数码管,同时将之前按秒计数的计数器的个位数值,通过数据总线输出,当该计数器为1时,我们接通第二个数码管,将按秒计数的计数器的十位数值,通过数据总线输出,以此类推完成整个按秒计数器的输出,当计数器到6时,我们不能直接清零,因为如果刷新频率过快,数码管会有重影,此时我们将所有的数码管同时接通,将所有数码管的数据位都输出为空,完成数码管的清零。此时就可以完成按秒计数器的计数值的显示。
按照这个逻辑,我们写出程序如下所示,运行改程序可以看见,完成了我们需要的功能,实验结果如下图所示。
`timescale 1n/1ps
module seg_test(
input clk,
input rst,
output [5:0]seg_pi,//片选
output [7:0] seg_data//数据位
);
reg[31:0]time_cnt;//时间计数器,用作计数器的计时时钟
reg[31:0]time_cnt2;//时间计数器2 用作刷新数码管的时钟
reg[31:0] all_num_cnt;//保存当前的计数
reg[7:0] wei_cnt_clk;//判断输出哪一个数码管
always@(posedge clk or negedge rst)
begin
if(rst==1'b0)
begin
time_cnt<=32'd0;
end
else if(time_cnt==32'd49_000_000)//计时一秒 或者其他数值 49_999_999表示一秒
begin
time_cnt<=0;
if(all_num_cnt==32'd999999)//时钟计数器 加到一定的值清零
begin
all_num_cnt<=0;
end
else
begin
all_num_cnt<=all_num_cnt+1;
end
end
else//时钟每秒加一
begin
time_cnt<=time_cnt+32'd1;
end
end
//数码管刷新时间 计数
always@(posedge clk or negedge rst)
begin
if(rst==1'b0)
begin
time_cnt2<=32'd0;
end
else if(time_cnt2==32'd1_000)
begin
time_cnt2<=32'd0;
if(wei_cnt_clk==8'd7)
begin
wei_cnt_clk<=0;
end
else
begin
wei_cnt_clk<=wei_cnt_clk+1;
end
end
else
begin
time_cnt2<=time_cnt2+32'd1;
end
end
reg[7:0] seg_get_data;//数码管编码后的显示 端选信号
reg[5:0] seg_pi_data;//片选信号
reg[7:0] seg_num;//数码管需要显示的数字
//数码管显示
always@(posedge clk)
begin
if(wei_cnt_clk==8'd0)//如果计时为0 刷新第一个数码管
begin
seg_pi_data<=6'b01_1111;//0代表点亮 选择第一个数码管
seg_num<=all_num_cnt%10;//显示个位
if(seg_num==8'd0)
begin
seg_get_data<=8'b1100_0000;//0的数码管编码
end
else if(seg_num==8'd1)
begin
seg_get_data<=8'b1111_1001;//1的编码
end
else if(seg_num==8'd2)
begin
seg_get_data<=8'b1010_0100;
end
else if(seg_num==8'd3)
begin
seg_get_data<=8'b1011_0000;
end
else if(seg_num==8'd4)
begin
seg_get_data<=8'b1001_1001;
end
else if(seg_num==8'd5)
begin
seg_get_data<=8'b1001_0010;
end
else if(seg_num==8'd6)
begin
seg_get_data<=8'b1000_0010;
end
else if(seg_num==8'd7)
begin
seg_get_data<=8'b1111_1000;
end
else if(seg_num==8'd8)
begin
seg_get_data<=8'b1000_0000;
end
else if(seg_num==8'd9)
begin
seg_get_data<=8'b1001_0000;
end
end
else if(wei_cnt_clk==8'd1)
begin
seg_pi_data<=6'b10_1111;//0代表点亮
seg_num<=all_num_cnt/10%10;
if(seg_num==8'd0)
begin
seg_get_data<=8'b1100_0000;
end
else if(seg_num==8'd1)
begin
seg_get_data<=8'b1111_1001;
end
else if(seg_num==8'd2)
begin
seg_get_data<=8'b1010_0100;
end
else if(seg_num==8'd3)
begin
seg_get_data<=8'b1011_0000;
end
else if(seg_num==8'd4)
begin
seg_get_data<=8'b1001_1001;
end
else if(seg_num==8'd5)
begin
seg_get_data<=8'b1001_0010;
end
else if(seg_num==8'd6)
begin
seg_get_data<=8'b1000_0010;
end
else if(seg_num==8'd7)
begin
seg_get_data<=8'b1111_1000;
end
else if(seg_num==8'd8)
begin
seg_get_data<=8'b1000_0000;
end
else if(seg_num==8'd9)
begin
seg_get_data<=8'b1001_0000;
end
end
else if(wei_cnt_clk==8'd2)
begin
seg_pi_data<=6'b11_0111;//0代表点亮
seg_num<=all_num_cnt/100%10;
if(seg_num==8'd0)
begin
seg_get_data<=8'b1100_0000;
end
else if(seg_num==8'd1)
begin
seg_get_data<=8'b1111_1001;
end
else if(seg_num==8'd2)
begin
seg_get_data<=8'b1010_0100;
end
else if(seg_num==8'd3)
begin
seg_get_data<=8'b1011_0000;
end
else if(seg_num==8'd4)
begin
seg_get_data<=8'b1001_1001;
end
else if(seg_num==8'd5)
begin
seg_get_data<=8'b1001_0010;
end
else if(seg_num==8'd6)
begin
seg_get_data<=8'b1000_0010;
end
else if(seg_num==8'd7)
begin
seg_get_data<=8'b1111_1000;
end
else if(seg_num==8'd8)
begin
seg_get_data<=8'b1000_0000;
end
else if(seg_num==8'd9)
begin
seg_get_data<=8'b1001_0000;
end
end
else if(wei_cnt_clk==8'd3)
begin
seg_pi_data<=6'b11_1011;//0代表点亮
seg_num<=all_num_cnt/1000%10;
if(seg_num==8'd0)
begin
seg_get_data<=8'b1100_0000;
end
else if(seg_num==8'd1)
begin
seg_get_data<=8'b1111_1001;
end
else if(seg_num==8'd2)
begin
seg_get_data<=8'b1010_0100;
end
else if(seg_num==8'd3)
begin
seg_get_data<=8'b1011_0000;
end
else if(seg_num==8'd4)
begin
seg_get_data<=8'b1001_1001;
end
else if(seg_num==8'd5)
begin
seg_get_data<=8'b1001_0010;
end
else if(seg_num==8'd6)
begin
seg_get_data<=8'b1000_0010;
end
else if(seg_num==8'd7)
begin
seg_get_data<=8'b1111_1000;
end
else if(seg_num==8'd8)
begin
seg_get_data<=8'b1000_0000;
end
else if(seg_num==8'd9)
begin
seg_get_data<=8'b1001_0000;
end
end
else if(wei_cnt_clk==8'd4)
begin
seg_pi_data<=6'b11_1101;//0代表点亮
seg_num<=all_num_cnt/10000%10;
if(seg_num==8'd0)
begin
seg_get_data<=8'b1100_0000;
end
else if(seg_num==8'd1)
begin
seg_get_data<=8'b1111_1001;
end
else if(seg_num==8'd2)
begin
seg_get_data<=8'b1010_0100;
end
else if(seg_num==8'd3)
begin
seg_get_data<=8'b1011_0000;
end
else if(seg_num==8'd4)
begin
seg_get_data<=8'b1001_1001;
end
else if(seg_num==8'd5)
begin
seg_get_data<=8'b1001_0010;
end
else if(seg_num==8'd6)
begin
seg_get_data<=8'b1000_0010;
end
else if(seg_num==8'd7)
begin
seg_get_data<=8'b1111_1000;
end
else if(seg_num==8'd8)
begin
seg_get_data<=8'b1000_0000;
end
else if(seg_num==8'd9)
begin
seg_get_data<=8'b1001_0000;
end
end
else if(wei_cnt_clk==8'd5)
begin
seg_pi_data<=6'b11_1110;//0代表点亮
seg_num<=all_num_cnt/100000%10;
if(seg_num==8'd0)
begin
seg_get_data<=8'b1100_0000;
end
else if(seg_num==8'd1)
begin
seg_get_data<=8'b1111_1001;
end
else if(seg_num==8'd2)
begin
seg_get_data<=8'b1010_0100;
end
else if(seg_num==8'd3)
begin
seg_get_data<=8'b1011_0000;
end
else if(seg_num==8'd4)
begin
seg_get_data<=8'b1001_1001;
end
else if(seg_num==8'd5)
begin
seg_get_data<=8'b1001_0010;
end
else if(seg_num==8'd6)
begin
seg_get_data<=8'b1000_0010;
end
else if(seg_num==8'd7)
begin
seg_get_data<=8'b1111_1000;
end
else if(seg_num==8'd8)
begin
seg_get_data<=8'b1000_0000;
end
else if(seg_num==8'd9)
begin
seg_get_data<=8'b1001_0000;
end
end
else if(wei_cnt_clk==8'd6)
begin
seg_pi_data<=6'b00_0000;//0代表点亮
seg_get_data<=8'b1111_1111;
end
end
assign seg_data=seg_get_data;//将寄存器与输出管教相连
assign seg_pi=seg_pi_data;
endmodule
我们可以看到上述程序虽然实现了需求的功能,但是最终的程序有点啰嗦,我们可以将后面的重复代码写出一个程序,传相应的参数进去即可。程序博主之后再进行修改。
多位数码管显示结果如下图所示
本次多数码管显示的工程文件地址如下: https://download.csdn.net/download/qq_34020487/12258982