[FPGA] Segment 제어
preview
intro
해당 글에서는 Segment에 대해 간단히 알아보고
FPGA 보드에서 Segment를 제어하는 방법에 대해 공유하고자 합니다.
-
💻 about Code
Verilog, XDC 코드는 제 Github 페이지에서 확인할 수 있습니다.해당 코드는 Nexys A7-T100 보드에 맞게 작성되었으며
보드에 따라 코드의 수정이 필요할 수 있습니다.
Segment
7-digit segment는 7개의 막대와 하나의 점으로 구성된 디스플레이 입니다.
각 막대는 A~G로 표기되며 점은 dp라고 불립니다. (그림참고)
각 부분을 조합해서 숫자나 알파벳 등을 표시할 수 있습니다.
Common Anode & Common Cathode
- Common Anode : 모든 세그먼트의 양극(애노드)이 공통으로 연결되어 있으며,
세그먼트를 켜기 위해서는 해당 세그먼트의 음극(캐소드)에 LOW 신호를 인가합니다. - Common Cathode : 모든 세그먼트의 음극(캐소드)이 공통으로 연결되어 있으며,
세그먼트를 켜기 위해서는 해당 세그먼트의 양극(애노드)에 HIGH 신호를 인가합니다.
Multiplexing
여러 개의 7세그먼트 디스플레이를 제어하려면 각각의 디스플레이마다 별도의 핀이 필요하고, 이에 따라 전력 소모도 증가합니다. 하지만 이러한 방식은 비효율적입니다.
이를 해결하기 위해, 8개의 제어 핀을 모든 세그먼트에 공유하여 연결하고, 매우 빠른 속도로 각 디스플레이를 순차적으로 켜고 끄는 멀티플렉싱 방식을 사용합니다. 이 방식은 눈에 보이지 않을 만큼 빠른 전환 속도 덕분에, 마치 여러 디스플레이가 동시에 숫자를 표시하고 있는 것처럼 보이게 해줍니다.
Implement
Hardware
Nexys A7 레퍼런스를 확인해 보면 Nexys A7-T100 보드에 포함된 세그먼트는 common anode 방식임을 알 수 있습니다. 따라서 세그먼트 제어를 위해 다음과 같은 핀을 사용합니다.
- AN[7:0]: 8개의 디지트(자릿수) 선택 핀
- CA, CB, CC, CD, CE, CF, CG: 각 세그먼트(A-G) 제어 핀
- DP: 소수점 제어 핀
Verilog coding
세그먼트 제어를 위한 Verilog 모듈의 기본 구조는 다음과 같습니다:
module seven_segment_controller (
input wire clk,
input wire [4:0] buttons,
output reg [7:0] segment_display,
output reg [7:0] digit_enable
);
// 구현 코드
endmodule
주요 기능 구현
- 디지트 값 저장: 8개의 디지트에 대한 값을 저장할 레지스터 배열을 선언합니다.
- 버튼 입력 처리: 버튼 입력을 감지하고 디바운싱 처리를 합니다. 버튼에 따라 디지트 선택, 값 변경, 편집 모드 전환 등의 기능을 구현합니다.
- 멀티플렉싱 로직: 고속으로 디지트를 전환하여 모든 디지트가 동시에 켜진 것처럼 보이게 합니다.
- 7-세그먼트 변환: 숫자를 7-세그먼트 패턴으로 변환하는 함수를 구현합니다.
reg [3:0] digit_value [7:0];
always @(posedge clk) begin
if (counter[15:0] == 16'hFFFF) begin
display_digit <= display_digit + 1;
digit_enable <= ~(1 << display_digit);
// 세그먼트 값 설정 로직
end
end
function [7:0] digit_to_7segment;
input [3:0] digit;
case (digit)
4'd0: digit_to_7segment = 8'b11000000; // 0
4'd1: digit_to_7segment = 8'b11111001; // 1
// ... 다른 숫자들 ...
default: digit_to_7segment = 8'b11111111; // blank
endcase
endfunction
XDC 파일 설정
보드의 핀 매핑을 위해 XDC를 작성해 줍니다.
래퍼런스 메뉴얼 9 Basic I/O항목을 보면 세그먼트는 3.3V로 동작하고 각각의 핀의 번호가 명시되어 있으니 참고하면 되겠습니다.
Code
main.v
module seven_segment_controller (
input wire clk,
input wire [4:0] buttons, // BTNL, BTNR, BTNU, BTND, BTNC
output reg [7:0] segment_display, // 7-segment display output
output reg [7:0] digit_enable // enable specific digit
);
reg [3:0] digit_value [7:0]; // Values for each of the 8 digits
reg [2:0] current_digit = 0; // Pointer to the current digit (0 to 7)
reg [22:0] counter = 0; // Counter for button debouncing and display multiplexing
reg [4:0] prev_buttons = 0; // Previous button state for edge detection
reg edit_mode = 0; // Toggle for edit mode
reg blink_state = 0; // Blink state for the current digit
// Button logic for navigating and editing
always @(posedge clk) begin
counter <= counter + 1;
// Debounce and edge detection
if (counter == 23'h7FFFFF) begin
// Move left/right to select digit
if (buttons[0] && !prev_buttons[0]) // BTNL
current_digit <= (current_digit == 0) ? 7 : current_digit - 1;
else if (buttons[1] && !prev_buttons[1]) // BTNR
current_digit <= (current_digit == 7) ? 0 : current_digit + 1;
// Toggle edit mode
if (buttons[4] && !prev_buttons[4]) // BTNC
edit_mode <= ~edit_mode;
// Increment/decrement selected digit value in edit mode
if (edit_mode) begin
if (buttons[2] && !prev_buttons[2]) // BTNU
digit_value[current_digit] <= (digit_value[current_digit] == 9) ? 0 : digit_value[current_digit] + 1;
else if (buttons[3] && !prev_buttons[3]) // BTND
digit_value[current_digit] <= (digit_value[current_digit] == 0) ? 9 : digit_value[current_digit] - 1;
end
// Store button states for next cycle
prev_buttons <= buttons;
end
// Blink logic (toggle every ~0.5 seconds)
if (counter[21:0] == 22'h3FFFFF) begin
blink_state <= ~blink_state;
end
end
// 7-segment multiplexing logic
reg [2:0] display_digit = 0;
always @(posedge clk) begin
if (counter[15:0] == 16'hFFFF) begin
display_digit <= display_digit + 1;
// Enable only the current display digit
digit_enable <= ~(1 << display_digit);
// Display logic
if (display_digit == current_digit && !edit_mode && !blink_state)
segment_display <= 8'b11111111; // Turn off the current digit for blinking
else
segment_display <= digit_to_7segment(digit_value[display_digit]);
end
end
// Function to convert a digit (0-9) to the 7-segment display pattern
function [7:0] digit_to_7segment;
input [3:0] digit;
case (digit)
4'd0: digit_to_7segment = 8'b11000000; // 0
4'd1: digit_to_7segment = 8'b11111001; // 1
4'd2: digit_to_7segment = 8'b10100100; // 2
4'd3: digit_to_7segment = 8'b10110000; // 3
4'd4: digit_to_7segment = 8'b10011001; // 4
4'd5: digit_to_7segment = 8'b10010010; // 5
4'd6: digit_to_7segment = 8'b10000010; // 6
4'd7: digit_to_7segment = 8'b11111000; // 7
4'd8: digit_to_7segment = 8'b10000000; // 8
4'd9: digit_to_7segment = 8'b10010000; // 9
default: digit_to_7segment = 8'b11111111; // blank
endcase
endfunction
endmodule
main.xdc
# Clock (clk)
set_property PACKAGE_PIN E3 [get_ports {clk}]
set_property IOSTANDARD LVCMOS33 [get_ports {clk}]
# Buttons (BTNL, BTNR, BTNU, BTND, BTNC)
set_property PACKAGE_PIN P17 [get_ports {buttons[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {buttons[0]}]
set_property PACKAGE_PIN M17 [get_ports {buttons[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {buttons[1]}]
set_property PACKAGE_PIN M18 [get_ports {buttons[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {buttons[2]}]
set_property PACKAGE_PIN P18 [get_ports {buttons[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {buttons[3]}]
set_property PACKAGE_PIN N17 [get_ports {buttons[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {buttons[4]}]
# Seven Segment Display (segment_display[0] ~ segment_display[7])
set_property PACKAGE_PIN T10 [get_ports {segment_display[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segment_display[0]}]
set_property PACKAGE_PIN R10 [get_ports {segment_display[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segment_display[1]}]
set_property PACKAGE_PIN K16 [get_ports {segment_display[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segment_display[2]}]
set_property PACKAGE_PIN K13 [get_ports {segment_display[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segment_display[3]}]
set_property PACKAGE_PIN P15 [get_ports {segment_display[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segment_display[4]}]
set_property PACKAGE_PIN T11 [get_ports {segment_display[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segment_display[5]}]
set_property PACKAGE_PIN L18 [get_ports {segment_display[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segment_display[6]}]
set_property PACKAGE_PIN H15 [get_ports {segment_display[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segment_display[7]}]
# Digit Enable (digit_enable[0] ~ digit_enable[7])
set_property PACKAGE_PIN U13 [get_ports {digit_enable[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {digit_enable[0]}]
set_property PACKAGE_PIN K2 [get_ports {digit_enable[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {digit_enable[1]}]
set_property PACKAGE_PIN T14 [get_ports {digit_enable[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {digit_enable[2]}]
set_property PACKAGE_PIN P14 [get_ports {digit_enable[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {digit_enable[3]}]
set_property PACKAGE_PIN J14 [get_ports {digit_enable[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {digit_enable[4]}]
set_property PACKAGE_PIN T9 [get_ports {digit_enable[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {digit_enable[5]}]
set_property PACKAGE_PIN J18 [get_ports {digit_enable[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {digit_enable[6]}]
set_property PACKAGE_PIN J17 [get_ports {digit_enable[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {digit_enable[7]}]
Code Review
- 주요 특징과 기능
1) 8자리 7-세그먼트 디스플레이 제어
2) 5개의 버튼 입력을 사용하여 디스플레이를 조작
3) 편집 모드와 일반 모드 지원
4) 디스플레이 멀티플렉싱 구현하여 8자리를 동시에 제어 - Code structure
-
모듈 정의
입력: 클럭, 5개 버튼
출력: 7-세그먼트 디스플레이 출력, 자릿수 활성화 신호 -
레지스터 정의
각 자릿수의 값, 현재 선택된 자릿수, 카운터, 이전 버튼 상태 등 -
버튼 로직
디바운싱을 위한 카운터 사용
좌/우 버튼으로 자릿수 선택
중앙 버튼으로 편집 모드 전환
위/아래 버튼으로 선택된 자릿수 값 변경 -
블링킹 로직
약 0.5초 간격으로 선택된 자릿수 깜빡임 -
디스플레이 멀티플렉싱
고속으로 각 자릿수를 순환하며 표시
현재 활성화된 자릿수만 켜짐 -
7-세그먼트 변환 함수
0-9 숫자를 7-세그먼트 패턴으로 변환
-