poniedziałek, 23 stycznia 2017

[9] STM32F7 - Generacja PWM

Ten post chciałbym poświęcić na opis sposobu generacji sygnału PWM za pomocą układu STM32F746, który zamontowano na płytce Discovery.

Poniżej przejdę przez wszystkie niezbędne funkcje do jego wykonania dla włączenie TIM 2 oraz 3. 

PWM kanał 1 z TIM3 generowany jest na PB4. 
PWM kanał 1 z TIM2 generowany jest na PA15.


Procedurę inicjalizacji należy rozpocząć od włączenia pinów dla pierwszego kanału. 

  1. #define TIM2_PWM_PORT GPIOA
  2. #define TIM3_PWM_PIN GPIO_PIN_15
  3.  
  4. #define TIM3_PWM_PORT GPIOB
  5. #define TIM3_PWM_PIN GPIO_PIN_4
  6.  
  7. void PWM_TIM2_InitGPIO(void)
  8. {
  9.   GPIO_InitTypeDef GPIO_InitStruct;
  10.  
  11.   __GPIOA_CLK_ENABLE();
  12.  
  13.   GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  14.   GPIO_InitStruct.Pull = GPIO_PULLUP;
  15.   GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
  16.   GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
  17.   GPIO_InitStruct.Pin = TIM3_PWM_PIN;
  18.   HAL_GPIO_Init(TIM2_PWM_PORT, &GPIO_InitStruct);
  19. }
  20.  
  21. void PWM_TIM3_InitGPIO(void)
  22. {
  23.   GPIO_InitTypeDef GPIO_InitStruct;
  24.  
  25.   __GPIOB_CLK_ENABLE();
  26.  
  27.   GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  28.   GPIO_InitStruct.Pull = GPIO_PULLUP;
  29.   GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
  30.   GPIO_InitStruct.Alternate = GPIO_AF2_TIM2;
  31.   GPIO_InitStruct.Pin = TIM3_PWM_PIN;
  32.   HAL_GPIO_Init(TIM3_PWM_PORT, &GPIO_InitStruct);
  33. }

Jak widać powyżej w pierwszej kolejności należy włączyć sygnał zegarowy, po czym wprowadzane są ustawienia. Dla timerów należy wykorzystać funkcje alternatywne.

Poniżej procedura uruchomienia TIM2 na kanale pierwszym. 

  1. #define TIM2_PRESCALER 249
  2. #define TIM2_PERIOD 255
  3. #defube TIM2_START_PULSE 100
  4. #define TIM2_POLARITY_PWM  TIM_OCNPOLARITY_HIGH //TIM_OCNPOLARITY_LOW
  5.  
  6. void PWM_TIM2_InitPWM(void)
  7. {
  8.   TIM_HandleTypeDef TimHandle;
  9.   TIM_OC_InitTypeDef ChannelConfig;
  10.  
  11.   //Clock enable
  12.   __HAL_RCC_TIM2_CLK_ENABLE();
  13.  
  14.   //Timer init
  15.   TimHandle.Instance = TIM2;
  16.   TimHandle.Init.Prescaler         = TIM2_PRESCALER ;   //Dzielnik
  17.   TimHandle.Init.Period            = TIM2_PERIOD ;      //Maksymalny okres
  18.   TimHandle.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;
  19.   TimHandle.Init.CounterMode       = TIM_COUNTERMODE_UP;
  20.   TimHandle.Init.RepetitionCounter = 0;
  21.   HAL_TIM_PWM_Init(&TimHandle);
  22.  
  23.   //Kanal 1
  24.   ChannelConfig.OCMode       = TIM_OCMODE_PWM1;
  25.   ChannelConfig.OCPolarity   = TIM_OCPOLARITY_HIGH;
  26.   ChannelConfig.OCFastMode   = TIM_OCFAST_DISABLE;
  27.   ChannelConfig.OCNPolarity  = TIM2_POLARITY_PWM;
  28.   ChannelConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  29.   ChannelConfig.OCIdleState  = TIM_OCIDLESTATE_RESET;
  30.   ChannelConfig.Pulse = TIM2_START_PULSE;
  31.   HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TIM_CHANNEL_1);
  32.   HAL_TIM_PWM_Start(&TimHandle, TIM_CHANNEL_1);
  33. }

Uruchomienie PWM z TIM3 na kanale 1:

  1. #define TIM3_PRESCALER 249
  2. #define TIM3_PERIOD 255
  3. #defube TIM3_START_PULSE 100
  4. #define TIM3_POLARITY_PWM  TIM_OCNPOLARITY_HIGH //TIM_OCNPOLARITY_LOW
  5. #define TIM3_Clock_On() __HAL_RCC_TIM3_CLK_ENABLE()
  6.  
  7. void PWM_TIM3_InitTIM(void)
  8. {
  9.   TIM_HandleTypeDef TimHandle;
  10.   TIM_OC_InitTypeDef ChannelConfig;
  11.  
  12.   //Clock enable
  13.   TIM3_Clock_On();
  14.  
  15.   //Timer init
  16.   TimHandle.Instance = TIM3;
  17.   TimHandle.Init.Prescaler         = TIM3_PRESCALER ;   //Dzielnik
  18.   TimHandle.Init.Period            = TIM3_PERIOD ;      //Maksymalny okres
  19.   TimHandle.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;
  20.   TimHandle.Init.CounterMode       = TIM_COUNTERMODE_UP;
  21.   TimHandle.Init.RepetitionCounter = 0;
  22.   HAL_TIM_PWM_Init(&TimHandle);
  23.  
  24.   //Kanal 1
  25.   ChannelConfig.OCMode       = TIM_OCMODE_PWM1;
  26.   ChannelConfig.OCPolarity   = TIM_OCPOLARITY_HIGH;
  27.   ChannelConfig.OCFastMode   = TIM_OCFAST_DISABLE;
  28.   ChannelConfig.OCNPolarity  = TIM2_POLARITY_PWM;
  29.   ChannelConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  30.   ChannelConfig.OCIdleState  = TIM_OCIDLESTATE_RESET;
  31.   ChannelConfig.Pulse = TIM3_START_PULSE;
  32.   HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TIM_CHANNEL_1);
  33.   HAL_TIM_PWM_Start(&TimHandle, TIM_CHANNEL_1);
  34. }

Teraz przykładowa funkcja włączająca wszystkie kanały dla TIM3:

  1. #define TIM3_PRESCALER 249
  2. #define TIM3_PERIOD 255
  3. #define TIM3_POLARITY_PWM  TIM_OCNPOLARITY_HIGH //TIM_OCNPOLARITY_LOW
  4. #define TIM3_Clock_On() __HAL_RCC_TIM3_CLK_ENABLE()
  5. #defube TIM3_START_PULSE_CH1 50
  6. #defube TIM3_START_PULSE_CH2 100
  7. #defube TIM3_START_PULSE_CH3 150
  8. #defube TIM3_START_PULSE_CH4 200
  9.  
  10. void PWM_TIM3_InitTIM(uint8_t channel)
  11. {
  12.   TIM_HandleTypeDef TimHandle;
  13.   TIM_OC_InitTypeDef ChannelConfig;
  14.  
  15.   //Clock enable
  16.   TIM3_Clock_On();
  17.  
  18.   //Timer init
  19.   TimHandle.Instance = TIM3;
  20.   TimHandle.Init.Prescaler         = TIM3_PRESCALER ;   //Dzielnik
  21.   TimHandle.Init.Period            = TIM3_PERIOD ;      //Maksymalny okres
  22.   TimHandle.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;
  23.   TimHandle.Init.CounterMode       = TIM_COUNTERMODE_UP;
  24.   TimHandle.Init.RepetitionCounter = 0;
  25.   HAL_TIM_PWM_Init(&TimHandle);
  26.  
  27.   //Kanal 1
  28.   ChannelConfig.OCMode       = TIM_OCMODE_PWM1;
  29.   ChannelConfig.OCPolarity   = TIM_OCPOLARITY_HIGH;
  30.   ChannelConfig.OCFastMode   = TIM_OCFAST_DISABLE;
  31.   ChannelConfig.OCNPolarity  = TIM2_POLARITY_PWM;
  32.   ChannelConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  33.   ChannelConfig.OCIdleState  = TIM_OCIDLESTATE_RESET;
  34.   ChannelConfig.Pulse = TIM3_START_PULSE_CH1;
  35.   HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TIM_CHANNEL_1);
  36.   HAL_TIM_PWM_Start(&TimHandle, TIM_CHANNEL_1);
  37.  
  38.   //Kanal 2
  39.   ChannelConfig.OCMode       = TIM_OCMODE_PWM1;
  40.   ChannelConfig.OCPolarity   = TIM_OCPOLARITY_HIGH;
  41.   ChannelConfig.OCFastMode   = TIM_OCFAST_DISABLE;
  42.   ChannelConfig.OCNPolarity  = TIM2_POLARITY_PWM;
  43.   ChannelConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  44.   ChannelConfig.OCIdleState  = TIM_OCIDLESTATE_RESET;
  45.   ChannelConfig.Pulse = TIM3_START_PULSE_CH2;
  46.   HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TIM_CHANNEL_2);
  47.   HAL_TIM_PWM_Start(&TimHandle, TIM_CHANNEL_2);
  48.  
  49.   //Kanal 3
  50.   ChannelConfig.OCMode       = TIM_OCMODE_PWM1;
  51.   ChannelConfig.OCPolarity   = TIM_OCPOLARITY_HIGH;
  52.   ChannelConfig.OCFastMode   = TIM_OCFAST_DISABLE;
  53.   ChannelConfig.OCNPolarity  = TIM2_POLARITY_PWM;
  54.   ChannelConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  55.   ChannelConfig.OCIdleState  = TIM_OCIDLESTATE_RESET;
  56.   ChannelConfig.Pulse = TIM3_START_PULSE_CH3;
  57.   HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TIM_CHANNEL_3);
  58.   HAL_TIM_PWM_Start(&TimHandle, TIM_CHANNEL_3);
  59.  
  60.   //Kanal 4
  61.   ChannelConfig.OCMode       = TIM_OCMODE_PWM1;
  62.   ChannelConfig.OCPolarity   = TIM_OCPOLARITY_HIGH;
  63.   ChannelConfig.OCFastMode   = TIM_OCFAST_DISABLE;
  64.   ChannelConfig.OCNPolarity  = TIM2_POLARITY_PWM;
  65.   ChannelConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  66.   ChannelConfig.OCIdleState  = TIM_OCIDLESTATE_RESET;
  67.   ChannelConfig.Pulse = TIM3_START_PULSE_CH4;
  68.   HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TIM_CHANNEL_4);
  69.   HAL_TIM_PWM_Start(&TimHandle, TIM_CHANNEL_4);
  70. }

Generowanie sygnału PWM na kanale 1:

  1. void PWM_TIM2_SetPWM_Channel_1(uint16_t wartosc)
  2. {
  3.     if(wartosc>TIM2_PERIOD ) { wartosc=TIM2_PERIOD; }
  4.     TIM2->CCR1 = wartosc;
  5. }
  6.  
  7. void PWM_TIM3_SetPWM_Channel_1(uint16_t wartosc)
  8. {
  9.     if(wartosc>TIM3_PERIOD ) { wartosc=TIM3_PERIOD; }
  10.     TIM3->CCR1 = wartosc;
  11. }

Generowanie sygnału PWM na zadanym kanale:

  1. void PWM_TIM2_SetPWM_Channel_1(uint16_t wartosc, uint8_t kanal)
  2. {
  3.     if(wartosc>TIM2_PERIOD ) { wartosc=TIM2_PERIOD; }
  4.     if(1 == kanal){ TIM2->CCR1 = wartosc; }
  5.     else if(2 == kanal){ TIM2->CCR2 = wartosc; }
  6.     else if(3 == kanal){ TIM2->CCR3 = wartosc; }
  7.     else if(4 == kanal){ TIM2->CCR4 = wartosc; }
  8.     else
  9.     {
  10.         TIM2->CCR1 = 0;
  11.         TIM2->CCR2 = 0;
  12.         TIM2->CCR3 = 0;
  13.         TIM2->CCR4 = 0;
  14.     }
  15. }
  16.  
  17. void PWM_TIM3_SetPWM_Channel_1(uint16_t wartosc, uint8_t kanal)
  18. {
  19.     if(wartosc>TIM2_PERIOD ) { wartosc=TIM3_PERIOD; }

  20.     if(1 == kanal){ TIM3->CCR1 = wartosc; }
  21.     else if(2 == kanal){ TIM3->CCR2 = wartosc; }
  22.     else if(3 == kanal){ TIM3->CCR3 = wartosc; }
  23.     else if(4 == kanal){ TIM3->CCR4 = wartosc; }
  24.     else
  25.     {
  26.         TIM3->CCR1 = 0;
  27.         TIM3->CCR2 = 0;
  28.         TIM3->CCR3 = 0;
  29.         TIM3->CCR4 = 0;
  30.     }
  31. }

Do funkcji podawany jest numer kanału, w przypadku wprowadzenia numeru z poza zakresu wszystkie sygnały na kanałach zostają wyzerowane.

Generowanie sygnału PWM przez określony czas na linii, można wykorzystać np. do buzzera.

  1. void Set_PWM_For_Time(uint8_t timer_number, uint8_t channel_number, uint8_t pwm_to_write, uint16_t time)
  2. {
  3.     uint8_t one_loop = 0;
  4.  
  5.     for(one_loop = 0; one_loop < 2; one_loop++)
  6.     {
  7.         if(2 == timer_number)  
  8.         {
  9.             if(1 == channel_number) {  PWM_TIM2_SetPWM_Channel_1(pwm_to_write); }
  10.             if(2 == channel_number) {  PWM_TIM3_SetPWM_Channel_2(pwm_to_write); }
  11.             if(3 == channel_number) {  PWM_TIM3_SetPWM_Channel_3(pwm_to_write); }
  12.             if(4 == channel_number) {  PWM_TIM3_SetPWM_Channel_4(pwm_to_write); }
  13.         }
  14.         else if(3 == timer_number) 
  15.         {
  16.             if(1 == channel_number) {  PWM_TIM3_SetPWM_Channel_1(pwm_to_write); }
  17.             if(2 == channel_number) {  PWM_TIM3_SetPWM_Channel_2(pwm_to_write); }
  18.             if(3 == channel_number) {  PWM_TIM3_SetPWM_Channel_3(pwm_to_write); }
  19.             if(4 == channel_number) {  PWM_TIM3_SetPWM_Channel_4(pwm_to_write); }
  20.         }
  21.  
  22.         if(0 == one_loop) {
  23.             HAL_Delay(time);
  24.             pwm_to_write = 0;
  25.         }
  26.     }
  27. }

Funckja dwukrotnie wchodzi w pętle loop, przy pierwszym przejściu generuje zadany sygnał, po czym odczekuje zadany czas i zeruje wartość podaną do funkcji pwm_to_write. Dzięki temu w kolejnym przejściu zostaje podany sygnał zerowy na wybrany kanał. Drugi raz nie następuje odczekanie czasu oraz zmienienie wartości PWM. Wobec tego pętla for wykona się tylko dwa razy.