버튼을 누르면 물리적인 접점 떨림(bounce) 때문에 짧은 시간 동안 HIGH/LOW가 수차례 튀게 됩니다. 이로 인해 버튼을 한 번 눌러도 여러 번 눌렸다고 오인할 수 있습니다.
실제 버튼을 눌렀을 때 bounce가 일어나는 시간은 대략 50~100μs라고 가정해보겠습니다. 이때 버튼 신호는 다음처럼 불안정하게 튀는(high/low 반복) 형태입니다.
시간 (μs) | 0 | 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | 90 |
---|---|---|---|---|---|---|---|---|---|---|
버튼(i_btn) | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 |
0~50μs 구간:
→ 0 → 1 → 0 → 1
이렇게 불안정하게 튐
→ 이건 물리적인 버튼 접점이 흔들리는 바운스(bounce) 상태
60μs 이후:
→ 1 → 1 → 1...
→ 접점이 안정화되고, 버튼이 눌린 상태
→ 노이즈를 무시하고 안정적인 눌림만 감지 가능
i_btn
는 짧은 시간 동안 튀는 노이즈가 섞일 수 있는 외부 버튼 입력입니다. o_btn
은 디바운싱을 통과한 후, 처음 눌릴 때만 1을 출력하는 펄스 신호입니다.
module btn_debounce (
input clk, // 100MHz 시스템 클럭
input rst, // 비동기 리셋
input i_btn, // 버튼 입력 (노이즈 가능성 있음)
output o_btn // 디바운싱 후 rising edge만 출력 (펄스)
);
아래 always 블록은 100MHz 입력 클럭을 1000으로 나눠서 100KHz 펄스 (r_clk
) 생성하는 클럭 분주기 코드입니다. r_clk
는 10μs마다 1클럭 동안만 1이 되는 펄스이고 이 펄스로 버튼 입력을 일정 간격으로 샘플링 할 수 있습니다.
parameter F_COUNT = 1000;
reg [$clog2(F_COUNT)-1:0] r_counter;
reg r_clk;
always @(posedge clk, posedge rst) begin
if (rst) begin
r_counter <= 0;
r_clk <= 0;
end else begin
if (r_counter == (F_COUNT - 1)) begin
r_counter <= 0;
r_clk <= 1'b1; // 펄스 발생
end else begin
r_counter <= r_counter + 1;
r_clk <= 1'b0; // 평소에는 0
end
end
end
아래 코드는 버튼 디바운싱 로직의 핵심, 즉 시프트 레지스터(shift register)를 이용한 입력 샘플링 및 필터링을 구현한 부분입니다. q_reg
는 버튼 입력을 8비트로 저장하는 시프트 레지스터이고 q_next
는다음 클럭에서 q_reg
에 저장될 값입니다.