Pulse Width Modulation
PWM은 Pulse의 1 주기에서 High/Low의 width를 Modulation하는 기술이다.
즉, 폭에 변조를 통하여 의미(Data)를 부여해주는 것이다.
Pulse 폭에 원하는 Data를 관련시키는 변조 방식이다.
라디오를 보면 AM/FM를 볼 수 있는데 이들은 진폭과 주파수로 Audio신호를 변조 하는 것처럼 말이다.
High와 Low에 대한 비율을 Width를 통해 정하고, Duty cycle을 정할 수 있다.
예를 들어, PWM 신호를 전력 제어용 반도체에 연결시, High 폭이 넓을수록 더 많은 전력을 전송하고,
모터의 High 폭이 넓을수록 속도가 더 빨라지고,
LED도 High 폭이 넓을수록 밝기가 더 밝아지고
음향, 파형발생 등등에도 마찬가지로 적용된다.
PWM은 각각 Channel들을 보유한다.
DC모터를 구동하기 위해선 4개의 Channel로 구성된 H-bridge(Full-bridge) 구조를 사용한다.
L9110S, L289N과 같은 모터 드라이버를 보면 상품설명에 H-bridge가 기재되어있음을 확인할 수 있다.
이를 반으로 나눠 VDC/2를 출력하는 Half-bridge 구조도 존재한다.
이 Full-bridge의 경우 단락이 발생하지 않도록, 특정 신호들이 항상 역으로 발생해야 한다.
예를 들어 S1, S2, S3, S4의 스위치가 있다면,
S1과 S4는 동시에 연결되어선 안되고
S2와 S3는 동시에 연결되어선 안된다.
S1 = ~S4 , S2 = ~S3
전력 반도체를 사용하는 경우 ON에 비해 OFF의 V level 하강 시간이 더 오래걸려
2개의 스위치가 동시 연결(단락, arm short)되는 현상때매 큰 문제가 발생할 수 있다.
이를 위해
하는 기능이 존재한다.
따라서 PWM 신호를 미세하게 제어하는 것이 굉장히 중요하다.
PWM의 parameters
Period(Frequency) :
50[Hz] 혹은 20[ms]를 주기로 반복적인 pulse 구형파가 생성되도록 한다.
주로 서보모터를 쓸 때 일반적으로 채택하는 주기 혹은 주파수이다.
Duty Cycle :
위 주기를 예로 들면, 한 주기가 20[ms]다.
이때, High Level인 구간이 4[ms]라면, Duty Cycle은 4/20으로
1/5 즉, 20%가 된다.
PWM with Timer
PWM신호는 TIM6, TIM7인 Basic Timer는 생성할 수 없다.
실제로 CubeMX를 실행해보면 PWM 설정자체가 존재하지 않는다.
I/O pin 설정을 할 수가 없기 때문이다.
참고로 Basic Timer는 Up counting밖에 지원을 안한다.
즉, TIM2~TIM5인 GP Timer와
TIM1, TIM8인 Advanced Timer만 생성이 가능하다.
여기에 Advanced Timer는 보완 Output을 지원한다.

첫 번째는 CNT counter register다. Counting은 16bit로 0~65535까지 가능하다.
우선, PWM 신호를 만들기 위해 TIM설정을 먼저해야한다.
PSC에 설정한 clock을 기준으로 특정 시간에 대한 주기를 생성해주고
CNT counter가 계속 UP / Down을 하다가 ARR(16bits)값과 일치하면,
event를 발생시켜 일정 구간을 또 생성한다(Data 의미 부여).
PWM에는 2가지의 mode가 있다.
PWM mode 1 :
CNT counter < CCR일 때 High 출력, 그렇지 않으면 Low 출력
PWM mode 2 :
CNT counter < CCR일 때 Low 출력, 그렇지 않으면 High 출력
여기에 Duty Cycle을 생성하기 위한 CCR(Capture Compare Register)를 추가하면
다음과 같이 된다.

mode1의 경우, 다음과 같이 CCR보다 Counter 값이 작을때 High를 출력하게 된다.

mode2의 경우, CCR보다 클 때 High를 출력하기 때문에 다음과 같이 된다.
이 때, OCREF는 Output Compare REFerence의 약어이다.
예를들어 CubeMX에서 설정시
Clock Source로는 Internal clock(80[MHz])을 선택 후,
Channel 설정에서 PWM Generation CH1, CH2를 각각 선택해주면
PA8, PA9에 TIM1_CH1, TIM_CH2가 PWM 출력 port로 할당된다.
이때, Clock Source가 80000000[Hz]이고, PSC를 8000으로 두게 되면,
10[kHz]가 된다. ARR을 100으로 할당해주면, 10[ms]마다 UI(overflow)가 발생한다.
여기에 Pulse(CCR)에 90을 지정해준다.
Channel 1과 2는 mode가 1과 2로 상이하다면
Mode 1 :
CNT counter < CCR 일때 High, 아닐때 Low 출력
즉, 0~89까지는 High를, 90~100 까지는 Low를 출력하는
90% Duty Cycle을 CCR을 통해 Data를 Width에 담아낼 수 있다.
Mode 2 :
CNT counter < CCR 일때 Low이기 때문에
여기는 반대로 10%의 Duty Cycle을 갖는 PWM 신호가 생성된다.
결론적으로 10[ms]중
Mode 1 : 9[ms]동안 High이다.
Mode 2 : 1[ms]동안 High이다.
다음과 같이 main.c에서 해당 timer를 enable과 시켜줌과 동시에 channel도 할당해 주어야한다.
HAL_TIM_PWM_Start() 호출 시 자동으로 Timer가 동작하므로
굳이, HAL_TIM_Base_Start() 함수로, Code Segment를 늘려 중복 호출할 필요가 없다.
다만, Interrupt를 쓰는 경우엔 필요할 수 있다.
과거에 반복적으로 함수를 호출하며 ( 내부에 heap 영역을 쓰는 변수가 있는데 재정의 하면서 해제가 제대로 이루어지지 않았다.) Memory leak이 발생한 경험이 있기 때문에 이런 부분은 조심하면서 작성해야겠다.
📌 정리하자면,
PWM 신호 생성 자체는 만들어진 시간 내에서
Output Compare 기반으로 생성된다.
이처럼, Advanced Timer와 GP Timer는 각각 자신만의 CCR을 보유하고 있다.
MCU에 따라 갯수의 차이는 있는데 M4 core이상의 고성능 core는 CCR1~CCR6 총 6개를 갖는다.
하지만, CCR1 ~ CCR4 이 4개의 Capture Compare Register(Channel)들만 PWM 신호를 출력 가능하다.
CCR5와 CCR6은 내부 동기화를 위한 trigger 신호 생성용으로만 사용가능하다.
TIM 구조체를 확인해보면, PSC, ARR, CCR, CNT register들을 확인해 볼 수가 있다.
CCR에 정해진 값과 같다면 event가 발생하고, 이에 따라 PWM 신호가 출력되는 것이다.
즉, High level 구간을 설정하기 위한 CCR 값은
Duty_Cycle = ( ARR - CCR ) / ARR
다음과 같고 이를 CCR에 대해 풀게 되면
CCR = ARR - ARR(Duty_Cycle) = ARR(1-Duty_Cycle)
임을 위에 나온 사진들을 통해 이끌어 낼 수가 있다.
예를 들어, 16bit MAX값을 기준으로
Duty_Cycle이 60%인 PWM신호를 생성하고 싶다면,
65535 X (1-0.6) = 26214를 CCR register에 대입해주면 된다.
다만, HAL 라이브러리의 __HAL_TIM_SET_COMPARE(&TIMx, CHANNEL_NO, CCR);
을 통해 Timer가 ARR까지 CNT를 세다가 Output Compare을 통해 CCR보다 크거나 작을때 event를 발생시켜, 간편히 Width에 High/Low의 의미를 부여할 수 있다.
혹은,
직접 TIMx->CCRx에 접근하여 Duty_Cycle을 변경 할 수 있다.(당연히 둘 다 실행중 변경가능)
예를 들어, 50[ms]마다 UI를 발생시키도록 Timer 설정을 해준 뒤,
UI callback함수에서 직접 CCR에 접근해,
callback 함수 내용을 기반으로 50[ms]마다 Duty_Cycle을 계속해서 조작해 줄수도 있다.
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
__weak함수인 이 함수를 main.c에서 재정의 해주면 된다.
Timer들을 동기화
신호 중, 위상차를 가져야 하는 신호들이 있다.
3상 교류 전동기의 전압 신호들이 그렇다.
각각 120도의 위상차를 갖는 U, V, W를 생성하고
각각의 전압에 DT(Dead Time)을 추가한 180도 위상차 X, Y, Z 전압을 생성하여
3상 교류 전동기를 구동할 수 있다.
즉 PWM신호들을 동기화 하여 120도와 180도의 위상차를 계속 유지하도록 해줘야한다.
또한, 6개 PWM 신호들과 모터가 회전하며 발생한 전류 변화 정보를 ADC에 feedback 받는데,
이 ADC 사이와도 동기화가 필요하다.
DTG(DT 생성) block은 Advanced Timer에만 있으니, Advanced Timer를 사용하여야 한다.
STM32 MCU의 동기화 설정
Gated Mode :
counter는 선택 입력 level 구간에 대해서만 counting
ex) trigger 입력 1이 low level을 가질때만 counting 수행
Triggerd Mode :
counter는 선택 입력에 대한 event 발생 시, counting 수행
전력제어를 포함해 다양한 분야에서 사용됨.
즉, 만들어진 시간에서 생성한 PWM 신호들 사이 일정한 위상각을 유지하도록 만들어주는 Mode