Language/Verilog

Vivado : 키패드

짱도르딘 2024. 7. 25. 19:20
728x90

키패드

이번에는 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

 

위의 코드를 동작시키면 아래와 같은 동작이 실행되는 것을 확인할 수 있다.

키패트 동작 1

 

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" 영상과 동일하게 동작한다.

 

 

728x90