키패드
이번에는 16개의 버튼이 달려있는 키패드를 제어하려고 한다.
키패드의 회로 구성도는 아래와 같다.
우선 키패드를 동작시키기 위해 키패드를 제어할 controler 모듈을 구현해야 한다.
키패드를 제어할 controler 모듈에서는 링카운터와 case문을 사용하여 4 digit 7 segment와 column을 제어하려고 한다.
코드 내 링카운터 부분에서는 column이 출력 부분으로 작동한다. 키가 눌려있는 상태에서는 column값이 유지된다.
단, Key_valid가 1 일시에는 현재 상태를 유지하는 것을 볼 수 있다. 링카운터의 동작 상태도는 아래와 같다.
16개의 스위치 제어에서는 case문을 사용하여 제어하려고 한다. case문에서는 row(행)과 col(열)의 입력을 통하여 출력을 결정하며, 출력은 스위치가 16개이므로 16진수를 사용하여 선언하였다.
링카운터를 쓰는 이유는, 위의 키패드 회로 구성도를 보면 알 수 있듯이 밑에 버튼 4개를 링카운터로 8ms씩 돌려 임의의 버튼을 눌렀을 때 해당하는 버튼이 반응할 수 있도록 설계한 것이다. row값은 왜 링카운트를 안 줄까 라는 생각이 들었지만, row값까지 링카운터로 돌릴 시 버튼을 누르는 것이 의미가 없다고 생각이 든다.
module key_pad_cntr(
input clk, reset_p,
input [3:0] row,
output reg [3:0] col,
output reg [3:0] key_value,
output reg key_valid);
reg [19:0] clk_div;
always @(posedge clk)clk_div = clk_div + 1;
wire clk_8msec_p, clk_8msec_n;
edge_detector_p ed(.clk(clk), .reset_p(reset_p), .cp(clk_div[19]), .n_edge(clk_8msec_n), .p_edge(clk_8msec_p));
// ring counter
always @(posedge clk or posedge reset_p)begin
if(reset_p) col = 4'b0001;
else if(clk_8msec_p && !key_valid)begin
case(col)
4'b0001: col = 4'b0010;
4'b0010: col = 4'b0100;
4'b0100: col = 4'b1000;
4'b1000: col = 4'b0001;
default: col = 4'b0001;
endcase
end
end
// 16's Key input/Output
always @(posedge clk or posedge reset_p)begin
if(reset_p)begin
key_value = 0;
key_valid = 0;
end
else begin
if(clk_8msec_n)begin // Read Low value
if(row)begin
key_valid = 1;
case({col, row})
8'b0001_0001: key_value = 4'h0;
8'b0001_0010: key_value = 4'h1;
8'b0001_0100: key_value = 4'h2;
8'b0001_1000: key_value = 4'h3;
8'b0010_0001: key_value = 4'h4;
8'b0010_0010: key_value = 4'h5;
8'b0010_0100: key_value = 4'h6;
8'b0010_1000: key_value = 4'h7;
8'b0100_0001: key_value = 4'h8;
8'b0100_0010: key_value = 4'h9;
8'b0100_0100: key_value = 4'ha;
8'b0100_1000: key_value = 4'hb;
8'b1000_0001: key_value = 4'hc;
8'b1000_0010: key_value = 4'hd;
8'b1000_0100: key_value = 4'he;
8'b1000_1000: key_value = 4'hf;
endcase
end
else begin
key_valid = 0;
// key_value = 0;
end
end
end
end
endmodule
다음 코드는 스위치를 물리적으로 동작을 시킬 코드이다. 물리적으로 구현을 할 때에는 wire를 선언하여 각각의 포트에 연결시켜 진행해야 한다. 위에서 구현한 코드인 key_pad_cntr 모듈을 불러와 test_top 모듈에서 진행시킨다.
스위치를 동작시켜 동작을 직접 확인할 수 있도록 도와주는 FND 모듈을 불러와야 한다. FND를 제어할 FND Controler 모듈의 코드는 test_top 코드 안에 기재되어 있으며 코드의 동작은 아래와 같다.
module keypad_test_top(
input clk, reset_p,
input [3:0] row,
output [3:0] col,
output [3:0] com,
output [7:0] seg_7,
output led_key_valid
);
wire [3:0] key_value;
key_pad_cntr keypad(.clk(clk), .reset_p(reset_p), .row(row), .col(col), .key_value(key_value), .key_valid(led_key_valid));
// FND Controler
fnd_cntr fnd(.clk(clk), .reset_p(reset_p), .value({12'b0, key_value}), .com(com), .seg_7(seg_7));
endmodule
위의 코드를 동작시키면 아래와 같은 동작이 실행되는 것을 확인할 수 있다.
Key_value가 0인 경우
이번에는 controler 모듈에서 Key_Value가 0으로 선언된 경우를 살펴보려 한다.
위의 코드와 모두 다 일치하지만, 마지막 부분에 key_value의 값이 0으로 선언된 것을 확인할 수 있다.
동작 또한 위의 코드의 동작과 유사하지만, 차이점은 value의 값이 0으로 초기화됨과 동시에 FND에 0을 출력할 것이라고 예상할 수 있다.
컨트롤러의 코드는 아래와 같다.
module key_pad_cntr(
input clk, reset_p,
input [3:0] row,
output reg [3:0] col,
output reg [3:0] key_value,
output reg key_valid);
reg [19:0] clk_div;
always @(posedge clk)clk_div = clk_div + 1;
wire clk_8msec_p, clk_8msec_n;
edge_detector_p ed(.clk(clk), .reset_p(reset_p), .cp(clk_div[19]), .n_edge(clk_8msec_n), .p_edge(clk_8msec_p));
// ring counter
always @(posedge clk or posedge reset_p)begin
if(reset_p) col = 4'b0001;
else if(clk_8msec_p && !key_valid)begin
case(col)
4'b0001: col = 4'b0010;
4'b0010: col = 4'b0100;
4'b0100: col = 4'b1000;
4'b1000: col = 4'b0001;
default: col = 4'b0001;
endcase
end
end
// 16's Key input/Output
always @(posedge clk or posedge reset_p)begin
if(reset_p)begin
key_value = 0;
key_valid = 0;
end
else begin
if(clk_8msec_n)begin // Read Low value
if(row)begin
key_valid = 1;
case({col, row})
8'b0001_0001: key_value = 4'h0;
8'b0001_0010: key_value = 4'h1;
8'b0001_0100: key_value = 4'h2;
8'b0001_1000: key_value = 4'h3;
8'b0010_0001: key_value = 4'h4;
8'b0010_0010: key_value = 4'h5;
8'b0010_0100: key_value = 4'h6;
8'b0010_1000: key_value = 4'h7;
8'b0100_0001: key_value = 4'h8;
8'b0100_0010: key_value = 4'h9;
8'b0100_0100: key_value = 4'ha;
8'b0100_1000: key_value = 4'hb;
8'b1000_0001: key_value = 4'hc;
8'b1000_0010: key_value = 4'hd;
8'b1000_0100: key_value = 4'he;
8'b1000_1000: key_value = 4'hf;
endcase
end
else begin
key_valid = 0;
key_value = 0;
end
end
end
end
endmodule
동작 영상은 아래의 영상과 같다. 위에서 예상한 바와 같이, Key_value가 0인 경우에는 버튼을 눌렀다가 떼는 순간에 FND 값이 0으로 초기화되는 것을 확인할 수 있다.
FSM
이번에는 FSM(Finite State Machine)을 활용하여 키패드를 제어할 예정이다.
FSM(유한 상태 머신)은 흔히 스테이트 머신이라고도 불리며, 스테이트 머신은 사용자가 임의로 설계한 대로만 동작한다는 큰 특징을 지닌다.
따라서 FSM을 사용할 시 사용자가 의도하지 않은 오류가 발생할 확률이 줄어든다는 이점을 지니고 있다.
FSM은 state, 즉 상태에 따라 작동한다는 특징을 지닌다. 따라서 always문을 살펴보면 알 수 있듯이, 각각의 상태마다 특정한 조건을 만족할 시, 다음 단계로 이동하는 것을 확인할 수 있다.
module keypad_cntr_FSM(
input clk, reset_p,
input [3:0] row,
output reg [3:0] col,
output reg [3:0] key_value,
output reg key_valid);
parameter SCAN0 = 5'b00001;
parameter SCAN1 = 5'b00010;
parameter SCAN2 = 5'b00100;
parameter SCAN3 = 5'b01000;
parameter KEY_PROCESS = 5'b10000;
reg [19:0] clk_div;
always @(posedge clk)clk_div = clk_div + 1;
wire clk_8msec;
edge_detector_p ed(.clk(clk), .reset_p(reset_p), .cp(clk_div[19]), .p_edge(clk_8msec_p), .n_edge(clk_8msec_n));
reg [4:0] state, next_state;
// FSM
always @(posedge clk or posedge reset_p)begin
if(reset_p) state = SCAN0;
else if(clk_8msec_n) state = next_state;
end
always @* begin
case(state)
SCAN0: begin
if(row == 0) next_state = SCAN1;
else next_state = KEY_PROCESS;
end
SCAN1: begin
if(row == 0) next_state = SCAN2;
else next_state = KEY_PROCESS;
end
SCAN2: begin
if(row == 0) next_state = SCAN3;
else next_state = KEY_PROCESS;
end
SCAN3: begin
if(row == 0) next_state = SCAN0;
else next_state = KEY_PROCESS;
end
KEY_PROCESS: begin
if(row == 0) next_state = SCAN0;
else next_state = KEY_PROCESS;
end
default: next_state = SCAN0;
endcase
end
always @(posedge clk or posedge reset_p) begin
if(reset_p)begin
key_value = 0;
key_valid = 0;
col = 0;
end
else if(clk_8msec_p) begin
case(state)
SCAN0:begin col = 4'b0001; key_valid = 0; end
SCAN1:begin col = 4'b0010; key_valid = 0; end
SCAN2:begin col = 4'b0100; key_valid = 0; end
SCAN3:begin col = 4'b1000; key_valid = 0; end
KEY_PROCESS: begin
key_valid = 1;
case({col, row})
8'b0001_0001: key_value = 4'h0;
8'b0001_0010: key_value = 4'h1;
8'b0001_0100: key_value = 4'h2;
8'b0001_1000: key_value = 4'h3;
8'b0010_0001: key_value = 4'h4;
8'b0010_0010: key_value = 4'h5;
8'b0010_0100: key_value = 4'h6;
8'b0010_1000: key_value = 4'h7;
8'b0100_0001: key_value = 4'h8;
8'b0100_0010: key_value = 4'h9;
8'b0100_0100: key_value = 4'ha;
8'b0100_1000: key_value = 4'hb;
8'b1000_0001: key_value = 4'hc;
8'b1000_0010: key_value = 4'hd;
8'b1000_0100: key_value = 4'he;
8'b1000_1000: key_value = 4'hf;
endcase
end
endcase
end
end
endmodule
Top 모듈의 코드는 아래와 같다.
module keypad_test_top(
input clk, reset_p,
input [3:0] row,
output [3:0] col,
output [3:0] com,
output [7:0] seg_7,
output led_key_valid
);
wire [3:0] key_value;
keypad_cntr_FSM keypad(.clk(clk), .reset_p(reset_p), .row(row), .col(col), .key_value(key_value), .key_valid(led_key_valid));
// FND Controler
fnd_cntr fnd(.clk(clk), .reset_p(reset_p), .value({12'b0, key_value}), .com(com), .seg_7(seg_7));
endmodule
위 코드의 동작은 "키패드 동작 1" 영상과 동일하게 동작한다.
'Language > Verilog' 카테고리의 다른 글
Vivado : Basys3 종합 시계 프로젝트 (타이머, 스톱워치, 시계) (2) | 2024.07.30 |
---|---|
Vivado : 온도 습도 센서 (0) | 2024.07.25 |
Vivado : 초음파 센서 (0) | 2024.07.25 |
Vivado : Basys3 Stop watch (0) | 2024.07.18 |
Vivado : Basys3 FND / 디지털 시계, 분주 (0) | 2024.07.17 |