这是FPGA之旅的第六个实例设计啦,驱动动态数码管,也是非常重要且基础的硬件电路。在数码管上,可以显示的字符有0-9和A-F,通过数码管,就可以将内部的相关信息直接显示出来,在学习初期使用的比较多,在课设中,一般利用数码管做数字时钟的比较多。其他例如可以显示DS18B20的温度数值和MPU6050传感器的ID值等等。
每个数码管共有八个LED灯,分别标号为a-f和dp(dp通常用作小数点),点亮原理和普通的一样。从图中可以看到,八个数码管的八个LED灯连在了一起,进行了复用。这时候,通过上面的LED1-LED8片选来控制当前的数码管是否使能。使能了的数码管正常亮灭,没有使能的,始终处于灭的状态。
数码管共有两种接法,一种是共阴,即复用端给高电平,对应的LED亮,另外一种为共阳,即复用端给低电平,对应的LED亮

本实例使用的是共阳数码管。
当需要显示的字符为2的时候,很显然,并不能直接给这八个LED灯赋值为2,而是需要经过一个译码,将2转成数码管上显示2所对应的量。译码的过程也很容易,是个体力活,将2对应到数码管上,可以看到需要点亮的LED灯有:a,b,g,c,d,其余的LED灯均灭。如果按照{dp,g,f,e,d,c,b,a}排列组成一个byte的话,那么显示2对应的byte值为8’b1010_0100也就是0xA4,其余字符的译码也是如此。对应成代码如下(只译码了0-9)
module Decode(
input[2:0] in_data, //输入的数据
output reg[7:0] out_data //解码输出
);
always@(*)
begin
case(in_data)
'd0: out_data <= 8'b1100_0000;
'd1: out_data <= 8'b1111_1001;
'd2: out_data <= 8'b1010_0100;
'd3: out_data <= 8'b1011_0000;
'd4: out_data <= 8'b1001_1001;
'd5: out_data <= 8'b1001_0010;
'd6: out_data <= 8'b1000_0010;
'd7: out_data <= 8'b1111_1000;
'd8: out_data <= 8'b1000_0000;
'd9: out_data <= 8'b1001_0000;
default: out_data <= 8'hff;
endcase
end
这样就完成了一个译码的模块,将需要显示的数据输入这个模块即可。
一个数码管显示的话,就将译码过后的数据,输出即可。动态数码管,什么是动态呢?当数码管的个数达到两个以上的时候,要想显示两个不同的字符的话,这个时候,就需要在数码管的片选端来回切换,利用视觉残留效应,可以在多个数码管上显示不同的信息,来回切换的过程记为动态。
假如我们需要显示四个数据在数码管上,
module Seg_scan(
input clk,
input rst,
output reg[3:0] sel, //数码管片选,低电平选中
output reg[7:0] dig, //数码管LED
//经译码过后的数据
input[7:0] seg_data_0,
input[7:0] seg_data_1,
input[7:0] seg_data_2,
input[7:0] seg_data_3
);
parameter SCAN_FREQ = 100000; //扫描频率
reg[30:0] scan_cnt;
reg[3:0] scan_sel; //扫描选择
always@(posedge clk or negedge rst)
begin
if(rst == 1'b0)
scan_cnt <= 'd0;
else if(scan_cnt >= SCAN_FREQ)
scan_cnt <= 'd0;
else
scan_cnt <= scan_cnt + 1'b1;
end
//选择数码管
always@(posedge clk or negedge rst)
begin
if(rst == 1'b0)
scan_sel <= 'd0;
else if(scan_cnt >= SCAN_FREQ)
if(scan_sel >= 3'b100)
scan_sel <= 3'b000;
else
scan_sel <= scan_sel + 1'b1;
end
always@(posedge clk or negedge rst)
begin
if(rst == 1'b0)
begin
sel <= 4'b1111;
dig <= 8'hff;
end
else
case(scan_sel)
3'b000:
begin
sel <= 4'b1101;
dig <= seg_data_0;
end
3'b001:
begin
sel <= 4'b1110;
dig <= seg_data_1;
end
3'b010:
begin
sel <= 4'b1011;
dig <= seg_data_2;
end
3'b011:
begin
sel <= 4'b0111;
dig <= seg_data_3;
end
3'b100:
begin
sel <= 4'b0000;
dig <= 8'hff;
end
default
begin
sel <= 4'b1111;
dig <= 8'hff;
end
endcase
end
endmodule
接下来就是编写测试模块了,这个模块也很容易,主要是看译码后的数据是否正确,二是数码管的片选端是否正常切换。
`timescale 1ns/1ps
module testbeach();
reg clk;
reg rst;
reg[2:0] in_data0;
reg[2:0] in_data1;
reg[2:0] in_data2;
reg[2:0] in_data3;
wire[7:0] dedata0;
wire[7:0] dedata1;
wire[7:0] dedata2;
wire[7:0] dedata3;
wire[3:0] sel; //数码管片选
wire[7:0] dig; //数码管数据
always #50 clk <= ~clk;
initial begin
clk = 1'b0;
rst = 1'b1;
in_data0 <= 'd1;
in_data1 <= 'd3;
in_data2 <= 'd5;
in_data3 <= 'd8;
#100
rst = 1'b0;
#100
rst = 1'b1;
end
Decode DecodeHP1(
.in_data (in_data0), //输入的数据
.out_data (dedata0) //解码输出
);
Decode DecodeHP2(
.in_data (in_data1), //输入的数据
.out_data (dedata1) //解码输出
);
Decode DecodeHP3(
.in_data (in_data2), //输入的数据
.out_data (dedata2) //解码输出
);
Decode DecodeHP4(
.in_data (in_data3), //输入的数据
.out_data (dedata3) //解码输出
);
Seg_scan Seg_scanHP(
.clk (clk),
.rst (rst),
.sel (sel),
.dig (dig),
.seg_data_0 (dedata0),
.seg_data_1 (dedata1),
.seg_data_2 (dedata2),
.seg_data_3 (dedata3)
);
endmodule

仿真波形如上图所示。可以看到片选和所译码给出的数据是一致的,也就是说我们的数码管可以正常显示啦。
欢迎关注微信公众号 回复 FPGA之旅设计99例之第六例 获取完整工程。