99在线精品免费视频九九视-99在线精品视频-99在线精品视频免费观里-99在线精品视频在线观看-99在线免费播放



官方論壇
官方淘寶
官方博客
微信公眾號
點擊聯系吳工 點擊聯系周老師

【案例】矩陣按鍵檢測

發布時間:2023-04-13   作者:admin 瀏覽量:

本文為明德揚原創及錄用文章,轉載請注明出處!

1.1 總體設計

1.1.1 概述

在鍵盤中按鍵數量較多時,為了減少I/O口的占用,通常將按鍵排列成矩陣形式。在矩陣式鍵盤中,每條水平線和垂直線在交叉處不直接連通,而是通過一個按鍵加以連接。這樣,一個端口(如P1口)就可以構成4*4=16個按鍵,比之直接將端口線用于鍵盤多出了一倍,而且線數越多,區別越明顯,比如再多加一條線就可以構成20鍵的鍵盤,而直接用端口線則只能多出一鍵(9鍵)。由此可見,在需要的鍵數比較多時,采用矩陣法來做鍵盤是合理的。

1.1.2 設計目標

完成矩陣鍵盤的掃描檢測程序,具體功能要求如下:

1. 運用逐行掃描的方法進行按鍵監測;

2. 每行掃描的時間不少于 20ms,濾除抖動;

3. 檢測到有按鍵按下之后,消抖時間 20ms;

4. 輸出信號 key_vld 持續一拍即可;

5. 輸出信號key_out表示16 個按鍵,并在數碼管上顯示對應數值;

1.1.3 系統結構框圖

系統結構框圖如下圖一所示:

「每周FPGA案例」至簡設計系列_矩陣按鍵檢測

圖一

1.1.4 模塊功能
? 矩陣鍵盤模塊實現功能

1、將外來異步信號打兩拍處理,將異步信號同步化。

2、實現20ms按鍵消抖功能。

3、實現矩陣鍵盤的按鍵檢測功能,并輸出有效按鍵信號。

? 數碼管顯示模塊實現功能

1、 對接收到的按鍵數據進行譯碼并顯示。

1.1.5 頂層信號
「每周FPGA案例」至簡設計系列_矩陣按鍵檢測
1.1.6 參考代碼

下面是使用工程的頂層代碼:

1.	module top(
2.	    clk         ,
3.	    rst_n       ,
4.	    key_col     ,
5.	    key_row     ,
6.	    segment     ,
7.	    seg_sel     
8.	    );
9.	
10.	    parameter      DATA_W =        4;
11.	
12.	    input               	clk    ;
13.	    input               	rst_n  ;
14.	    input   [3:0]      	key_col;
15.	    
16.	    output  [3:0]       key_row;
17.	    output  [7:0]       segment;
18.	    output  [1:0]       seg_sel;
19.	
20.	    wire     [3:0]       key_row;
21.	    wire     [7:0]       segment;
22.	    wire     [1:0]       seg_sel;
23.	
24.	    wire    [DATA_W-1:0]  	key_out		;
25.	    wire                  	key_vld		;
26.	    wire    [DATA_W-1:0]  	segment_data	;
27.	
28.	key_scan    u1(
29.	   .clk        (   clk                 ),
30.	   .rst_n      (   rst_n               ),
31.	   .key_col    (   key_col             ),
32.	   .key_row    (   key_row             ),
33.	   .key_out    (   key_out             ),
34.	   .key_vld    (   key_vld             )
35.	    );
36.	
37.	seg_disp    u3(
38.	   .clk               (  clk             ),
39.	   .rst_n             (  rst_n           ),
40.	   .data_in           (  key_out           ),
41.	   .key_en            (  key_vld           ),
42.	   .segment           (  segment           ),
43.	   .seg_sel           (  seg_sel           )
44.	);
45.	
46.	    endmodule


1.2 矩陣鍵盤模塊設計

1.2.1 接口信號
「每周FPGA案例」至簡設計系列_矩陣按鍵檢測
1.2.2 設計思路
? 行掃描法原理

開發板上為 4*4 矩陣鍵盤:默認 4 條列線上來高電平,4 條行線默認接高電平。列線 KEY_C1 ~ KEY_C4分別接有4個上拉電阻到正電源 +3.3 V,并把列線 KEY_C1~KEY_C4設置為輸入線,行線 KEY_R1~KEY_R4設置為輸出線。4根行線和4根列線形成16個相交點。

如下圖所示:

「每周FPGA案例」至簡設計系列_矩陣按鍵檢測

確認矩陣鍵盤上哪個按鍵被按下有多同方法,其中行掃描法又稱為逐行(或列)掃描查詢法,是一種最常用的按鍵識別方法。

1. 判斷鍵盤中有無鍵按下:將全部行線 KEY_R1~KEY_R4 置低電平,然后檢測列線 KEY_C1~KEY_C4 的狀態。只要有一列的電平為低,則表示鍵盤中有鍵被按下,而且閉合的鍵位于低電平線與 4根行線相交叉的 4 個按鍵之中。若所有列線均為高電平,則鍵盤中無鍵按下。

2. 判斷閉合鍵所在的位置:在確認有鍵按下后,即可進入確定具體閉合鍵的過程。其方法是:依次將行線置為低電平,即在置某根行線為低電平時,其它線為高電平。在確定某根行線位置為低電平后,再逐行檢測各列線的電平狀態。若某列為低,則該列線與置為低電平的行線交叉處的按鍵就是閉合的按鍵。

? 打拍操作

輸入的key_col是異步信號,所以要進行打兩拍操作,將異步信號key_col同步化,并防止亞穩態。

設計代碼如下:

1.	input   [3:0]           key_col     ;
2.	
3.	reg     [3:0]           key_col_ff0      ;
4.	reg     [3:0]           key_col_ff1      ;
5.	
6.	always  @(posedge clk or negedge rst_n)begin
7.	    if(rst_n==1'b0)begin
8.	        key_col_ff0 <= 4'b1111;
9.	        key_col_ff1 <= 4'b1111;
10.	    end
11.	    else begin
12.	        key_col_ff0 <= key_col    ;
13.	        key_col_ff1 <= key_col_ff0;
14.	    end
15.	end


? 按鍵消抖

對于按鍵和觸摸屏等機械設備來說,都存在一個固有問題,那就是“抖動”,按鍵從最初接通到穩定接通要經過數毫秒,其間可能發生多次“接通-斷開”這樣的毛刺。如果不進行處理,會使系統識別到抖動信號而進行不必要的反應,導致模塊功能不正常,為了避免這種現象的產生,需要進行按鍵消抖的操作。

軟件方法消抖,即檢測出鍵閉合后執行一個延時程序,抖動時間的長短由按鍵的機械特性決定,一般為5ms~20ms,讓前沿抖動消失后再一次檢測鍵的狀態,如果仍保持閉合狀態電平,則確認按下按鍵操作有效。當檢測到按鍵釋放后,也要給5ms~20ms的延時,待后沿抖動消失后才能轉入該鍵的處理程序。

「每周FPGA案例」至簡設計系列_矩陣按鍵檢測

由于按鍵按下去的時間一般都會大于20ms,為了達到不管按鍵按下多久,都視為按下一次的效果,提出以下計數器架構,如下圖所示:

「每周FPGA案例」至簡設計系列_矩陣按鍵檢測

消抖計數器shake_cnt:用于計算20ms的時間,加一條件為key_col_ff1 != 4'hf || key_row_check==1,表示當某個按鍵按下或者進行行掃描時就開始計數;數到1,000,000下,表示數到20ms就結束。

行掃描計數器row_index:用于區分掃描的行,加一條件為key_row_check && end_shake_cnt,表示當處于行掃描狀態并且每行消抖20ms后,開始掃描下一行;數到4下,表示4行按鍵掃描完了。

按鍵:表示有無按鍵按下,沒被按下時為高電平,按下后為低電平。

行掃描指示信號key_row_check:該信號為高電平,指示當前處于行掃描狀態。

矩陣鍵盤列信號key_col_ff1:4bit位寬的矩陣鍵盤列信號,最高位表示矩陣鍵盤往右數第四列,默認信號為key_col_ff1 = 4'hf,否則表示該信號低電平對應位的列有按鍵按下。

1.2.3 參考代碼

16.	module key_scan(
17.	    clk    ,
18.	    rst_n  ,
19.	    key_col,
20.	    key_row,
21.	    key_out,
22.	    key_vld
23.	    );
24.	
25.	    parameter       DATA_W      =   4           ;
26.	    parameter       TIME_20MS   =   1_000_000   ;
27.	
28.	    input                   clk         ;
29.	    input                   rst_n       ;
30.	    input   [3:0]           key_col     ;
31.	
32.	    output  [3:0]           key_row     ;
33.	    output                  key_vld     ;
34.	    output  [DATA_W-1:0]    key_out     ;
35.	
36.	    reg     [3:0]           key_row     ;
37.	    reg                     key_vld     ;
38.	    reg     [DATA_W-1:0]    key_out     ;
39.	
40.	    reg     [3:0]           key_col_ff0      ;
41.	    reg     [3:0]           key_col_ff1      ;
42.	    reg                     key_col_check    ;
43.	    reg     [21:0]          shake_cnt        ;
44.	    wire                    add_shake_cnt    ;
45.	    wire                    end_shake_cnt    ;
46.	    reg     [1:0]           key_col_get      ;
47.	    reg                     key_row_check    ;
48.	    reg     [1:0]           row_index        ;
49.	    wire                    add_row_index    ;
50.	    wire                    end_row_index    ;
51.	
52.	
53.	always  @(posedge clk or negedge rst_n)begin
54.	    if(rst_n==1'b0)begin
55.	        key_col_ff0 <= 4'b1111;
56.	        key_col_ff1 <= 4'b1111;
57.	    end
58.	    else begin
59.	        key_col_ff0 <= key_col    ;
60.	        key_col_ff1 <= key_col_ff0;
61.	    end
62.	end
63.	
64.	always  @(posedge clk or negedge rst_n)begin
65.	    if(rst_n==1'b0)begin
66.	        key_col_check <= 1'b0;
67.	    end
68.	    else if(key_col_ff1 !=4'hf && end_shake_cnt)begin
69.	        key_col_check <= 1'b1;
70.	    end
71.	    else if(key_col_ff1==4'hf)begin
72.	        key_col_check <= 1'b0;
73.	    end
74.	end
75.	
76.	
77.	always @(posedge clk or negedge rst_n) begin 
78.	    if (rst_n==0) begin
79.	        shake_cnt <= 0; 
80.	    end
81.	    else if(add_shake_cnt) begin
82.	        if(end_shake_cnt)
83.	            shake_cnt <= 0; 
84.	        else
85.	            shake_cnt <= shake_cnt+1 ;
86.	   end
87.	end
88.	assign add_shake_cnt = key_col_ff1 !=4'hf || key_row_check==1;
89.	assign end_shake_cnt = add_shake_cnt  && shake_cnt == TIME_20MS-1 ;
90.	
91.	
92.	always  @(posedge clk or negedge rst_n)begin
93.	    if(rst_n==1'b0)begin
94.	        key_col_get <= 0;
95.	    end
96.	    else if(key_col_check) begin
97.	        if(key_col_ff1==4'b1110)
98.	            key_col_get <= 0;
99.	        else if(key_col_ff1==4'b1101)
100.	            key_col_get <= 1;
101.	        else if(key_col_ff1==4'b1011)
102.	            key_col_get <= 2;
103.	        else if(key_col_ff1==4'b0111) 
104.	            key_col_get <= 3;
105.	    end
106.	end
107.	
108.	always  @(posedge clk or negedge rst_n)begin
109.	    if(rst_n==1'b0)begin
110.	        key_row_check <= 0;
111.	    end
112.	    else if(key_col_check)begin
113.	        key_row_check <= 1;
114.	    end
115.	    else if(key_vld)begin
116.	        key_row_check <= 0;
117.	    end
118.	end
119.	
120.	
121.	always @(posedge clk or negedge rst_n) begin 
122.	    if (rst_n==0) begin
123.	        row_index <= 0; 
124.	    end
125.	    else if(add_row_index) begin
126.	        if(end_row_index)
127.	            row_index <= 0; 
128.	        else
129.	            row_index <= row_index+1 ;
130.	   end
131.	end
132.	assign add_row_index = key_row_check && end_shake_cnt;
133.	assign end_row_index = add_row_index  && row_index == 4-1 ;
134.	
135.	
136.	always  @(posedge clk or negedge rst_n)begin
137.	    if(rst_n==1'b0)begin
138.	        key_row = 4'b0;
139.	    end
140.	    else if(key_row_check)begin
141.	        key_row = ~(4'b0001 << row_index);
142.	    end
143.	    else begin
144.	        key_row = 4'b0;
145.	    end
146.	end
147.	
148.	
149.	always  @(posedge clk or negedge rst_n)begin
150.	    if(rst_n==1'b0)begin
151.	        key_vld <= 1'b0;
152.	    end
153.	    else if(key_row_check && key_col_ff1[key_col_get]==1'b0 && key_col_check==0 )begin
154.	        key_vld <= 1'b1;
155.	    end
156.	    else begin
157.	        key_vld <= 1'b0;
158.	    end
159.	end
160.	
161.	
162.	always  @(posedge clk or negedge rst_n)begin
163.	    if(rst_n==1'b0)begin
164.	        key_out <= 4'd0;
165.	    end
166.	    else if(key_vld)begin
167.	        key_out <= {row_index,key_col_get};    
168.	    end
169.	    else begin
170.	        key_out <= 4'd0;
171.	    end
172.	end
173.	
174.	endmodule


1.3 數碼管顯示模塊設計

1.3.1 接口信號
「每周FPGA案例」至簡設計系列_矩陣按鍵檢測
1.3.2 設計思路

在前面的案例中已經有數碼管顯示的介紹,所以這里不在過多介紹,詳細介紹請看下方鏈接:

http://fpgabbs.com/forum.php?mod=viewthread&tid=399

1.3.3 參考代碼

1.	module seg_disp(
2.	    clk         ,
3.	    rst_n       ,
4.	    data_in     ,
5.	    key_en      ,
6.	    segment     ,
7.	    seg_sel      
8.	);
9.	
10.	parameter   ZERO           =   8'b0000_0011          ;
11.	parameter   ONE            =   8'b1001_1111          ;
12.	parameter   TWO            =   8'b0010_0101          ;
13.	parameter   THREE          =   8'b0000_1101          ;
14.	parameter   FOUR           =   8'b1001_1001          ;
15.	parameter   FIVE           =   8'b0100_1001          ;
16.	parameter   SIX            =   8'b0100_0001          ;
17.	parameter   SEVEN          =   8'b0001_1111          ;
18.	parameter   EIGHT          =   8'b0000_0001          ;
19.	parameter   NINE           =   8'b0000_1001          ;
20.	
21.	input             clk             ;         
22.	input             rst_n           ;   
23.	input    [3:0]    data_in         ;
24.	input             key_en          ;
25.	output   [7:0 ]   segment         ; 
26.	output   [1:0 ]   seg_sel         ;
27.	
28.	reg      [7:0 ]   segment         ;
29.	reg      [1:0 ]   seg_sel         ;
30.	reg      [10:0]   delay           ;
31.	reg      [1:0 ]   delay_time      ;
32.	wire              add_delay_time  ;
33.	wire              end_delay_time  ;
34.	wire              add_delay       ;
35.	wire              end_delay       ;
36.	reg      [4:0 ]   segment_tmp     ;
37.	reg      [3:0 ]   segment_data    ;
38.	
39.	
40.	always @(posedge clk or negedge rst_n) begin 
41.	    if (rst_n==0) begin
42.	        delay <= 0; 
43.	    end
44.	    else if(add_delay) begin
45.	        if(end_delay)
46.	            delay <= 0; 
47.	        else
48.	            delay <= delay+1 ;
49.	   end
50.	end
51.	assign add_delay = 1;
52.	assign end_delay = add_delay  && delay == 2000-1 ;
53.	
54.	
55.	always @(posedge clk or negedge rst_n) begin 
56.	    if (rst_n==0) begin
57.	        delay_time <= 0; 
58.	    end
59.	    else if(add_delay_time) begin
60.	        if(end_delay_time)
61.	            delay_time <= 0;
62.	        else
63.	            delay_time <= delay_time+1 ;
64.	   end
65.	end
66.	assign add_delay_time = end_delay;
67.	assign end_delay_time = add_delay_time  && delay_time == 2-1 ;
68.	
69.	
70.	always  @(posedge clk or negedge rst_n)begin
71.	    if(rst_n==1'b0)begin
72.	        segment_data <= 4'd0;
73.	    end
74.	    else if(key_en)begin
75.	        segment_data <= data_in;
76.	    end
77.	end
78.	
79.	always  @(posedge clk or negedge rst_n)begin
80.	    if(rst_n==1'b0)begin
81.	        segment_tmp <= 4'd0;
82.	    end
83.	    else if(add_delay_time  && delay_time == 1-1)begin
84.	        segment_tmp <= (segment_data+1)%10;
85.	    end
86.	    else if(end_delay_time)begin
87.	        segment_tmp <= (segment_data+1)/10;
88.	    end
89.	end
90.	
91.	
92.	always  @(posedge clk or negedge rst_n)begin
93.	    if(rst_n==1'b0)begin
94.	        segment <= ZERO;
95.	    end
96.	    else begin
97.	        case(segment_tmp)
98.	            4'd0:segment <= ZERO;
99.	            4'd1:segment <= ONE  ;
100.	            4'd2:segment <= TWO  ;
101.	            4'd3:segment <= THREE;
102.	            4'd4:segment <= FOUR ;
103.	            4'd5:segment <= FIVE ;
104.	            4'd6:segment <= SIX  ;
105.	            4'd7:segment <= SEVEN;
106.	            4'd8:segment <= EIGHT;
107.	            4'd9:segment <= NINE ;
108.	            default:begin
109.	                segment <= segment;
110.	            end
111.	        endcase
112.	    end
113.	end
114.	
115.	
116.	always  @(posedge clk or negedge rst_n)begin
117.	    if(rst_n==1'b0)begin
118.	        seg_sel <= 2'b11;
119.	    end
120.	    else begin
121.	        seg_sel <= ~(2'b1<<delay_time);
122.	    end
123.	end
124.	
125.	
126.	endmodule


1.4 效果和總結

? 下圖是該工程在db603開發板上的現象——按下按鍵s7
「每周FPGA案例」至簡設計系列_矩陣按鍵檢測
? 下圖是該工程在ms980試驗箱上的現象——按下按鍵s13
「每周FPGA案例」至簡設計系列_矩陣按鍵檢測

由于該項目的上板現象是按下矩陣按鍵,數碼管顯示對應的按鍵號,想觀看完整現象的朋友可以看一下現象演示的視頻。

設計視頻教程、工程源代碼請移步明德揚論壇觀看下載。

感興趣的朋友也可以訪問明德揚論壇(www.fpgabbs.cn)進行FPGA相關工程設計學習,也歡迎大家在評論進行討論!



也可以看一下我們往期的文章:

《基于FPGA的密碼鎖設計》

《波形相位頻率可調DDS信號發生器》

《基于FPGA的曼徹斯特編碼解碼設計》

《基于FPGA的出租車計費系統》

《數電基礎與Verilog設計》

《基于FPGA的頻率、電壓測量》

《基于FPGA的漢明碼編碼解碼設計》

《關于鎖存器問題的討論》

《阻塞賦值與非阻塞賦值》

《參數例化時自動計算位寬的解決辦法》


明德揚是一家專注于FPGA領域的專業性公司,公司主要業務包括開發板、教育培訓、項目承接、人才服務等多個方向。

點撥開發板——學習FPGA的入門之選。

MP801開發板——千兆網、ADDA、大容量SDRAM等,學習和項目需求一步到位。

網絡培訓班——不管時間和空間,明德揚隨時在你身邊,助你快速學習FPGA。

周末培訓班——明天的你會感激現在的努力進取,升職加薪明德揚來助你。

就業培訓班——七大企業級項目實訓,獲得豐富的項目經驗,高薪就業。

專題課程——高手修煉課:提升設計能力;實用調試技巧課:提升定位和解決問題能力;FIFO架構設計課:助你快速成為架構設計師;時序約束、數字信號處理、PCIE、綜合項目實踐課等你來選。

項目承接——承接企業FPGA研發項目。

人才服務——提供人才推薦、人才代培、人才派遣等服務。

   拓展閱讀
主站蜘蛛池模板: 亚洲免费三区| 玖玖视频精品| 国产亚洲精品aaa大片| 国产精品好好热在线观看| 一级毛片大全| 军营呻吟娇喘np| 黄色免费小视频| 国产日韩一区二区三区在线播放 | 国产伦理播放一区二区| 亚洲欧美视频一区二区| 亚洲一二区| 免费看一级欧美毛片视频| 国产成人一区二区三区视频免费| 国产精品视频一区麻豆| 夭天色综合| 日本高清xxx| 亚洲乱码中文字幕久久| 国产日产欧美a级毛片| 9久热这里只有精品免费| 午夜视频在线观看视频| 久久青草国产精品一区| 伊甸园久久网站| 欧美另类精品xxxx人妖换性| 国产免费一区二区| 制服丝袜第一页在线观看| 国产日韩欧美一区二区| 久久亚洲国产成人亚| 在线视频欧美亚洲| 一一级黄色片| 大伊香蕉精品二区视频在线| 日本乱人伦片中文字幕三区| 中国一级特黄真人毛片免| 欧美一级aa免费毛片| 视频一区二区免费| 大ji吧快给我别停受不了视频| 欧美日韩亚洲另类| 亚洲一区二区三区夜色| 久久美女视频| 永久在线| 伊人精品视频| 免费亚洲一区|