一. 简介
这是FPGA之旅的第六个实例设计啦,驱动动态数码管,也是非常重要且基础的硬件电路。在数码管上,可以显示的字符有0-9和A-F,通过数码管,就可以将内部的相关信息直接显示出来,在学习初期使用的比较多,在课设中,一般利用数码管做数字时钟的比较多。其他例如可以显示DS18B20的温度数值和MPU6050传感器的ID值等等。
二. 硬件电路
每个数码管共有八个LED灯,分别标号为a-f和dp(dp通常用作小数点),点亮原理和普通的一样。从图中可以看到,八个数码管的八个LED灯连在了一起,进行了复用。这时候,通过上面的LED1-LED8片选来控制当前的数码管是否使能。使能了的数码管正常亮灭,没有使能的,始终处于灭的状态。
数码管共有两种接法,一种是共阴,即复用端给高电平,对应的LED亮,另外一种为共阳,即复用端给低电平,对应的LED亮
本实例使用的是共阳数码管。
三. Verilog代码实现—译码
当需要显示的字符为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
这样就完成了一个译码的模块,将需要显示的数据输入这个模块即可。
四. Verilog代码显示–数码管显示
一个数码管显示的话,就将译码过后的数据,输出即可。动态数码管,什么是动态呢?当数码管的个数达到两个以上的时候,要想显示两个不同的字符的话,这个时候,就需要在数码管的片选端来回切换,利用视觉残留效应,可以在多个数码管上显示不同的信息,来回切换的过程记为动态。
假如我们需要显示四个数据在数码管上,
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
五. testbeach编写
接下来就是编写测试模块了,这个模块也很容易,主要是看译码后的数据是否正确,二是数码管的片选端是否正常切换。
`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例之第六例 获取完整工程。
公众号:FPGA之旅