거북이처럼 천천히

Verilog RTL 설계(8월 2일 - 3, PWM을 통한 Servo-motor 제어 - 3) 본문

RTL Design/Verilog RTL 설계

Verilog RTL 설계(8월 2일 - 3, PWM을 통한 Servo-motor 제어 - 3)

유로 청년 2024. 8. 17. 23:46

1. 버튼을 통해 Servo-motor의 duty 를 컨트롤하기

  • 4개의 버튼을 통해 서보 모터에 인가되는 PWM의 duty ratio를 컨트롤 할 수 있도록 설계하겠다.
  • 4개의 버튼들은 다음과 같은 동작을 수행하게 된다.
    - btn[0], btn_dir : duty ratio의 값을 up-counting할 것인지, down-counting할 것인지를 결정한다.
    - btn[1], btn_min : duty ratio의 minimum value를 결정한다.
    - btn[2], btn_max : duty ratio의 maximum value를 결정한다.
    - btn[3], btn_reset : 설정된 duty ratio의 범위를 초기화한다.

 

 

1.1. 버튼을 통한 서보 모터 제어 알고리즘

  • 서보 모터에 50Hz 주파수를 갖고, duty step이 200단계인 PWM을 인가한다.
  • 이전 설계를 통해 해당 PWM을 전달할 경우, duty ratio가 5 ~ 25단계일 때 서보 모터가 -90도 ~ 90도 사이를 위치 조정할 수 있음을 확인할 수 있었기 때문에 이번 구현에서 duty 값은 5 ~ 25 범위를 갖고, up / down counting을 하게 된다.
  • 4개의 버튼을 누르게 되면 다음과 동작하도록 설계하였다.
    - btn_dir : 해당 버튼을 누르면 counting의 방향이 전환되게 된다.
    - btn_min : 해당 버튼을 누르면 현재의 duty 값이 duty 값의 하한값이 된다.
    - btn_max : 해당 버튼을 누르면 현재의 duty 값이 duty 값의 상한값이 된다.
    - btn_reset : 해당 버튼을 누르면 duty 값의 하한, 상한값이 초기 상태인 5와 25로 돌아간다.

 

 

 

2. 버튼을 통해 Servo-motor의 duty 를 컨트롤 하기 (설계)

< Source, PWM Control Module >

// PWM Control Module
module servo_motor_cntr_3 #(
    parameter sys_clk = 100_000_000,
    parameter duty_step = 200,
    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
  • Servo-motor에 인가되는 PWM은 50Hz 주파수를 가지며, duty ratio를 200단계로 나누어 컨트롤 할 수 있다.

 

 

< Source, Top module of control survo-motor >

// Top module of Servo - motor
module top_module_of_servo_motor (
    input clk, reset_p,
    input [3:0] btn,
    output [3:0] com,
    output [7:0] seg_7,
    output pwm );
    
    // Get one cycle pulse of button.
    wire btn_dir, btn_min, btn_max, btn_reset;
    btn_cntr btn_cntr_dir (.clk(clk), .reset_p(reset_p), .btn(btn[0]), .btn_pedge(btn_dir));
    btn_cntr btn_cntr_left (.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_pedge(btn_min));
    btn_cntr btn_cntr_right (.clk(clk), .reset_p(reset_p), .btn(btn[2]), .btn_pedge(btn_max));
    btn_cntr btn_cntr_reset (.clk(clk), .reset_p(reset_p), .btn(btn[3]), .btn_pedge(btn_reset));
    
    // 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));
    
    // Select duty ratio of PWM.
    reg [4:0] duty, min_duty, max_duty;
    reg left_right;
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin 
            duty = 5; left_right =  0; 
            min_duty = 5; max_duty = 25;
        end
        else if(btn_reset) begin
            min_duty = 5; max_duty = 25;
        end
        else if(btn_dir) left_right = ~left_right;
        else if(btn_min) min_duty = duty;
        else if(btn_max) max_duty = duty;
        else if(clk_1sec) begin
            if(!left_right) begin
                if(duty >= max_duty) begin
                    duty = max_duty - 1;
                    left_right = ~left_right;
                end 
                else duty = duty + 1;
            end
            else begin
                if(duty <= min_duty) begin
                    duty = min_duty + 1;
                    left_right = ~left_right;
                end 
                else duty = duty - 1;
            end
        end
    end
    
    // PWM Control module
    servo_motor_cntr_3 #(.duty_step(200), .pwm_freq(50)) pwm_cntr 
       (.clk(clk), .reset_p(reset_p), .duty(duty), .pwm(pwm));
    
    // Show FND to Basys 7-segment.
    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 모듈와 D Flip-Flop을 통해 1msec delay time을 갖고, button 값을 읽게된다.
  • duty의 하한 값을 5으로, 상한 값을 25으로 초기화한 뒤, 각 버튼의 one cycle pulse가 활성화 되면 해당 버튼의 역활에 맞게 현재의 duty 값을 duty 범위의 한계로 설정한다.
  • btn_reset 버튼이 눌러지게 되면 세팅되었던 duty의 범위를 다시 5와 25로 초기화된다.

 

 

 

 

3. 구현 영상

  • btn_min 버튼이 눌려지게 되면 현재 duty 값이 duty ratio의 하한 값으로 설정되며,
    btn_max 버튼이 눌러지게 되면 현재 duty 값이 duty ratio의 상한 값으로 설정되게 된다.
  • btn_reset 버튼이 눌르게 되면 세팅 되었던 duty 값의 범위가 다시 초기화 된다.