계산기를 설계할 때에는 Digilent의 Pmod KYPD를 통해 값을 입력받아 숫자나 문자로 변환하는 Decoder와 Decoder로부터 받은 값을 연산하여 세그먼트 디스플레이로 보내기 위한 calculator 모듈이 필요합니다.
프로젝트 소개 게시글에서 간략하게 설명했듯이, KYPD의 Schematic은 아래와 같습니다.
이 키패드를 통해 어떤 버튼이 눌리는지 감지하는 방법은 먼저 각 열(COL)을 순차적으로 LOW로 설정합니다.
[ 1110->1101->1011->0111 ]
이후 LOW로 설정된 열에서 키가 눌린 경우, 이에 해당하는 행(ROW) 핀에 LOW 신호가 전달됩니다. 이 과정을 반복하여 각 열을 1ms마다 LOW로 설정한 뒤 ROW 값을 입력으로 확인하면 어떤 키가 눌렸는지 파악할 수 있습니다.
예를 들어, 좌측 상단부터 1-2-3-4-5-6-...-13-14-15-16으로 번호를 설정하면
1. 1번 버튼이 눌렸을 때에는 COL1이 LOW, ROW1이 LOW로, COL은 0111일 때 ROW의 입력이 0111이 됩니다.
2. 마찬가지로 4번 버튼을 누르면 COL4이 LOW, ROW1이 LOW가 되므로 COL이 1110일 때 ROW의 입력이 0111이 됩니다.
3. 13번을 누르면 COL1이 LOW, ROW4이 LOW가 되므로 COL이 0111일 때 ROW 입력은 1110이 됩니다.
이렇게 Decoder의 COL 4비트를 출력으로, ROW 4비트를 입력으로 하여 어떤 위치의 키가 눌렸는지 파악하고, 키에 해당하는 값을 출력으로 내보내어 디코더 동작이 가능합니다.
Decoder 모듈 정의
Module Name: Decoder
Inputs
- rst
- clk
- [3:0] row
Outputs
- reg [3:0] col: 1ms마다 하나의 col만 LOW로 설정하여 스캔
- reg [7:0] key: 숫자 0~9와 문자 +, -, /, *, =, C를 저장하여 출력하기 위한 변수
- pressed_debounce: 키가 눌렸을 때 1clk만 HIGH 신호를 내보내는 출력
Registers
- reg [BITS-1 : 0] key_counter: 매 clk마다 1씩 증가하여 1ms마다 col 값을 변화시키기 위해 감지하는 값
- reg pressed: 키패드의 키가 눌린 상태에서는 1, 눌리지 않은 상태에서는 0인 신호를 저장하기 위한 값
- reg reset: key_counter을 초기화하기 위한 값
- reg [25:0] pressed_cnt, unpressed_cnt: row값이 일정하게 유지된 시간을 판단하기 위한 카운터
Decoder
Logic
계산기 작동 시 네자리 수인 1234를 입력하기 위해서는 다음과 같이 출력되어야 합니다.
0001 -> 0012 -> 0123 -> 1234
이를 위해서는 키가 눌렸음을 감지하여 1clk만 HIGH 신호를 내보내는 pressed_debounce 신호를 통해 키가 눌린 횟수를 카운트하고, 이에 따라 입력한 숫자의 자리를 옮겨서 수행할 수 있습니다.
키를 눌렀을 때 1, 뗐을 때 0이 되는 pressed 신호 생성
col의 출력값이 매 1ms마다 변하므로, 1번 스위치를 눌렀다가 뗐을 때 다음과 같은 결과를 얻을 수 있습니다.
1번 스위치를 누르고 있을 때에도 col을 스캔하기 위한 값은 1ms마다 변하므로, col이 0111일 때에만 row가 0111을 감지합니다. 따라서 버튼을 계속 누르고 있어도 row는 지속적으로 그 값이 1111로 변하는 것을 알 수 있습니다.
[ pressed 신호 LOW ]
그런데 버튼을 누르고 있는 경우 row가 1111로 유지되는 시간은 3ms을 넘지 못하므로, pressed 신호를 LOW로 만드는 조건을 row값이 1111로 3ms 넘게 유지되는 경우로 설정할 수 있습니다. 이 때 시간을 정확히 3ms에 맞춰 동작하도록 하면 오차가 발생할 수 있으므로 코드에서는 4ms보다 row 값 1111유지 시간이 커지면 pressed을 LOW로 설정하도록 설계했습니다.
[ pressed 신호 HIGH ]
어떤 키를 누르고 있다면 위의 그림과 같이 row가 1111이 아닌(위에선 0111인) 시간이 1ms 지속됩니다. 따라서 row가 1111이 아닌 시간이 0.5ms 이상이면 pressed 신호가 HIGH가 되도록 설계했습니다.
[pressed_debounce]
pressed_debounce는 키가 눌렸을 때 1clk만 HIGH가 되는 신호를 출력하여 calculator 모듈에서 키 입력 횟수에 따라 숫자를 이동하는 것을 돕습니다.
debouncing 모듈은 아래의 게시글을 참고하여 Cora Z7보드의 클럭주파수인 125MHz에 맞게 다음과 같이 수정하여 사용했습니다.
debounce.v
`timescale 1ns / 1ps
module debounce_switch(
input clk,
input reset,
input i_btn,
output o_btn
);
reg d1_ff, d2_ff, d3_ff;
reg r_clk = 0;//125MHz
reg [31:0] counter =0;
initial begin
counter <= 0;
r_clk <= 0 ;
d1_ff <= 0;
d2_ff <=0;
d3_ff <= 0;
end
always@(posedge clk) begin
if(reset) ;
else begin
if(counter == (625_000 -1)) begin //125_000_000 / 2 00= 625_000 500_000 = 100_000_000 / 200
//if(counter == (5 -1)) begin // for testbench
counter <= 0;
r_clk <= ~r_clk;
end
else begin
counter <= counter +1;
end
end
end
always@(posedge r_clk)begin //debounce
if (reset) begin
d1_ff <= 0;
d2_ff <=0;
end
else begin
d1_ff <= i_btn;
d2_ff <= d1_ff;
end
end
always @ (posedge clk) begin // oneshot
if (reset) begin
d3_ff <= 0;
end
else begin
d3_ff <= d2_ff;
end
end
assign o_btn = d2_ff & ~d3_ff;
endmodule
Verilog 복습 3일차
Verilog를 이용하여 스톱워치를 만들었으나, 버튼이 제대로 동작하지 않는 문제가 있었다.증상은 다음과 같다.1\. 버튼을 눌렀을때도 동작하지 않는다.2\. 버튼이 여러번 눌린 것처럼 동작후 바로
velog.io
버튼에 할당된 값을 key로 출력
위의 키패드 배열에 맞게 col 값과 row 값을 확인하여 key로 출력합니다. 이 값은 calculator로 전달되어 피연산자 또는 연산자 변수에 저장됩니다.
Decoder.v
`timescale 1ns / 1ps
/*=========================================
[Module Declaration]
=========================================*/
module Decoder(
input rst,
input clk,
input [3:0] row,
output reg [3:0] col,
output reg [7:0] key,
output pressed_debounce
);
/*=========================================
[Parameter Declaration]
=========================================*/
localparam BITS = 20;
localparam ONE_MS_TICKS = 125_000_000 / 1_000;
localparam SETTLE_TIME = 125_000_000 / 1_000_000;
/*=========================================
[Register Declaration]
=========================================*/
reg [BITS-1 : 0] key_counter;
reg pressed;
reg reset = 1'b0;
reg [3:0] row_prev; // 이전 row 값을 저장
reg [25:0] pressed_cnt, unpressed_cnt; // row 값이 일정하게 유지된 카운터
/*=========================================
[Behavioral Logic]
=========================================*/
//Added code
always @(posedge clk) begin
// row 값이 변하면 stable_cnt 증가
if (row == 4'b1111) begin
unpressed_cnt <= unpressed_cnt +1; //row가 떼져있으면 계속 1111이므로 unpressed 상태
pressed_cnt <= 0; //row가 1111로 유지되면 pressed_cnt를 초기화하고 unpressed_cnt를 증가
end else begin
pressed_cnt <= pressed_cnt + 1; //row가 변하면 pressed_cnt를 증가. row가 1111과 눌린 값을 일정시간 유지하며 번갈아가며 변하므로 row가 변하는데 1111이 아닐 때 증가
unpressed_cnt <= 0; //이 때 눌려있지 않은 상태인 unpressed_cnt는 초기화
end
// 일정 시간 동안 row 값이 유지되었을 때만 pressed를 변경
if (unpressed_cnt > 4*ONE_MS_TICKS) pressed <= 0; //4ms 넘게 키가 안눌리면 pressed OFF
if (pressed_cnt >= ONE_MS_TICKS/2) pressed <= 1; //0.5ms이상 키가 눌려있으면 pressed ON
end
//Referernce
always @(posedge clk) begin
if(reset) begin
key_counter <= 0;
end
else begin
key_counter <= key_counter + 1;
end
end
always @(posedge clk) begin
case(key_counter)
0: reset <= 1'b0; //resets every 4ms+settle time
//1ms
ONE_MS_TICKS : col <= 4'b0111;
ONE_MS_TICKS + SETTLE_TIME: begin
case(row)
4'b0111:
key <= 1; // 1
4'b1011:
key <= 4; // 4
4'b1101:
key <= 7; // 7
4'b1110:
key <= 0; // 0
endcase
end
//2ms
2 * ONE_MS_TICKS : col <= 4'b1011;
2 * ONE_MS_TICKS + SETTLE_TIME: begin
case (row)
4'b0111:
key <= 2; // 2
4'b1011:
key <= 5; // 5
4'b1101:
key <= 8; // 8
4'b1110:
key <= "C"; // F
endcase
end
//3ms
3 * ONE_MS_TICKS : col <= 4'b1101;
3 * ONE_MS_TICKS + SETTLE_TIME: begin
case (row)
4'b0111:
key <= 3; // 3
4'b1011:
key <= 6; // 6
4'b1101:
key <= 9; // 9
4'b1110:
key <= "="; // E
endcase
end
// 4ms
4 * ONE_MS_TICKS : col <= 4'b1110;
4 * ONE_MS_TICKS + SETTLE_TIME: begin
case (row)
4'b0111:
key <= "/"; // A
4'b1011:
key <= "*"; // B
4'b1101:
key <= "-"; // C
4'b1110:
key <= "+"; // D
endcase
// reset the counter
reset <= 1'b1;
end
endcase
end
debounce_switch debounce(
.clk (clk),
.reset (rst),
.i_btn (pressed),
.o_btn (pressed_debounce)
);
endmodule
'자습시간 > Verilog' 카테고리의 다른 글
Pmod OLED 공룡 게임 만들기(1) - 비트맵 이미지 구현 (0) | 2025.02.17 |
---|---|
Vivado와 Vitis를 이용한 Pmod OLED 제어 [Pmod IP 이용] (0) | 2025.02.14 |
Verilog 복습 프로젝트(2) - 스탑워치 설계 (0) | 2025.02.12 |
Verilog 복습 프로젝트(1) - 7세그먼트 제어 (0) | 2025.02.11 |