開拓者FPGA開發闆上有六個共陽極八段數位管,本實驗将完成數位管動态顯示。
數位管動态/靜态顯示差別
靜态顯示:
每一個管腳都用固定的一個電平去控制。
優點:能夠做到”同時”
缺點:管腳太多
動态顯示:
每一個數位管共用一套電路,顯示時隻需控制哪一個數位管進行顯示。
優點:大大減小了管腳的數量
缺點:一次隻能控制單獨一個數位管進行顯示,但可以快速切換數位管顯示,利用人眼的”視覺暫留"來“同步”進行顯示。
功能描述
動态控制數位管,使其每1ms從0開始累加1,按下複位鍵後重新開始計數。
計數子產品
先通過分頻,每0.1s輸出一個脈沖信号,數位管每接收到一個脈沖信号時,數值累加一次。
module count(
//mudule clock
input clk , // 時鐘信号
input rst_n, // 複位信号
//user interface
output reg [19:0] data , // 6個數位管要顯示的數值
output reg [ 5:0] point, // 小數點的位置,高電平點亮對應數位管位上的小數點
output reg en , // 數位管使能信号
output reg sign // 符号位,高電平時顯示負号,低電平不顯示負号
);
//parameter define
parameter MAX_NUM = 23'd5000_000; // 計數器計數的最大值
//reg define
reg [22:0] cnt ; // 計數器,用于計時100ms
reg flag; // 标志信号
//*****************************************************
//** main code
//*****************************************************
//計數器對系統時鐘計數達100ms時,輸出一個時鐘周期的脈沖信号
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 23'b0;
flag<= 1'b0;
end
else if (cnt < MAX_NUM - 1'b1) begin
cnt <= cnt + 1'b1;
flag<= 1'b0;
end
else begin
cnt <= 23'b0;
flag <= 1'b1;
end
end
//數位管需要顯示的資料,從0累加到999999
always @ (posedge clk or negedge rst_n) begin
if (!rst_n)begin
data <= 20'b0;
point <=6'b000000;
en <= 1'b0;
sign <= 1'b0;
end
else begin
point <= 6'b000000; //不顯示小數點
en <= 1'b1; //打開數位管使能信号
sign <= 1'b0; //不顯示負号
if (flag) begin //顯示數值每隔0.1s累加一次
if(data < 20'd999999)
data <= data +1'b1;
else
data <= 20'b0;
數位管顯示子產品
和靜态不同的是,需要通過一個額外的寄存器來控制數位管的選擇計數,這裡用的是 cnt_sel。
module seg_led(
input clk , // 時鐘信号
input rst_n , // 複位信号
input [19:0] data , // 6位數位管要顯示的數值
input [5:0] point , // 小數點具體顯示的位置,從高到低,高電平有效
input en , // 數位管使能信号
input sign , // 符号位(高電平顯示“-”号)
output reg [5:0] seg_sel, // 數位管位選,最左側數位管為最高位
output reg [7:0] seg_led // 數位管段選
);
//parameter define
localparam CLK_DIVIDE = 4'd10 ; // 時鐘分頻系數
localparam MAX_NUM = 13'd5000 ; // 對數位管驅動時鐘(5MHz)計數1ms所需的計數值
//reg define
reg [ 3:0] clk_cnt ; // 時鐘分頻計數器
reg dri_clk ; // 數位管的驅動時鐘,5MHz
reg [23:0] num ; // 24位bcd碼寄存器
reg [12:0] cnt0 ; // 數位管驅動時鐘計數器
reg flag ; // 标志信号(标志着cnt0計數達1ms)
reg [2:0] cnt_sel ; // 數位管位選計數器
reg [3:0] num_disp ; // 目前數位管顯示的資料
reg dot_disp ; // 目前數位管顯示的小數點
//wire define
wire [3:0] data0 ; // 個位數
wire [3:0] data1 ; // 十位數
wire [3:0] data2 ; // 百位數
wire [3:0] data3 ; // 千位數
wire [3:0] data4 ; // 萬位數
wire [3:0] data5 ; // 十萬位數
//*****************************************************
//** main code
//*****************************************************
//提取顯示數值所對應的十進制數的各個位
assign data0 = data % 4'd10; // 個位數
assign data1 = data / 4'd10 % 4'd10 ; // 十位數
assign data2 = data / 7'd100 % 4'd10 ; // 百位數
assign data3 = data / 10'd1000 % 4'd10 ; // 千位數
assign data4 = data / 14'd10000 % 4'd10; // 萬位數
assign data5 = data / 17'd100000; // 十萬位數
//對系統時鐘10分頻,得到的頻率為5MHz的數位管驅動時鐘dri_clk
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
clk_cnt <= 4'd0;
dri_clk <= 1'b1;
end
else if(clk_cnt == CLK_DIVIDE/2 - 1'd1) begin
clk_cnt <= 4'd0;
dri_clk <= ~dri_clk;
end
else begin
clk_cnt <= clk_cnt + 1'b1;
dri_clk <= dri_clk;
end
end
//将20位2進制數轉換為8421bcd碼(即使用4位二進制數表示1位十進制數)
always @ (posedge dri_clk or negedge rst_n) begin
if (!rst_n)
num <= 24'b0;
else begin
if (data5 || point[5]) begin //如果顯示資料為6位十進制數,
num[23:20] <= data5; //則依次給6位數位管指派
num[19:16] <= data4;
num[15:12] <= data3;
num[11:8] <= data2;
num[ 7:4] <= data1;
num[ 3:0] <= data0;
end
else begin
if (data4 || point[4]) begin //如果顯示資料為5位十進制數,則給低5位數位管指派
num[19:0] <= {data4,data3,data2,data1,data0};
if(sign)
num[23:20] <= 4'd11; //如果需要顯示負号,則最高位(第6位)為符号位
else
num[23:20] <= 4'd10; //不需要顯示負号時,則第6位不顯示任何字元
end
else begin //如果顯示資料為4位十進制數,則給低4位數位管指派
if (data3 || point[3]) begin
num[15: 0] <= {data3,data2,data1,data0};
num[23:20] <= 4'd10; //第6位不顯示任何字元
if(sign) //如果需要顯示負号,則最高位(第5位)為符号位
num[19:16] <= 4'd11;
else //不需要顯示負号時,則第5位不顯示任何字元
num[19:16] <= 4'd10;
end
else begin //如果顯示資料為3位十進制數,則給低3位數位管指派
if (data2 || point[2]) begin
num[11: 0] <= {data2,data1,data0};
//第6、5位不顯示任何字元
num[23:16] <= {2{4'd10}};
if(sign) //如果需要顯示負号,則最高位(第4位)為符号位
num[15:12] <= 4'd11;
else //不需要顯示負号時,則第4位不顯示任何字元
num[15:12] <= 4'd10;
end
else begin //如果顯示資料為2位十進制數,則給低2位數位管指派
if (data1 || point[1]) begin
num[ 7: 0] <= {data1,data0};
//第6、5、4位不顯示任何字元
num[23:12] <= {3{4'd10}};
if(sign) //如果需要顯示負号,則最高位(第3位)為符号位
num[11:8] <= 4'd11;
else //不需要顯示負号時,則第3位不顯示任何字元
num[11:8] <= 4'd10;
end
else begin //如果顯示資料為1位十進制數,則給最低位數位管指派
num[3:0] <= data0;
//第6、5位不顯示任何字元
num[23:8] <= {4{4'd10}};
if(sign) //如果需要顯示負号,則最高位(第2位)為符号位
num[7:4] <= 4'd11;
else //不需要顯示負号時,則第2位不顯示任何字元
num[7:4] <= 4'd10;
end
end
end
end
end
end
end
//每當計數器對數位管驅動時鐘計數時間達1ms,輸出一個時鐘周期的脈沖信号
always @ (posedge dri_clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
cnt0 <= 13'b0;
flag <= 1'b0;
end
else if (cnt0 < MAX_NUM - 1'b1) begin
cnt0 <= cnt0 + 1'b1;
flag <= 1'b0;
end
else begin
cnt0 <= 13'b0;
flag <= 1'b1;
end
end
//cnt_sel從0計數到5,用于選擇目前處于顯示狀态的數位管
always @ (posedge dri_clk or negedge rst_n) begin
if (rst_n == 1'b0)
cnt_sel <= 3'b0;
else if(flag) begin
if(cnt_sel < 3'd5)
cnt_sel <= cnt_sel + 1'b1;
else
cnt_sel <= 3'b0;
end
else
cnt_sel <= cnt_sel;
end
//控制數位管位選信号,使6位數位管輪流顯示
always @ (posedge dri_clk or negedge rst_n) begin
if(!rst_n) begin
seg_sel <= 6'b111111; //位選信号低電平有效
num_disp <= 4'b0;
dot_disp <= 1'b1; //共陽極數位管,低電平導通
end
else begin
if(en) begin
case (cnt_sel)
3'd0 :begin
seg_sel <= 6'b111110; //顯示數位管最低位
num_disp <= num[3:0] ; //顯示的資料
dot_disp <= ~point[0]; //顯示的小數點
end
3'd1 :begin
seg_sel <= 6'b111101; //顯示數位管第1位
num_disp <= num[7:4] ;
dot_disp <= ~point[1];
end
3'd2 :begin
seg_sel <= 6'b111011; //顯示數位管第2位
num_disp <= num[11:8];
dot_disp <= ~point[2];
end
3'd3 :begin
seg_sel <= 6'b110111; //顯示數位管第3位
num_disp <= num[15:12];
dot_disp <= ~point[3];
end
3'd4 :begin
seg_sel <= 6'b101111; //顯示數位管第4位
num_disp <= num[19:16];
dot_disp <= ~point[4];
end
3'd5 :begin
seg_sel <= 6'b011111; //顯示數位管最高位
num_disp <= num[23:20];
dot_disp <= ~point[5];
end
default :begin
seg_sel <= 6'b111111;
num_disp <= 4'b0;
dot_disp <= 1'b1;
end
endcase
end
else begin
seg_sel <= 6'b111111; //使能信号為0時,所有數位管均不顯示
num_disp <= 4'b0;
dot_disp <= 1'b1;
end
end
end
//控制數位管段選信号,顯示字元
always @ (posedge dri_clk or negedge rst_n) begin
if (!rst_n)
seg_led <= 8'hc0;
else begin
case (num_disp)
4'd0 : seg_led <= {dot_disp,7'b1000000}; //顯示數字 0
4'd1 : seg_led <= {dot_disp,7'b1111001}; //顯示數字 1
4'd2 : seg_led <= {dot_disp,7'b0100100}; //顯示數字 2
4'd3 : seg_led <= {dot_disp,7'b0110000}; //顯示數字 3
4'd4 : seg_led <= {dot_disp,7'b0011001}; //顯示數字 4
4'd5 : seg_led <= {dot_disp,7'b0010010}; //顯示數字 5
4'd6 : seg_led <= {dot_disp,7'b0000010}; //顯示數字 6
4'd7 : seg_led <= {dot_disp,7'b1111000}; //顯示數字 7
4'd8 : seg_led <= {dot_disp,7'b0000000}; //顯示數字 8
4'd9 : seg_led <= {dot_disp,7'b0010000}; //顯示數字 9
4'd10: seg_led <= 8'b11111111; //不顯示任何字元
4'd11: seg_led <= 8'b10111111; //顯示負号(-)
default:
seg_led <= {dot_disp,7'b1000000};
頂層子產品
module top_seg_led(
//global clock
input sys_clk , // 全局時鐘信号
input sys_rst_n, // 複位信号(低有效)
//seg_led interface
output [5:0] seg_sel , // 數位管位選信号
output [7:0] seg_led // 數位管段選信号
);
//wire define
wire [19:0] data; // 數位管顯示的數值
wire [ 5:0] point; // 數位管小數點的位置
wire en; // 數位管顯示使能信号
wire sign; // 數位管顯示資料的符号位
//*****************************************************
//** main code
//*****************************************************
//計數器子產品,産生數位管需要顯示的資料
count u_count(
.clk (sys_clk ), // 時鐘信号
.rst_n (sys_rst_n), // 複位信号
.data (data ), // 6位數位管要顯示的數值
.point (point ), // 小數點具體顯示的位置,高電平有效
.en (en ), // 數位管使能信号
.sign (sign ) // 符号位
);
//數位管動态顯示子產品
seg_led u_seg_led(
.clk (sys_clk ), // 時鐘信号
.rst_n (sys_rst_n), // 複位信号
.data (data ), // 顯示的數值
.point (point ), // 小數點具體顯示的位置,高電平有效
.en (en ), // 數位管使能信号
.sign (sign ), // 符号位,高電平顯示負号(-)
.seg_sel (seg_sel ), // 位選
.seg_led (seg_led ) // 段選
);