728x90
반응형
[PWM]
📌 PWM 기초
클락의 펄스를 예로 들어 보자.
module clock_usec(
input clk, rst,
input enable,
output clk_usec
);
// 125개 카운트하면 1us
reg [6:0] cnt_8nsec;
wire cp_usec; // cp : clock pulse
always @(posedge clk, posedge rst) begin
if(rst) cnt_8nsec = 0; // reset이 들어오면 cnt = 0
else if(cnt_8nsec >= 124) cnt_8nsec = 0; // 0부터 124번까지 카운트하면 다시 0으로
else cnt_8nsec = cnt_8nsec + 1;
end
assign cp_usec = cnt_8nsec < 63 ? 0 : 1; // 0~62 : 0, 63~124 : 1
// 여기서 63 값을 조정하여 듀티사이클를 조정할 수 있다.
// 예를 들어 90으로 바꾸면, 0~89까지는 0이고, 90~124까지는 1이므로 듀티사이클이 줄어든다.
// 비정규 clock이므로 동기화가 필요하다.
edge_detector_n edg(.clk(clk), .cp_in(cp_usec), .rst(rst), .n_edge(clk_usec)); /// 안 쓰는 출력은 빼버리면 된다.
endmodule
위 코드에서 cnt_8nsec 값을 비교하는 부분을 조절하면, 듀티사이클을 조절할 수 있다.
📌 부품에서의 PWM 사용
LED는 주파수가 1000Hz에서 10000Hz일 때 출력이 잘 나온다
최소 2ms 주기로 켜져있으면, 깜빡이는 것처럼 보이지 않는다.(다이내믹 구동)
깜빡이는 속도는 주파수로 조절, 밝기는 듀티사이클로 조절
모터는 100~1kHz 정도의 주파수로 조절해야 한다. 더 커지면 이잉 소리만 나면서 제대로 동작 안함.
📌 PWM 코드 및 설명
1초에 1000개의 클락을 만들 거면,
클락은 이 것의 100배인 100,000개가 나와야 하고
그 중에서 100개 셀 때마다 pwm이 하나씩 나와야 한다.
- 이 때, 100개 중에서 1의 갯수에 따라 듀티 사이클이 정해진다.
- 또한 여기서 100을 정밀도라고 한다.
따라서 1초에 1000개의 pwm이 나오게 되고
이 pwm의 주파수는 1000Hz
module PWM_100(
input clk, rstp,
input [6:0] duty, // 듀티비 설정 변수
input [13:0] pwm_freq, // parameter 대신 여기에 선언해도 된다. 10,000Hz 쓸 거니까 14bit
output reg pwm_100pc // pc : % -> 하나당 센티(1/100)이라는 뜻
);
parameter sys_clk_freq = 125_000_000; // cora boadr의 시스템 클락
// parameter pwm_freq = 1000; // 1000Hz짜리 pwm을 만들기 위한 define
reg [26:0] cnt;
reg clk_freqX100; // 100배된 클럭 주기 생성을 위한 변수
always @(posedge clk, posedge rstp) begin
if(rstp) begin
cnt = 0;
clk_freqX100 = 0;
end
else begin
// 125000까지 카운트하고 클럭 주기 생성
// if(cnt >= 124_999) begin // 1000Hz짜리를 만들 것이기 때문에 sys clk를 1000으로 나눔
// if(cnt >= (sys_clk_freq / pwm_freq / 2)) begin // 토글시켜서 사용할 것이니까 2로 나눔
// if(cnt >= (sys_clk_freq / pwm_freq / 2 / 100)) begin // 듀티비를 계산하기 위해 100 나눠주고 clk_freqX100을 카운트하여 0과 1을 부여한다.
if(cnt >= (sys_clk_freq / pwm_freq / 200)) begin // always문 들어 올때마다 나누기 연산하게 되므로, Negative Slack 발생 가능성 높아짐. 간단하게 바꿔주자
cnt = 0;
clk_freqX100 = ~clk_freqX100; // pwm 한 주기를 만들기 위한 문장
end
else begin
cnt = cnt + 1; // 클락을 세는 카운터
end
// // Duty cycle : 50%
// if(cnt < 62500) begin
// pwm_100pc = 0;
// end
// else begin
// pwm_100pc = 1;
// end
end
end
wire clk_freqX100_n;
edge_detector_p edg(.clk(clk), .cp_in(clk_freqX100), .rst(rst), .n_edge(clk_freqX100_n)); // Edge Detector
// 이전 always문에서 clk_freqX100 동작하고 4ns 후에나 edge 잡을 것이다.
// Timing 문제로 인해 posedge clk를 사용하는 edge detector를 사용하는 것이 좋다.
// 지금 상황에서는 nedge clk보다 pedge clk가 약 2배정도 더 여유가 있다.
reg [6:0] cnt_duty; // 듀티사이클의 카운터 변수
always @(posedge clk, posedge rstp) begin
if(rstp) begin
cnt_duty = 0;
pwm_100pc = 0;
end
else if(clk_freqX100_n) begin
if(cnt_duty >= 99) begin
cnt_duty = 0;
end
else begin
cnt_duty = cnt_duty + 1;
end
if(cnt_duty < duty) pwm_100pc = 1; // 예를 들어 50을 duty로 받으면, 50까지는 1, 나머지는 0
else pwm_100pc = 0;
end
end
endmodule
📌 PWM Simulation
clk : 8ns로 0 ~1 반복
rstp : 8ns동안 1줘서 초기화하고 0으로 전환
duty : Unsigned Decimal로 30 (30%)
pwm_freq : Unsigned Decimal로 1000 (주파수 1000Hz짜리 pwm)
728x90
반응형