背景
此篇文章是4x4矩陣鍵盤的衍生品。
4x4的矩陣鍵盤一共16個按鍵,每個按鍵對應相應的鍵值,分別爲0~15。每按一個鍵,對應的鍵值並聯到4個LED燈上,以二進制的形式表示(比如鍵值5,二進制爲'b0101
,對應的LED燈亮滅情況爲:亮滅亮滅),同時也並聯到數碼管上,通過數碼管顯示出目前對應的鍵值。
然而,一個數碼管足夠顯示0~F(即0到15)這16個鍵值,如果想完完全全轉換成十進制來顯示,即鍵值10顯示爲10
而非a
,鍵值11顯示爲11
而非b
,那該如何操作呢?
以上即爲不安分守己的瞎折騰背景。
開始
之前,寫過一篇關於小梅哥74HC595驅動設計的思考,該驅動可充分利用兩片級聯的74HC595芯片,將16位串行數據轉換爲16位並行數據。實際應用中,由於開發板上有8個數碼管;每個數碼管又有8個段,正好16位數據。驅動將16位並行數據data[15:0]
輸入,輸出串行的16位數據ds
,並利用兩片級聯芯片將16位數據ds
再次轉爲並行數據輸出,以此驅動數碼管,並顯示相應數據。選擇對應的數碼管(以sel[7:0]
信號表示),數碼管上對應需要顯示的段(以seg[7:0]
信號表示)。
目前74HC595驅動已經寫好,只等數碼管的位選和段選數據輸入。產生16位數據的verilog文件以梅哥的hex8.v文件爲參考,並作些許修改。其中有一兩個小技巧一同分享。
分析
大於等於10的數據,其二進制編碼有以下特點:
- 最高位
dis_data[3]
永遠爲1; dis_data[2]
和dis_data[1]
取邏輯或,其值永遠爲1;
雖然鍵值8和9的最高位也爲1,但是其
dis_data[2]
和dis_data[1]
爲0,兩者相或,值爲0.
以上,足以利用該特點進行兩個數碼管顯示0~15,即令
wire oct = dis_data[3] && (dis_data[2] || dis_data[1]) ;
(英文oct即爲十進制的意思)
當oct == 1
,即鍵值大於等於10,選擇兩個數碼管進行顯示,並將第二個數碼管永遠置爲1;
當oct == 0
,既鍵值小於10,只需選擇一個數碼管進行顯示。
產生位選sel[7:0]
和段選seg[7:0]
的代碼如下:
/*=============================================================================
# FileName: hex8.v
# Desc: copy from 小梅哥
# Author: 小梅哥
# Email:
# HomePage:
# Version: 0.0.1
# LastChange: 2019-11-30 16:49:52
# History:
=============================================================================*/
module hex8(
clk_50M ,
rstn ,
en_hex8 ,
dis_data,
sel ,
seg
);
input clk_50M ;
input rstn ;
input en_hex8 ;
input [3:0] dis_data ;
output [7:0] sel ;
output reg [7:0] seg ;
reg [15:0] cnt ;
reg clk_1K;
reg [7 :0] select; //定義一箇中間寄存器,數碼管位選信號sel用來調用,詳見代碼第74行
parameter CNT_NUM = 16'd25_000;
wire oct = (dis_data[3] && (dis_data[2] || dis_data[1]));
//divide clk_50M to clk_1K
//用頻率爲1K的時鐘進行8個數碼管的掃描
always @ (posedge clk_50M or negedge rstn) begin
if (!rstn)
cnt <= 16'h0;
else if (!en_hex8)
cnt <= 16'h0;
else begin
if (cnt == CNT_NUM/2 - 1)
cnt <= 16'h0;
else
cnt <= cnt + 1'b1;
end
end
always @ (posedge clk_50M or negedge rstn) begin
if (!rstn)
clk_1K <= 1'b0;
else if (cnt == CNT_NUM/2 - 1)
clk_1K <= ~clk_1K;
else
clk_1K <= clk_1K;
end
//gate control clk
//scanning the hex
always @ (posedge clk_1K or negedge rstn) begin
if (!rstn)
select <= 8'b0000_0001;
else if (oct) begin
if (select == 8'b0000_0010) //只選擇末端兩個數碼管
select <= 8'b0000_0001;
else
select <= select << 1;
end
else
select <= 8'b0000_0001;
end
assign sel = (en_hex8 ? (oct ? select : 8'b0000_0001) : 8'b0);
reg [3:0] data_tmp;
always @ (*) begin
if (oct) begin //如果鍵值大於等於10
case (sel)
8'b0000_0001 : data_tmp = dis_data[2 : 0];
8'b0000_0010 : data_tmp = dis_data[3 ];
default : data_tmp = 4'b0 ;
endcase
end
else //如果鍵值小於10
data_tmp = dis_data[3 : 0];
end
always @ (*) begin
if (oct) begin
case (data_tmp)
3'd1 : seg = 8'b1111_1001; //第二個數碼管顯示1
3'd2 : seg = 8'b1100_0000; //第一個數碼管顯示其他數
3'd3 : seg = 8'b1111_1001;
3'd4 : seg = 8'b1010_0100;
3'd5 : seg = 8'b1011_0000;
3'd6 : seg = 8'b1001_1001;
3'd7 : seg = 8'b1001_0010;
default : seg = 8'b1111_1111;
endcase
end
else
case(data_tmp)
4'd0 : seg = 8'b1100_0000;
4'd1 : seg = 8'b1111_1001;
4'd2 : seg = 8'b1010_0100;
4'd3 : seg = 8'b1011_0000;
4'd4 : seg = 8'b1001_1001;
4'd5 : seg = 8'b1001_0010;
4'd6 : seg = 8'b1000_0010;
4'd7 : seg = 8'b1111_1000;
4'd8 : seg = 8'b1000_0000;
4'd9 : seg = 8'b1001_0000;
default : seg = 8'b1111_1111;
endcase
end
endmodule
比較巧妙的是,當鍵值大於等於10,將dis_data
拆分成dis_data[3]
和dis_data[2:0]
;由於dis_data[3] == 1'b1
,且
- 當鍵值爲10,
dis_data[2:0] == 2
; - 當鍵值爲11,
dis_data[2:0] == 3
; - 當鍵值爲12,
dis_data[2:0] == 4
; - …
而在case語句中,最高位dis_data[3]
的取值正好不與後三位dis_data[2:0]
的取值重合,故當oct == 1
時,可直接在case語句中判斷整個dis_data[3:0]
的情況,並將其在數碼管上顯示出來。
將該文件的輸出sel[7:0]
,seg[7:0]
,以{seg[7:0], sel[7:0]}
的順序位拼接並連上74HC595驅動上的data
端口,調用ISSP可實現兩個數碼管顯示16位數據。兩文件調用方式如下:
module hex_top(
clk ,
rstn ,
ds ,
shcp ,
stcp
);
input clk ;
input rstn ;
output ds ;
output shcp ;
output stcp ;
wire [3:0] dis_data;
wire [7:0] sel ;
wire [7:0] seg ;
hex_data_ISSP U_hex_data(
.probe ( ),
.source (dis_data)
);
hex8 U_hex8(
.clk_50M (clk ),
.rstn (rstn ),
.en_hex8 (1'b1 ),
.dis_data (dis_data),
.sel (sel ),
.seg (seg )
);
HC595_driver U0_HC595_driver(
.clk (clk ),
.rstn (rstn ),
.data ({seg, sel} ),
.en (1'b1 ),
.ds (ds ),
.shcp (shcp ),
.stcp (stcp )
);
endmodule
實際效果
由於此文作爲矩陣鍵盤的一個衍生品,故實際驗證的過程中本人將矩陣鍵盤與數碼管顯示相結合,當然調用ISSP只是爲了降低此文寫作難度(矩陣鍵盤還沒想好怎麼寫–!),視頻如下:
FPGA兩段數碼管顯示0~15的數字