czwartek, 19 listopada 2015

[6a] STM32 M3 - Nucleo - F103RB - PWM

Ten post jest kontynuacją Timerów. Opiszę w nim sposób generacji sygnału PWM.

Wstęp


Sygnał PWM jest najczęściej wykorzystywany do sterowania silnikami elektrycznymi prądu stałego jak i zmiennego oprócz tego wykorzystuje się je do sterowania temperaturą czy natężeniem światła.

Ogólnie największe zastosowanie jest w układach które potrzebują dużej wartości prądu do zasilania bądź wymagana jest duża moc. 


Modulacja szerokością impulsów ma dużo zalet, niestety są też wady. Wśród nich można wyróżnić następujące: 
  • może wystąpić brak liniowego działania układu, dotyczy to zwłaszcza małej wartości wypełnienia.
  • silniki sterowane przez PWM mogą być znacznie głośniejsze od tych sterowanych normalnie.

Programowanie


W celu zaprogramowania PWM należałoby ustawić wartość wypełnienia sygnału w polu TIM_Pulse. Maksymalna wartość należy wpisać w pole TIM_Period. W celu zmiany wypełnienia można się posłużyć funkcjami TIM_SetCompare1() do TIM_SetCompare4().

Program 1


W tym programie zaprezentuję generację sygnałów PWM z jednego licznika na czterech kanałach. Częstotliwość taktowania ustawiona jest na 128kHz (64MHz/500). Dzięki temu możliwa jest obserwacja różnego koloru świecenia diod. Kiedy częstotliwość będzie mniejsza, czyli zostanie ustawiony większy dzielnik (prescaler), wtedy diody będą mrugać z określoną wartością PWM.

Dla dzielnika wynoszącego 1000 diody świecą światłem ciągłym, jednak bardzo łatwo można zaobserwować ich mruganie. Im dzielnik większy tym mniejsza częstotliwość, a co za tym idzie dłuższy czas wygenerowania pełnego sygnału.

Oczywiście należy także pamiętać, że większa częstotliwość powoduje występowania większych zaburzeń promieniowania elektromagnetycznego.

#include "stm32f10x.h"
 
void GPIOInit(void);
void TIMInit(void);
 
int main(void)
{
 GPIOInit();
 TIMInit();
 
 while (1) {
 }
}
 
void TIMInit(void)
{
 TIM_TimeBaseInitTypeDef TIMInit;
 TIM_OCInitTypeDef  PWMInit;
 
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
 
 TIM_TimeBaseStructInit(&TIMInit);
 TIMInit.TIM_CounterMode = TIM_CounterMode_Up;
 TIMInit.TIM_Prescaler = 500 - 1;
 TIMInit.TIM_Period = 2000 - 1;
 TIMInit.TIM_ClockDivision = TIM_CKD_DIV1;
 TIM_TimeBaseInit(TIM3, &TIMInit);
 
 //Konfiguracja PWM
 
 //Kanal 1, pulse 500 wypełnienie 25%
 //wybranie trybu działania
 PWMInit.TIM_OCMode = TIM_OCMode_PWM1;
 PWMInit.TIM_OutputState = TIM_OutputState_Enable;
 PWMInit.TIM_OCPolarity = TIM_OCPolarity_High;
 PWMInit.TIM_Pulse = 500;
 TIM_OC1Init(TIM3, &PWMInit);
 
 //Kanal 2, pulse 100 wypełnie 50%
 PWMInit.TIM_OCMode = TIM_OCMode_PWM1;
 PWMInit.TIM_OutputState = TIM_OutputState_Enable;
 PWMInit.TIM_OCPolarity = TIM_OCPolarity_High;
 PWMInit.TIM_Pulse = 1000;
 TIM_OC2Init(TIM3, &PWMInit);
 
 //Kanal 3, pulse 1500, wypełnienie 75%
 PWMInit.TIM_OCMode = TIM_OCMode_PWM1;
 PWMInit.TIM_OutputState = TIM_OutputState_Enable;
 PWMInit.TIM_OCPolarity = TIM_OCPolarity_High;
 PWMInit.TIM_Pulse = 1500;
 TIM_OC3Init(TIM3, &PWMInit);
 
 //Kanal 4, pulse 2000, wypełnienie 100%
 PWMInit.TIM_OCMode = TIM_OCMode_PWM1;
 PWMInit.TIM_OutputState = TIM_OutputState_Enable;
 PWMInit.TIM_OCPolarity = TIM_OCPolarity_High;
 PWMInit.TIM_Pulse = 2000;
 TIM_OC4Init(TIM3, &PWMInit);
 
 //Wlaczenie buforowania dla OC4
 TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);
 
 //Wlaczenie buforowania dla okresu licznika
 TIM_ARRPreloadConfig(TIM3, ENABLE);
 
 //Wlaczenie timera
 TIM_Cmd(TIM3, ENABLE);
}
 
void GPIOInit(void)
{
 GPIO_InitTypeDef GPIOInit;
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
 
 GPIOInit.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
 GPIOInit.GPIO_Speed = GPIO_Speed_50MHz;
 GPIOInit.GPIO_Mode = GPIO_Mode_AF_PP;
 GPIO_Init(GPIOA, &GPIOInit);
 
 GPIOInit.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
 GPIOInit.GPIO_Speed = GPIO_Speed_50MHz;
 GPIOInit.GPIO_Mode = GPIO_Mode_AF_PP;
 GPIO_Init(GPIOB, &GPIOInit);
}

Z przedstawionego programu można wywnioskować, że każdy z kanałów jednego timera ma jeden okres. Regulowane jest natomiast wypełnienie.

Możliwe jest także podłączenie przycisku z ustawionym przerwaniem zewnętrznym. Dzięki temu w obsłudze przerwania istnieje możliwość dynamicznej zmiany sygnału PWM przez użytkownika. Podobną opcje można także uzyskać poprzez wykorzystanie pętli np. for, która będzie zmieniać wartość wypełnienia.

Taką regulacja może być przydatna np. dla wiatraka komputerowego, gdzie można dostosować prędkość obrotową poprzez zadanie sygnału PWM dla własnych potrzeb.