PWM_(Pulse Width Modulation)
PWM이란 디지털 신호의 펄스 폭(ON 되는 시간)을 조절하여, 출력의 평균값이나 에너지를 조정하는 신호 처리 방식
- 디지털 신호(0 또는 1, ON/OFF)로 구성된 사각파(펄스)를 일정 주기로 반복적으로 출력
- 한 주기(Period) 동안 신호가 1(ON)인 구간의 비율(%)을 듀티 사이클(Duty Cycle)이라한다.
- 이 듀티 사이클을 변화시키면, 출력의 평균 전압 또는 전달되는 에너지의 양이 변한다.
목적
- Verilog를 이용하여 128단계의 PWM(Pulse Width Modulation) 신호를 생성하는 하드웨어 회로를 설계
- PWM이란 디지털 신호의 펄스 폭(ON 되는 시간)을 조절하여, 출력의 평균값이나 에너지를 조정하는 신호 처리 방식
사용 코드_ 상위모듈
module led_pwm_top(
input clk, reset_p, // 시스템 클록, 비동기 리셋(Active-high)
output r, g, b, // RGB LED 출력
output [15:0] debug_led, // 디버그용 LED(16개)
output [7:0] seg_7, // 7세그먼트 a~g, dp
output [3:0] com // 7세그먼트 자릿수 선택
);
// 카운터 선언: 다양한 주기/패턴 생성을 위한 카운터
integer cnt;
always @(posedge clk) cnt = cnt + 1; // 매 클록 상승에지마다 cnt 1증가
// 각 컬러별 PWM 신호 생성
// cnt의 상위 비트들을 PWM duty로 사용해 색이 천천히 바뀜
pwm_led_128step pwm_r(
.clk(clk),
.reset_p(reset_p),
.duty(cnt[29:23]), // R: cnt[29:23] 사용(7비트)
.pwm(r)
);
pwm_led_128step pwm_g(
.clk(clk),
.reset_p(reset_p),
.duty(cnt[30:24]), // G: cnt[30:24] 사용(7비트)
.pwm(g)
);
pwm_led_128step pwm_b(
.clk(clk),
.reset_p(reset_p),
.duty(cnt[28:22]), // B: cnt[28:22] 사용(7비트)
.pwm(b)
);
// 디버그용 LED 0번에도 PWM 출력 연결(Blue와 동일 duty)
pwm_led_128step pwm_led(
.clk(clk),
.reset_p(reset_p),
.duty(cnt[28:22]),
.pwm(debug_led[0])
);
// cnt[28:22] 값(7비트)을 BCD로 변환하여 4자리 7세그먼트 표시
wire [15:0] bcd_duty; // 변환된 BCD 값(4자리 x 4비트)
bin_to_dec bcd_x(
.bin(cnt[28:22]), // 입력: 7비트 이진값
.bcd(bcd_duty) // 출력: 4자리 BCD
);
// 7세그먼트 드라이버: 변환된 BCD duty값 표시
fnd_4digit_cntr fnd(
.clk(clk),
.reset_p(reset_p),
.value(bcd_duty), // 표시할 값(BCD)
.seg_7(seg_7),
.com(com)
);
endmodule
사용 코드_ 하위 모듈
module pwm_led_128step (
input clk, reset_p, // 시스템 클록, 비동기 리셋(Active-high)
input [6:0] duty, // 7비트 PWM 듀티비(0~127)
output reg pwm // PWM 출력
);
// 파라미터: 시스템 클록, PWM 목표 주파수, duty resolution(128단계)
parameter sys_clk_freq = 100_000_000; // 시스템 클록 100MHz (예시)
parameter pwm_freq = 10_000; // PWM 주파수 10kHz (예시)
parameter duty_step = 128; // PWM 분해능(128단계)
parameter temp = sys_clk_freq / pwm_freq / duty_step / 2;
// temp: 하나의 duty 구간에서 토글될 때까지의 클록 수 (1주기=2번 토글, 128단계*2번*temp=전체 PWM period)
integer cnt; // sys_clk을 temp로 분주하는 카운터
reg pwm_freqX128; // 128*PWM 주기 신호(토글 신호)
// [1] PWM 내부 분주기: cnt가 temp-1이 되면 토글, 128*2*temp=전체 PWM period
always @(posedge clk, posedge reset_p) begin
if (reset_p) begin
cnt = 0;
end else begin
if (cnt >= temp-1) begin
cnt = 0;
pwm_freqX128 = ~pwm_freqX128; // 토글
end else
cnt = cnt + 1;
end
end
// [2] 분주 신호의 네거티브 에지 검출기(1 PWM step마다 동작)
wire pwm_freqX128_nedge;
edge_detector_p ed_btn(
.clk(clk),
.reset_p(reset_p),
.cp(pwm_freqX128),
.n_edge(pwm_freqX128_nedge)
);
reg [6:0] cnt_duty; // 0~127까지 카운터 (duty cycle)
// [3] 네거티브 에지마다 duty 카운터 증가, 128되면 자동 0으로 wrap
// cnt_duty < duty 이면 PWM=1(ON), 아니면 PWM=0(OFF)
always @(posedge clk or posedge reset_p) begin
if (reset_p) begin
cnt_duty = 0;
pwm = 0;
end
else if (pwm_freqX128_nedge) begin
cnt_duty = cnt_duty + 1;
if (cnt_duty < duty)
pwm = 1; // 듀티 구간: ON
else
pwm = 0; // OFF 구간
end
end
endmodule
사용 코드_ XDC
## Clock signal
set_property -dict { PACKAGE_PIN W5 IOSTANDARD LVCMOS33 } [get_ports clk]
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports clk] s clk]
## Switches
set_property PACKAGE_PIN V17 [get_ports {duty[0]}]
set_property PACKAGE_PIN W17 [get_ports {duty[1]}]
set_property PACKAGE_PIN W16 [get_ports {duty[2]}]
set_property PACKAGE_PIN V16 [get_ports {duty[3]}]
set_property PACKAGE_PIN W15 [get_ports {duty[4]}]
set_property PACKAGE_PIN V15 [get_ports {duty[5]}]
set_property PACKAGE_PIN W14 [get_ports {duty[6]}]
## LEDs
set_property -dict { PACKAGE_PIN U16 IOSTANDARD LVCMOS33 } [get_ports {debug_led[0]}]
set_property -dict { PACKAGE_PIN E19 IOSTANDARD LVCMOS33 } [get_ports {debug_led[1]}]
set_property -dict { PACKAGE_PIN U19 IOSTANDARD LVCMOS33 } [get_ports {debug_led[2]}]
set_property -dict { PACKAGE_PIN V19 IOSTANDARD LVCMOS33 } [get_ports {debug_led[3]}]
set_property -dict { PACKAGE_PIN W18 IOSTANDARD LVCMOS33 } [get_ports {debug_led[4]}]
set_property -dict { PACKAGE_PIN U15 IOSTANDARD LVCMOS33 } [get_ports {debug_led[5]}]
set_property -dict { PACKAGE_PIN U14 IOSTANDARD LVCMOS33 } [get_ports {debug_led[6]}]
set_property -dict { PACKAGE_PIN V14 IOSTANDARD LVCMOS33 } [get_ports {debug_led[7]}]
set_property -dict { PACKAGE_PIN V13 IOSTANDARD LVCMOS33 } [get_ports {debug_led[8]}]
set_property -dict { PACKAGE_PIN V3 IOSTANDARD LVCMOS33 } [get_ports {debug_led[9]}]
set_property -dict { PACKAGE_PIN W3 IOSTANDARD LVCMOS33 } [get_ports {debug_led[10]}]
set_property -dict { PACKAGE_PIN U3 IOSTANDARD LVCMOS33 } [get_ports {debug_led[11]}]
set_property -dict { PACKAGE_PIN P3 IOSTANDARD LVCMOS33 } [get_ports {debug_led[12]}]
set_property -dict { PACKAGE_PIN N3 IOSTANDARD LVCMOS33 } [get_ports {debug_led[13]}]
set_property -dict { PACKAGE_PIN P1 IOSTANDARD LVCMOS33 } [get_ports {debug_led[14]}]
set_property -dict { PACKAGE_PIN L1 IOSTANDARD LVCMOS33 } [get_ports {debug_led[15]}]
##7 Segment Display
set_property -dict { PACKAGE_PIN W7 IOSTANDARD LVCMOS33 } [get_ports {seg_7[7]}]
set_property -dict { PACKAGE_PIN W6 IOSTANDARD LVCMOS33 } [get_ports {seg_7[6]}]
set_property -dict { PACKAGE_PIN U8 IOSTANDARD LVCMOS33 } [get_ports {seg_7[5]}]
set_property -dict { PACKAGE_PIN V8 IOSTANDARD LVCMOS33 } [get_ports {seg_7[4]}]
set_property -dict { PACKAGE_PIN U5 IOSTANDARD LVCMOS33 } [get_ports {seg_7[3]}]
set_property -dict { PACKAGE_PIN V5 IOSTANDARD LVCMOS33 } [get_ports {seg_7[2]}]
set_property -dict { PACKAGE_PIN U7 IOSTANDARD LVCMOS33 } [get_ports {seg_7[1]}]
set_property -dict { PACKAGE_PIN V7 IOSTANDARD LVCMOS33 } [get_ports seg_7[0]]
set_property -dict { PACKAGE_PIN U2 IOSTANDARD LVCMOS33 } [get_ports {com[0]}]
set_property -dict { PACKAGE_PIN U4 IOSTANDARD LVCMOS33 } [get_ports {com[1]}]
set_property -dict { PACKAGE_PIN V4 IOSTANDARD LVCMOS33 } [get_ports {com[2]}]
set_property -dict { PACKAGE_PIN W4 IOSTANDARD LVCMOS33 } [get_ports {com[3]}]
##Buttons
set_property -dict { PACKAGE_PIN U18 IOSTANDARD LVCMOS33 } [get_ports reset_p]
##Pmod Header JA
set_property -dict { PACKAGE_PIN J1 IOSTANDARD LVCMOS33 } [get_ports {r}];#Sch name = JA1
set_property -dict { PACKAGE_PIN L2 IOSTANDARD LVCMOS33 } [get_ports {g}];#Sch name = JA2
set_property -dict { PACKAGE_PIN J2 IOSTANDARD LVCMOS33 } [get_ports {b}];#Sch name = JA3
##Pmod Header JXADC
set_property -dict { PACKAGE_PIN J3 IOSTANDARD LVCMOS33 } [get_ports {vauxp6}];#Sch name = XA1_P
set_property -dict { PACKAGE_PIN L3 IOSTANDARD LVCMOS33 } [get_ports {vauxp14}];#Sch name = XA2_P
set_property -dict { PACKAGE_PIN K3 IOSTANDARD LVCMOS33 } [get_ports {vauxn6}];#Sch name = XA1_N
set_property -dict { PACKAGE_PIN M3 IOSTANDARD LVCMOS33 } [get_ports {vauxn14}];#Sch name = XA2_N
## Configuration options, can be used for all designs
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property CFGBVS VCCO [current_design]
## SPI configuration mode options for QSPI boot, can be used for all designs
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 33 [current_design]
set_property CONFIG_MODE SPIx4 [current_design]
Schematic_ATL_Analysis

Schematic_ Synthesis

회로 구현

동작 영상
오실로스코프 측정
결과
- 카운터 생성
- 내부에서 cnt라는 정수형 카운터가 클럭마다 1씩 계속 증가한다.
- 이 카운터의 상위 비트(cnt[28:22])는 0에서 127까지 변하며 계속 순환된다.
- PWM 신호 생성
- 각 RGB LED(R/G/B)와 디버그용 LED에 대해, 각각 cnt의 서로 다른 7비트 구간을 duty 입력으로 사용해
PWM 신호를 만든다. - 이 PWM 신호는 duty 값에 따라 High(ON)와 Low(OFF) 구간이 달라지며, 실제 LED의 밝기를 제어하는 것을 확인 할 수 있다.
- duty 값이 커질수록 ON 구간이 길어져서 LED가 더 밝아지는 것을 확인 할 수 있다..
- 각 RGB LED(R/G/B)와 디버그용 LED에 대해, 각각 cnt의 서로 다른 7비트 구간을 duty 입력으로 사용해
- 7세그먼트(FND) 표시
- cnt[28:22] 값(0~127)을 이진수에서 BCD로 변환해서 4자리 7세그먼트에 나오는 것을 확인 할 수 있다.
- 7세그먼트에 나타나는 숫자는 현재 PWM 듀티 값과 정확히 일치하는 것을 확인 할 수 있다.
- LED 확인
- FND=10 → PWM duty=10, LED 매우 어둡고, PWM ON구간 매우 짧음
- FND=64 → PWM duty=64, LED 중간 밝기, PWM ON/OFF 50:50
- FND=127 → PWM duty=127, LED 매우 밝음, PWM 거의 항상 ON
- 오실로스코프 확인
- FND에 표시되는 값이 1씩 증가하는 시점이 곧 PWM 파형의 듀티 사이클(duty cycle)이 갱신되어 한 주기가 종료되고 다음 주기가 시작되는 타이밍과 일치하는 것을 확인 할 수 있다.
- 따라서, 7세그먼트 값이 변할 때마다 오실로스코프 상에서도 PWM 신호의 High/Low 패턴이 다음 duty로 즉시 전환되는 동기적인 변화를 확인 할 수 있다.
'Verilog_RTL 설계 > Basys_3' 카테고리의 다른 글
| [FSM] I2C_LCD_Control (3) | 2025.07.22 |
|---|---|
| [FSM] Ultrasonic_sensor_(HC_SR04) (0) | 2025.07.16 |
| [FSM] DTH11_Sensor (0) | 2025.07.14 |
| [Sequential_Circuit] edge_detector (0) | 2025.07.13 |
| [Shift_Register] PIPO_(Parallel-In Parallel-Out) (0) | 2025.07.12 |