2024.7.8 수업날
오늘은 Atmega 128a에서 배운 PWM을 알아보고, Basys3에 연결해서 결과를 살펴본다.
< PWM >
PWM(Pluse Width Modulation)이란
주파수의 pluse 신호에서 pluse 폭을 조절하여 신호의 듀티 사이클을 제어하는 기술이다.
이 방법은 주로 모터 제어, 조명 밝기 조절, 오디오 신호 생성 등 다양한 응용분야에서 사용된다.
PWM은 코드를 보면서 알아보도록하자.
pwm_128step 소스코드
///////////////////////////////////////////////////////////// 2024.7.8
//////////////////////////pwm 제어하기, 128분주기(100MHz로 나눈)
// led는 10,000Hz, 모터는 100Hz
module pwm_128step(
input clk, reset_p,
input [6:0]duty, // // 2^7=128분주기
output reg pwm );
parameter sys_clk_freq = 100_000_000; //시스템 클록, 100MHz
parameter pwm_freq = 10_000; //pwm 출력, 10,000Hz
parameter duty_step = 128; // 듀티 싸이클 128로 설정
parameter temp = sys_clk_freq / pwm_freq / duty_step; // 타이머 카운트 주기, parameter 상수는 그냥 수이기 때문에 회로 만들때는 나누기 회로가 만들어지지 않음
parameter temp_half = temp / 2;
integer cnt; // 카운터 변수 선언
reg pwm_freqX128; // 128배 주파수를 가지는 PWM 신호를 저장하는 레지스터 선언
always @ (posedge clk or posedge reset_p) begin
if(reset_p) begin
pwm_freqX128 = 0;
cnt = 0;
end
else begin
if(cnt >= (temp - 1))cnt = 0; //78분주-->10,000/128
else cnt = cnt + 1;
//if(cnt < (pwmXduty_steps / 2)) pwm_freqX128 = 0; // 마찬가지로 상수이기 때문에 상관 없음
if(cnt < temp_half) pwm_freqX128 = 0;
else pwm_freqX128 = 1;
end
end
wire pwm_freqX128_nedge;
edge_detector_n ed(
.clk(clk), .reset_p(reset_p),
.cp(pwm_freqX128), .n_edge(pwm_freqX128_nedge));
reg [6:0] cnt_duty;
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; // 128에 한번 씩 0이됨
if(cnt_duty < duty)pwm = 1;
else pwm = 0;
end
end
endmodule
sys_clk_freq는 시스템 클록 주파수로 100MHz로 설정된다.(100,000,000Hz)
pwm_freq는 생성하고자 하는 PWM 신호의 주파수이며 10kHz로 설정된다.
temp - 1을 사용한 이유는 cnt(타이머 카운터)가 0부터 temp - 1까지 증가해야 한다. temp가 1250이라면, 카운터는 0부터 1249까지 증가한다.
temp_half를 사용한 이유는 128분주기 주파수를 가지는 신호의 듀티 사이클을 50%로 유지할 수 있기 때문이다.
128분주를 사용한 이유는 7비트(0~127)로 표현하여 128단계의 세분화된 제어가 가능하기 때문이다. 그리고 더 많은 분해능을 제공하여 보다 정밀한 PWM 신호를 생성하는 데 유리하다.
78분주를 사용하는 이유는 시스템 클록 주파수와 PWM 주파수를 고려하여 타이머 주기를 설정하고, 정확한 PWM 주파수를 유지하기 위함이다.
총 2번의 분주기를 사용하는데 아래와 같은 순서대로 진행하게 된다.
이제 이 소스코드를 가지고 시뮬레이션을 실행해보자
clk를 10ns, reset을 1, duty를 constant에서 unsigned Decimal로 하여 40, 시뮬레이션 돌리는 시간을 10ns로 하여 한 번 실행한다.
그 다음으로 reset을 0, 시뮬레이션 돌리는 시간을 1s로 하다가 중간에 일시정지하면 아래와 같이 확인할 수 있다.
cnt와 cnt_duty에서 오른쪽 클릭하여 Radix를 unsigned Decimal로 하면 십진수로 표시되는 것을 확인할 수 있다.
cnt 카운터는 시스템 클록에 따라 0부터 temp-1까지(1250) 증가한다.
cnt가 temp_half 보다 작을 때, pwm_freqX128은 0이고, 클 때는 1이다.
또한 pwm_freqX128이 활성화될 때마다 cnt_duty 카운터가 증가한다.
cnt_duty가 duty(28)보다 작을 때, pwm은 1로 설정되고 그 외에는 0으로 설정된다.
즉, 주어진 duty 값(28)에 따라 pwm 신호가 약 21.875% 듀티 사이클로 출력되고 있다.
다음으로 pwm_128step 소스코드를 이용하여 Basys3의 led를 제어해보자.
이번에는 switch를 사용하여 led0의 밝기를 조절해본다. 아래와 같이 .xdc 파일을 수정한다.
그러면 아래와 같이 스위치 0번부터 스위치 6번까지 7개의 스위치를 하나씩 순서대로 올리면 led0이 점점 밝아지고,
반대로 스위치 6번부터 스위치 0번까지 하나씩 순서대로 내리면 led0이 점점 꺼지는 것을 확인할 수 있다.
각 스위치에 해당하는 듀티사이클은 아래와 같다.
sw6 | sw5 | sw4 | sw3 | sw2 | sw1 | sw0 |
127 | 63 | 31 | 15 | 7 | 3 | 1 |
다음으로 자동으로 led가 밝아졌다가 최대 밝기에 도달하면 완전히 꺼졌다가 다시 밝아지면서 반복하는 소스코드를 만들어보자.
led_pwm_top 소스코드
//////////////////////////////////////// 2024.7.8
/////////// led가 최대밝기까지 점점 밝아지다가 최대 밝기가 되면 꺼지고 다시 점점 밝아지면서 반복함
module led_pwm_top(
input clk, reset_p,
output led_pwm);
reg [27:0] clk_div;
always @(posedge clk) clk_div = clk_div + 1;
pwm_128step pwm_led(
.clk(clk), .reset_p(reset_p),
.duty(clk_div[27:21]),
.pwm(led_pwm));
endmodule
여기서 reg [27:0] clk_div는 28비트 너비를 가진 레지스터를 의미한다. 즉, 클럭 신호를 분주하기 위해 사용되는 레지스터이다
clk_div[27:21]은 clk_div의 특정 비트를 선택하는 것이며, 27번 비트부터 21번 비트까지 7비트를 선택한다는 의미이다.
선택된 비트들은 PWM 모듈의 듀티 사이클(Duty Cycle)로 사용된다. 듀티 사이클은 PWM의 High 상태의 비율을 결정하므로, LED 밝기를 조절하는 데 사용된다.
이번에는 led1번에 led_pwm이라고 지정하여 결과를 살펴볼 것이기 때문에 아래와 같이 .xdc를 수정한다.
자동으로 밝아졌다가 완전히 꺼지면서 다시 자동으로 밝아지면서 반복하는 것을 확인할 수 있다.
다음으로 RGB_led 모듈에 pwm을 사용하여 led 밝기를 조절해보는 소스코드를 작성해보자.
RGB_led_pwm_top 소스코드
/////////////////////////////////////// rgb_led pwm
module RGB_led_pwm_top(
input clk, reset_p,
output led_pwm, led_R, led_G, led_B);
reg [27:0] clk_div;
always @(posedge clk) clk_div = clk_div + 1;
pwm_128step pwm_led(
.clk(clk), .reset_p(reset_p),
.duty(clk_div[27:21]),
.pwm(led_pwm));
pwm_128step pwm_led_R(
.clk(clk), .reset_p(reset_p),
.duty(clk_div[27:21]),
.pwm(led_R));
pwm_128step pwm_led_G(
.clk(clk), .reset_p(reset_p),
.duty(clk_div[27:21]),
.pwm(led_G));
pwm_128step pwm_led_B(
.clk(clk), .reset_p(reset_p),
.duty(clk_div[27:21]),
.pwm(led_B));
endmodule
RGB_led 모듈에는 각 RGB가 8비트씩 가지고 있다. 그리고 아래처럼 각 led에는 저항을 연결해야한다.
65옴이라는 저항은 없어서 최소 100옴 저항부터 쓸 수 있는데, 강의실에는 220옴 저항만 있기 때문에 그걸로 대체한다.
RGB_led 모듈을 Basys3과 연결하기 위해 Basys3의 JA7, JA8, JA9에 연결하기 위해 아래와 같이 .xdc를 수정한다.
RGB_led 모듈에는 R,G,B,GND가 있는데, RGB에는 각각 220옴 저항을 연결하고 지정한 JA번호와 연결되도록 한다. GND에는 Basys3의 GND에 연결한다.
B(파란색)이 전압이 세서 전체적으로 파란색 위주로 보이는 것을 확인할 수 있다.
밝기가 너무 밝아서 색 구별이 어렵다면 휴지, 솜과 같은 불투명한 물체를 RGB_led 모듈 위에 올려두면 색 관찰이 더 쉽다.
이번에는 응용하여 RGB_led 모듈이 느린 속도로 밝기가 제어되는 소스코드를 작성해보자.
slow_RGB_led_pwm_top 소스코드
/////////////////////////////////////// rgb_led pwm 느린버전
module slow_RGB_led_pwm_top(
input clk, reset_p,
output led_pwm, led_R, led_G, led_B);
reg [31:0] clk_div;
always @(posedge clk) clk_div = clk_div + 1;
pwm_128step pwm_led(
.clk(clk), .reset_p(reset_p),
.duty(clk_div[27:21]),
.pwm(led_pwm));
pwm_128step pwm_led_R(
.clk(clk), .reset_p(reset_p),
.duty(clk_div[31:25]),
.pwm(led_R));
pwm_128step pwm_led_G(
.clk(clk), .reset_p(reset_p),
.duty(clk_div[30:24]),
.pwm(led_G));
pwm_128step pwm_led_B(
.clk(clk), .reset_p(reset_p),
.duty(clk_div[29:23]),
.pwm(led_B));
endmodule
reg [31:0] clk_div;로 수정하고
각 led마다 clk_div의 값을 수정하면 밝기를 느리게 제어할 수 있다.
각각 켜지는 led 색에 따라서 magenta, cyan, yellow 색이 보여지는 것을 확인할 수 있다.
2024.7.8 [Verilog]15 - PWM으로 led 제어하기 끝!
'Harman 세미콘 아카데미 > Verilog' 카테고리의 다른 글
2024.7.10 [Verilog]17 - PWM으로 servo 모터 제어하기, ADC (0) | 2024.07.10 |
---|---|
2024.7.9 [Verilog]16 - PWM으로 모터 제어하기 (0) | 2024.07.09 |
2024.7.5 [Verilog]14 - 다기능 시계 만들기 개인프로젝트 (0) | 2024.07.05 |
2024.7.4 [Verilog]13 - 초음파센서 (0) | 2024.07.04 |
2024.7.3 [Verilog]12 - 습도센서 테스트벤치, 에러 대비, 16진수를 10진수로 변환하기 (0) | 2024.07.03 |