본문 바로가기

MCU 제어/STM32Cube Mx

[STM32]PWM을 이용하여 BLDC 모터 구동하기

BLCD모터는 FC에서 작동시키는게 아니라 FC에서 PWM신호를 받은 ESC가 작동시킨다.

그렇다면 어떻게 ESC에게 PWM신호를 줄 수 있을까?

 

Timer를 이용하면 된다. Cube Mx 설정부터 알아보자. 일단 Timer는 PWM신호를 발생시킬 수 있는 타이머로 설정해야된다. TIM6, TIM7은 Basic Timer이므로 PWM을 발생시킬 수 없다.

 

Timer 설정

나는 고급 타이머인 TIM1번을 선택했다. Slave mode와 Trigger Source는 Disable해두자. 사실 나도 자세히 어떤건지 몰라서 나중에 쓸일이 있으면 설명해보겠다. Clock source는 APB 버스를 통한 내부 클럭을 사용할 것이므로 Internal Clock으로 설정해준다. 외부클럭으로 설정하면 실제 외부핀에서 클럭을 받아 APB버스와 동기화하여 사용한다는데 사용한 적도 없고 잘 몰라서 설명하지 않겠다.

 

그리고 자신이 사용하는 MCU 데이터 시트를 확인하여 내가 쓰는 타이머가 연결된 APB버스가 몇 번인지 확인해야한다. 나는 TIM1이 APB2 timer clock과 연결됬다는 것을 확인했다.

 

그리고 clock configuration 탭에 가면 APB2 timer clock은 216MHz라는 것을 알 수 있다.

 

나는 1ms~2ms 펄스를 줘야하므로 400Hz(2.5ms) PWM 파형을 발생시키도록 Counter setting을 했다.

prescaler = 216, Counter Period = 2500

 

prescaler와 Counter Period의 값을 다르게 주어도 400Hz를 만들 수 있다. 그 대신 분해능은 달라질 것이다. 분해능은 PWM펄스를 얼마나 쪼개서 보낼 수 있느냐하는 것이다. 나는 2500으로 설정했으므로 2500 step의 펄스를 출력할 수 있다. 실제로는 1ms~2ms가 모터를 움직이므로 1000step정도이다. 드론에서는 이 분해능이 너무 낮으면 자세제어가 힘들다. 예시가 너무 극단적이긴 하지만 1step밖에 없다면 On 또는 Off이므로 힘을 주는게 엄청 강하거나 엄청 약하므로 자세제어가 안되겠지? 내 수준에서 할 수 있는 최대의 설명이다...

Timer Clock(216Mhz) / Prescaler(216) / 400Hz = 2500(Auto Reload register, ARR)

그리고 Channer1~4를 PWM Generation CH1~4로 설정했다. 채널이 의미하는 것은 Capture Compare 레지스터(CCR)의 갯수를 의미하는데 설정한 Timer로 각기 다른 4개의 펄스를 4개의 핀에 출력할 수 있다는 것이다.

counter는 0에서 증가하다가 CCR을 만나면 하강엣지가 되었다가 다시 Auto Reload 레지스터를 만나면 상승엣지가 된다. 따라서 CCR의 값을 통해 출력할 펄스(Duty Cycle)를 조절할 수 있다.

 

코드를 설명하기 전에 아래 포스팅을 한 번보고 오면 좋을 것 같다.

https://how-to-make-a-quadcopter.tistory.com/11

 

//main.c
void ESC_CALIBRATION()
{
    TIM1->CCR1 = (uint32_t)2000;
    TIM1->CCR2 = (uint32_t)2000;
    TIM1->CCR3 = (uint32_t)2000;
    TIM1->CCR4 = (uint32_t)2000;
    HAL_Delay(7000);

    TIM1->CCR1 = (uint32_t)1000;
    TIM1->CCR2 = (uint32_t)1000;
    TIM1->CCR3 = (uint32_t)1000;
    TIM1->CCR4 = (uint32_t)1000;
    HAL_Delay(5000);
} //캘리브레이션을 하기위해 최대 Throttle을 입력하고 일정시간이 지난 후 최소 Throttle을 주는 함수

void ESC_ARMING()
{
    TIM1->CCR1 = (uint32_t)1000;
    TIM1->CCR2 = (uint32_t)1000;
    TIM1->CCR3 = (uint32_t)1000;
    TIM1->CCR4 = (uint32_t)1000;
    HAL_Delay(3000);
} //ESC가 PWM신호를 받아들일 수 있도록 최소 Throttle을 일정시간 입력해주는 함수

void TEST_PWM()
{
   TIM1->CCR1 = (uint32_t)1000;
   TIM1->CCR2 = (uint32_t)1300;
   TIM1->CCR3 = (uint32_t)1500;
   TIM1->CCR4 = (uint32_t)2000;
} //잘 작동하는지 테스트하는 함수

int main(void)
{
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); 
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2); 
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3); 
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4);
//PWM파형을 출력하기 시작하는 함수

//ESC_CALIBRATION(); 
   ESC_ARMING();
  TEST_PWM();
    while(1)
    {
        
    }
}

코드는 길지만 솔직히 설명하기 부끄러운 매우 간단한 코드이다. 한 번해보면 쉽게 알 수 있을 것이다.

 

나는 캘리브레이션을 할 떄 귀찮아서 Delay를 이용하였지만 비프음을 듣고 인터럽트를 이용해 다음 Throttle 신호를 주는게 더 정확할 것이다.

 

+ 설명이 이해하기 힘들고 부족하며 틀린 부분이 있을 수 있습니다. 잘못된 부분이 있으면 지적해주시면 감사하겠습니다.

+실행 중 주기(ARR)나 Prescaler를 바꾸고 싶다던지하면 CubeMx가 설정한 TIM설정함수의 정의로 들어가서 어떤 레지스터를 사용하면 되는지 확인해보면된다.

'MCU 제어 > STM32Cube Mx' 카테고리의 다른 글

[STM32] External Interrupt(외부인터럽트)  (0) 2020.07.29
[STM32] Basic Timer  (1) 2020.03.02
[STM32]UART 인터럽트 송수신 실습  (3) 2020.02.29
Cube MX란?  (0) 2020.02.21
[STM32]ST-Link 사용법 및 IAR 디버깅 방법  (0) 2020.02.02