본문 바로가기
# Semiconductor/- Semicon Academy

[Harman 세미콘 아카데미] 61일차 - Verilog(PWM을 활용한 Servo Motor의 각도 제어)

by Graffitio 2023. 9. 20.
[Harman 세미콘 아카데미] 61일차 - Verilog(PWM을 활용한 Servo Motor의 각도 제어)
728x90
반응형
[Multi Function Clock PT]

 

오전에는 이틀동안 진행했던 다기능 시계 프로젝트에 대한 PT를 진행하였다.

짧은 기간이었음에도 불구하고 모든 팀들이 완성도 높은 시계를 구현하였고

중간 중간에 놀라움과 즐거움을 주는 팀들도 있었다.

 

요즘 기업에서 PT 면접을 많이 진행한다 하는데,

미리 맛볼 수 있는 좋은 기회가 되었고

교수님께서도 자주 진행하신다 하시니, 

다음 번에는 좀 더 높은 퀄리티를 목표로 달려봐야겠다.

 


 

 

[취업 설명회 - OneSemicon]

 

One Semicon이라는 기업에서 취업 설명회를 개최하였다.

상무님(CFO)께서 직접 설명회를 진행하셨고

비메모리 반도체 생태계의 전반적인 상황,

대표이사님이 왜 RCD를 사업 아이템으로 시작했는지,

그리고 회사의 비젼 및 사내 분위기, 직무 등에 대해 알려주셨다.

 

지금은 뭐 가고 싶다 해도 아직 하만 과정이 절반 이상 남아서 가지도 못 한다.

그냥 현재에 충실해야지.

 


 

[PWM을 활용한 Servo motor 구동]

 

📌 Servo Motor에 대하여

 

Servo Motor에 대한 개념은 

 

아래 링크 참조

 

[Harman 세미콘 아카데미] 24일차 - ATmega128(PWM 활용, Servo Motor)

[참조] https://rangvest.tistory.com/manage/newpost/101?type=post&returnURL=https%3A%2F%2Frangvest.tistory.com%2Fmanage%2Fposts https://rangvest.tistory.com/manage/newpost/101?returnURL=https%3A%2F%2Frangvest.tistory.com%2Fmanage%2Fposts&type=post rangves

rangvest.tistory.com

 

SG90 9 g Micro Servo.pdf
0.26MB
sg90_datasheet.pdf
0.13MB

 


 

📌 Servo Motor 설계 구상

 

 

각도 조절

 

Data Sheet를 보면,

Servo Motor의 PWM 주기는 50Hz(20ms) 범위에서 동작하고

0~2ms의 듀티 사이클 안에서 각도가 조절된다.

여기에는 pulse width가 1.5ms일 때, 0 / ~2ms 일 때, 90˚ / ~1ms일 때, -90˚ 라고 나와 있으나,

중국 제품이므로 동작시켜보면서 추가 칼리브레이션이 필요할 것 같다.

 

또한 10㎲의 펄스를 사용하면 죽어버리니 주의하여 사용하도록 하자.

 

정밀도 선택

 

저번 시간에 PWM을 활용하여 LED 밝기를 조절할 때는 1/100의 정밀도를 사용하였다.

Servo Motor는 20ms의 주기 중 2ms까지만 사용하여 각도를 조절해야 하는데

따지고보면 10%의 듀티사이클 내에서 각도를 조절해야된다는 의미이다.

그렇게 되면 총 10단계로밖에 조절이 안 된다는 얘기인데

Servo Motor를 부드럽게 동작시키기 위해서 1단계마다 18˚씩 구동되는 1/100의 정밀도는 한계가 있다.

 

따라서 우리는 1/1000의 정밀도를 사용하여 1단계마다 1.8˚씩 구동되도록 만들 것이다. 

 


 

📌 Servo Motor 코드 및 칼리브레이션

 

1000정밀도의 PWM 모듈

 

module PWM_1000(
    input clk, rstp,
    input [9:0] duty, // 듀티비 설정 변수 
    input [13:0] pwm_freq, // parameter 대신 여기에 선언해도 된다. 10,000Hz 쓸 거니까 14bit
    output reg pwm_1000pc // 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_freqX1000; // 100배된 클럭 주기 생성을 위한 변수 
    always @(posedge clk, posedge rstp) begin
        if(rstp) begin
            cnt = 0;
            clk_freqX1000 = 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 / 1000)) begin // 듀티비를 계산하기 위해 1000 나눠주고 clk_freqX100을 카운트하여 0과 1을 부여한다.
            if(cnt >= (sys_clk_freq / pwm_freq / 2000)) begin // always문 들어 올때마다 나누기 연산하게 되므로, Negative Slack 발생 가능성 높아짐. 간단하게 바꿔주자
                cnt = 0;
                clk_freqX1000 = ~clk_freqX1000; // 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_freqX1000_n;
    edge_detector_p edg(.clk(clk), .cp_in(clk_freqX1000), .rst(rst), .n_edge(clk_freqX1000_n)); // Edge Detector
    // 이전 always문에서 clk_freqX100 동작하고 4ns 후에나 edge 잡을 것이다.
    // Timing 문제로 인해 posedge clk를 사용하는 edge detector를 사용하는 것이 좋다.
    // 지금 상황에서는 nedge clk보다 pedge clk가 약 2배정도 더 여유가 있다.
    
    reg [9:0] cnt_duty; // 듀티사이클의 카운터 변수 
    always @(posedge clk, posedge rstp) begin
        if(rstp) begin
            cnt_duty = 0;
            pwm_1000pc = 0;
        end
        else if(clk_freqX1000_n) begin
            if(cnt_duty >= 999) begin // 0~999까지 1000번 카운트하여 1/1000 정밀도 구현
                cnt_duty = 0;    
            end
            else begin
                cnt_duty = cnt_duty + 1;
            end
            if(cnt_duty < duty) pwm_1000pc = 1; // 예를 들어 50을 duty로 받으면, 50까지는 1, 51~999까지는 0
            else pwm_1000pc = 0;
        end
    end 
endmodule

 

Servo Motor Top

 

pwm_freq : Servo Motor의 PWM 주기는 50Hz이므로 50을 준다.

원래는 0˚를 구현하기 위해 20ms의 1.5ms의 듀티사이클을 사용해야 했고,

여기에 정밀도를 적용하기 위해 1000을 곱하면 75가 나온다.

duty = (1.5 / 20) x 1000 → 0~1000에 0도에 해당하는 듀티사이클인 (1.5 / 20)을 곱해준다. 

 

위와 같이 0˚, 90˚, -90˚의 duty 값을 계산해서 적용시켰으나,

역시 중국 제품이라 Data Sheet와 맞지 않는다.

1씩 조절하면서 칼리브레이션한 결과는 아래와 같다.

 

module Servo_Motor_Top(
    input clk, rstp,
    input [3:0] btn,
    output servo,
    output reg [7:0] LED_bar
    );
    
    wire [2:0] mode;
    button_cntr btn_cntr_mode(.clk(clk), .reset_p(rstp), .btn(btn[0]), .btn_pe(mode_btn));
    counter_dec_mode mode_select(.clk(clk), .reset_p(rstp), .btn(mode_btn), .dec1(mode));
    
    reg [9:0] duty;
    always @(posedge clk) begin
            case(mode) // 숫자가 커질수록 -90도에 가까워짐
                0 : begin // 90도
                    duty = 7'd28;
                    LED_bar = 8'b0000_0001;
                end
                1 : begin // 0도
                    duty = 7'd77;
                    LED_bar = 8'b0000_0010;
                end
                2 : begin // -90도
                    duty = 7'd125;
                    LED_bar = 8'b0000_0100;
                end
            endcase
    end
    PWM_1000 pwm__90(.clk(clk), .rstp(rstp), .duty(duty), .pwm_freq(50), .pwm_1000pc(servo));
endmodule

 


 

📌 Result

 

 

각 단계에 따라서 90˚, 0˚, -90˚ 에 맞게 동작한다.

 

 

📌 오류 수정

 

reg [6:0] duty;

Error

 

우리는 정밀도를 1000까지 줬고, duty를 1000까지 세려면 10bit까지 써야된다.

하지만 그걸 생각 안 하고 7bit만 줬고 duty는 128까지만 카운트되고 있었다.

따라서 PWM 주기는 2.5ms가 되었고 

운이 좋다고 해야 되나? -90˚에 해당하는 값이 125라서 동작은 정상적으로 되었다.

그 덕분에 이상한 곳에서 원인을 찾고 있었고, 같이 공부하는 동기와 상의하던 도중에

진짜 원인을 발견해서 수정하였다.

 

// 수정 후 코드
reg [9:0] duty;

 


 

728x90
반응형