RTL Design/Verilog RTL 설계
Verilog RTL 설계(8월 2일 - 2, PWM을 통한 Servo-motor 제어 - 2)
유로 청년
2024. 8. 16. 16:52
1. 시간에 따라 Servo-motor가 이동하도록 설계
- 이전 게시글까지 Servo-motor에 대해서 살펴보고, -90도, 0도, 90도에 위치시키도록 하기 위해서는 얼마만큼의 duty ratio가 필요한지를 실험을 통해 확인할 수 있었다.
- 이번 게시글에는 이를 토대로 servo-motor를 활용하도록 하겠다.
- Servo-motor에 대해서 궁금하거나 자세히 알고 싶다면 아래 게시글을 참고하길 바란다.
https://jbhdeve.tistory.com/306
Verilog RTL 설계(8월 2일 - 1, PWM을 통한 Servo-motor 제어 - 1)
1. Servo - motorServo는 "노예, 추종하다."를 의미하며, 라틴어의 servus에서 유래했다.Servo motor는 주로 기기를 시스템이 요구하는 위치로 이동하거나 특정 속도 및 토크로 가동시킬 때, 정확하게 제어
jbhdeve.tistory.com
- 이번에는 시간에 따라 Servo-motor를 -90도 ▶ 0도 ▶ 90도 ▶ 0도 ▶ -90도 ▶ .... 로 위치시키도록 설계하겠다.
- Servo-motor가 -90도, 0도, 90도에 위치시키기 위해서 이전 게시글을 통해 얻은 duty ratio을 활용하도록 하겠다.
< Source, PWM Control Module >
// PWM Control module
module pwm_cntr_1 #(
parameter sys_clk = 100_000_000,
parameter duty_step = 40,
parameter pwm_freq = 50,
parameter temp = sys_clk / duty_step / pwm_freq,
parameter temp_half = temp/2 )(
input clk, reset_p,
input [31:0] duty,
output pwm );
// temp 분주화
integer cnt_temp;
always @(posedge clk or posedge reset_p) begin
if(reset_p) cnt_temp = 0;
else begin
if(cnt_temp >= temp-1) cnt_temp = 0;
else cnt_temp = cnt_temp + 1;
end
end
wire temp_pwm, temp_pwm_nedge;
assign temp_pwm = (cnt_temp < temp_half) ? 0 : 1;
edge_detector edge_detector_0 (.clk(clk), .reset_p(reset_p),
.cp(temp_pwm), .n_edge(temp_pwm_nedge));
// duty_step 분주화
integer cnt_duty;
always @(posedge clk or posedge reset_p) begin
if(reset_p) cnt_duty = 0;
else if(temp_pwm_nedge) begin
if(cnt_duty >= duty_step - 1) cnt_duty = 0;
else cnt_duty = cnt_duty + 1;
end
end
assign pwm = (cnt_duty < duty) ? 1 : 0;
endmodule
- 첫 번째 분주화 작업은 pwm_freq 주파수를 갖는 PWM을 생성하기 위한 작업이며, 이를 위해 10ns Clock Pulse를 temp 분주화 작업을 한다. 이를 통해 얻어진 PWM은 sys_clk / temp 주파수를 갖는 PWM이 된다.
- 두 번째 분주화 작업은 PWM의 Duty ratio을 duty_step 단계로 나누어 컨트롤하기 위한 작업이며, 이를 위해 첫 번째 분주화 작업을 통해 얻어진 PWM에서 duty_step 분주화 작업을 한다. 이를 통해 얻어진 PWM은 sys_clk / temp / duty_step == pwm_freq 주파수를 갖는 PWM이 된다.
< Source, Top module of Servo motor >
// Top module of Servo-Motor
module servo_motor_cntr_1(
input clk, reset_p,
output [3:0] com,
output [7:0] seg_7,
output pwm );
// Declare Parameter
reg up_down;
// Get 1sec One Cycle Pulse
wire clk_1usec, clk_1msec, clk_1sec;
clk_div_100 clk_div_100_0 (.clk(clk), .reset_p(reset_p), .clk_div_100(clk_1usec));
clk_div_1000 clk_div_1000_0 (.clk(clk), .reset_p(reset_p), .clk_source(clk_1usec),
.clk_div_1000(clk_1msec));
clk_div_1000 clk_div_1000_1 (.clk(clk), .reset_p(reset_p), .clk_source(clk_1msec),
.clk_div_1000_nedge(clk_1sec));
reg [7:0] counter;
always @(posedge clk or posedge reset_p) begin
if(reset_p) begin counter = 6; up_down = 1; end
else if(clk_1sec) begin
if(counter >= 25) begin counter = 24; up_down = 0; end
else if(counter <= 5) begin counter = 6; up_down = 1; end
else begin
if(up_down) counter = counter + 1;
else counter = counter - 1;
end
end
end
// PWM Control
pwm_cntr_1 #(.duty_step(200), .pwm_freq(50)) pwm_cntr_0 (.clk(clk), .reset_p(reset_p), .duty(counter), .pwm(pwm));
// Convert from binary to BCD.
wire [15:0] bcd_duty;
bin_to_dec bin_to_dec_0 (.bin(counter), .bcd(bcd_duty));
// FND Control
fnd_cntr fnd_cntr_0 (.clk(clk), .reset_p(reset_p), .hex_value(bcd_duty), .com(com), .seg_7(seg_7));
endmodule
- PWM의 duty 값은 5 ~ 25까지 값을 가지며, duty 값은 1초 One Cycle Pulse에 의해 1초마다 변화한다.
- up_down 변수에 의해 서보 모터에 전달되는 PWM의 Duty 값이 up counting이 되는가? down counting이 되는가? 가 결정된다.
- up_down 변수 값이 1이면 up-counting하며, up_down 변수 값이 0이면 down-counting한다.
< 구현 영상 >
- 50Hz 주파수를 갖는 PWM의 duty 값을 5 ~ 25까지 범위를 갖고, 서보 모터에 인가함으로서 1초마다 duty 값이 변화하면서 이에 따른 서보 모터는 위치 값이 변화함을 확인할 수 있다.
2. 버튼 값에 따라 Servo-motor가 이동하도록 설계
- 이번에는 3개의 버튼을 이용하여 각각의 버튼 값에 따라 서보 모터의 위치 값이 -90도, 0도, 90도에 위치시키도록 설계해보도록 하겠다.
- pwm_cntr 모듈은 그대로 사용하되, Top module만 변경하여 설계할 것이기 때문에 pwm_cntr 모듈에 대한 설명은 생략하도록 하겠다.
< Source, PWM Control >
// PWM Control
module pwm_cntr_2 # (
parameter sys_clk = 100_000_000,
parameter duty_step = 100,
parameter pwm_freq = 50,
parameter temp = sys_clk / duty_step / pwm_freq,
parameter temp_half = temp / 2 )(
input clk, reset_p,
input [31:0] duty,
output pwm );
// temp 분주화
integer cnt_temp;
always @(posedge clk or posedge reset_p) begin
if(reset_p) cnt_temp = 0;
else begin
if(cnt_temp >= temp-1) cnt_temp = 0;
else cnt_temp = cnt_temp + 1;
end
end
wire pwm_temp, pwm_temp_nedge;
assign pwm_temp = (cnt_temp < temp_half) ? 0 : 1;
edge_detector edge_detector_0 (.clk(clk), .reset_p(reset_p),
.cp(pwm_temp), .n_edge(pwm_temp_nedge));
// duty_step 분주화
integer cnt_duty;
always @(posedge clk or posedge reset_p) begin
if(reset_p) cnt_duty = 0;
else if(pwm_temp_nedge) begin
if(cnt_duty >= duty_step-1) cnt_duty = 0;
else cnt_duty = cnt_duty + 1;
end
end
assign pwm = (cnt_duty < duty) ? 1 : 0;
endmodule
< Top module of servo motor >
// Top module of Servo-motor
module servo_motor_cntr_2(
input clk, reset_p,
input [2:0] btn,
output pwm,
output [3:0] com,
output [7:0] seg_7);
// Get one cycle pulse of button.
wire btn_mid, btn_left, btn_right;
btn_cntr btn_cntr_mid(.clk(clk), .reset_p(reset_p), .btn(btn[0]), .btn_pedge(btn_mid));
btn_cntr btn_cntr_left(.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_pedge(btn_left));
btn_cntr btn_cntr_right(.clk(clk), .reset_p(reset_p), .btn(btn[2]), .btn_pedge(btn_right));
// Select duty ratio.
reg [31:0] duty;
always @(posedge clk or posedge reset_p) begin
if(reset_p) duty = 5;
else begin
if(btn_mid) duty = 32'd15;
else if(btn_left) duty = 32'd5;
else if(btn_right) duty = 32'd25;
end
end
// PWM Control
pwm_cntr_2 #(.duty_step(200), .pwm_freq(50)) pwm_cntr (.clk(clk), .reset_p(reset_p), .duty(duty), .pwm(pwm));
// Convert from binary to BCD.
wire [15:0] bcd_duty;
bin_to_dec bin_to_dec_0 (.bin(duty), .bcd(bcd_duty));
// FND Control
fnd_cntr fnd_cntr_0 (.clk(clk), .reset_p(reset_p), .hex_value(bcd_duty), .com(com), .seg_7(seg_7));
endmodule
- button chattering 문제를 해결하기 위해 btn_cntr 모듈을 설계하였으며, btn_cntr 모듈내에서는 D-Flip Flop을 이용하여 button 값을 바로 입력 받는 것이 아닌 1msec delay time을 갖고, button 값을 받는다.
- 이를 통해 chattering 현상으로 인한 불안정한 버튼 값의 변화가 지나고 난 뒤, 안정된 버튼 값을 입력 받아 chattering 문제를 소프트웨어 적으로 해결하게 된다.
- 3개의 버튼을 통해 서보 모토의 위치를 3단계로 나누어 컨트롤 하게 된다.
< 구현 영상 >