Augus

15.菜鸟初入FPGA之数码管

0
阅读(2693)

在电子系统中,通常都需要有输出设备来输出或显示一定的信息,以指 示当前系统运行的状态....FPGA则因为其独特的硬件结构,如果用RTL级电路来驱 动彩色液晶屏来显示一定的数据,势必是非常不划算的选择,而且驱动也极 为复杂。数码管作为一种能够直观显示一定数据信息的输出设备,具有驱动 简单、 显示直观的特点,尤其适合作为FPGA系统的输出设备。

数码管所谓的动态扫描,就是利用人眼的视觉暂留特性,在人眼能分辨 的变化速度以外,快速分时的点亮各个数码管及其对应的段。因为分别点亮 所有数码管一次所用时间小于人眼的视觉暂留,因此,在人们眼里看来,这 些数码管都是同时持续点亮的,并不会有闪烁的感觉。

因为数码管属于低速设备,其正常的扫描频率为500~10KHz,扫描频率 太快,会导致系统功耗增加,显示效果变暗。扫描频率太慢,会有明显的闪 烁感。本实验通过调试观察,选择以1KHz作为扫描频率,实际显示效果非常 好。 因此本实验首先就需要产生一个1KHz的扫描时钟,该时钟由系统时钟分 频得到。

设计结构:

2016-04-12_172959.jpg

2016-04-12_173240.jpg

代码如下:

//TOP module HEX8( clk, rst_n, En, disp_data, dig_sel, dig_seg ); input clk,rst_n,En; output [7:0]dig_sel;//weixuan output [6:0]dig_seg;//duanxuan input[31:0]disp_data; divider divider( .clk(clk), .rst_n(rst_n), .En(En), .div_clk(div_clk) ); // key_detect key_detect( // clk, // rst_n, // key_in // ); // // key_ctrl key_ctrl( // clk, // rst_n, // key_flag, // key_value, // data // ); dig_driver dig_driver( .div_clk(div_clk), .rst_n(rst_n), .disp_data(disp_data), .dig_sel(dig_sel), .dig_seg(dig_seg), .En(En) ); endmodule module divider( clk, rst_n, En, div_clk ); input clk,rst_n; input En; output reg div_clk; reg [14:0]div_cnt;//25000-1 // 分频计数器计数模块 always@(posedge clk or negedge rst_n) if(!rst_n) div_cnt <= 15'd0; else if(!En) div_cnt <= 15'd0; else if(div_cnt == 24999) div_cnt <= 15'd0; else div_cnt <= div_cnt + 1'b1; //1K扫描时钟生成模块 always@(posedge clk or negedge rst_n) if(!rst_n) div_clk <= 1'b0; else if(div_cnt == 24999) div_clk <= ~div_clk; else div_clk <= div_clk; endmodule module dig_driver( div_clk, rst_n, disp_data, dig_sel, data_tmp, dig_seg, En ); input div_clk,rst_n,En; input [31:0]disp_data; output [7:0]dig_sel; output reg[3:0]data_tmp;//cha zhao biao huan cun output reg[6:0]dig_seg; reg [7:0]sel; //8位循环移位寄存器 always@(posedge div_clk or negedge rst_n) if(!rst_n) sel <= 8'b0000_0001; else if(sel == 8'b1000_0000) sel <= 8'b0000_0001; else sel <= sel << 1; always@(*) case(dig_sel) 8'b0000_0001:data_tmp = disp_data[3:0]; 8'b0000_0010:data_tmp = disp_data[7:4]; 8'b0000_0100:data_tmp = disp_data[11:8]; 8'b0000_1000:data_tmp = disp_data[15:12]; 8'b0001_0000:data_tmp = disp_data[19:16]; 8'b0010_0000:data_tmp = disp_data[23:20]; 8'b0100_0000:data_tmp = disp_data[27:24]; 8'b1000_0000:data_tmp = disp_data[31:28]; default:data_tmp = 4'b0000; endcase always@(*) case(data_tmp) 4'h0: dig_seg = 7'b1000000; 4'h1: dig_seg = 7'b1111001; 4'h2: dig_seg = 7'b0100100; 4'h3: dig_seg = 7'b0110000; 4'h4: dig_seg = 7'b0011001; 4'h5: dig_seg = 7'b0010010; 4'h6: dig_seg = 7'b0000010; 4'h7: dig_seg = 7'b1111000; 4'h8: dig_seg = 7'b0000000; 4'h9: dig_seg = 7'b0010000; 4'ha: dig_seg = 7'b0001000; 4'hb: dig_seg = 7'b0000011; 4'hc: dig_seg = 7'b1000110; 4'hd: dig_seg = 7'b0100001; 4'he: dig_seg = 7'b0000110; 4'hf: dig_seg = 7'b0001110; endcase assign dig_sel = (En)?sel : 8'b0000_0000; endmodule `timescale 1ns/1ns `define clk_period 20 module HEX8_tb; reg clk; reg rst_n; reg En; reg [31:0]disp_data; wire [7:0] sel;//数码管位选 wire [6:0] seg;//数码管段选 HEX8 HEX8( .clk(clk), .rst_n(rst_n), .En(En), .disp_data(disp_data), .dig_sel(sel), .dig_seg(seg) ); initial clk = 1; always#(`clk_period/2) clk = ~clk; initial begin rst_n = 0; En = 1; disp_data = 32'h12345678; #(`clk_period*20); rst_n = 1; #(`clk_period*20); #20000000; $stop; end endmodule

仿真:

2016-04-12_211803.jpg

备注: 本程序整理来源为小梅哥数码管教程

后续设计通过按键控制数码管,(按键包括按键消抖前文都有描述),按键控制数码管不在此上传,有兴趣者可以联系博主一起讨论学习

Baidu
map