środa, 6 kwietnia 2016

[4] STM32F4 - Discovery - CubeMx - PWM

Ten post chciałbym poświęcić generacji sygnału PWM, za pomocą bibliotek HAL.

Wstęp 


Dla przypomnienia wklejam poniżej opis poszczególnych liczników wraz z ich zastosowaniem oraz opisem sygnałów zegarowych.

Timer
Typ
Rozdzielczość
Dzielnik
Kanał
Max Zegar Intrerf
Max Zegar Timera
APB
1, 8
Zaawansowany
16 bit
16 bit
4
SysClk/2
SysClk
2
2, 5
Ogólny
32 bit
16 bit
4
SysClk/4
SysClk, SysClk/2
1
3, 4
Ogólny
16 bit
16 bit
4
SysClk/4
1
9
Ogólny
16 bit
16 bit
2
SysClk/2
SysClk
2
10, 11
Ogólny
16 bit
16 bit
1
SysClk/2
SysClk
2
12
Ogólny
16 bit
16 bit
2
SysClk/4
SysClk, SysClk/2
1
13, 14
Ogólny
16 bit
16 bit
1
SysClk/4
1
6, 7
Podstawowy
16 bit
16 bit
0
SysClk/4
1

CubeMx


Po wybraniu mikrokontrolera należy włączyć wybrane Timery oraz skonfigurować sygnał zegarowy.

Co do Timerów ja wybrałem TIM1, w który ustawiłem tylko źródło sygnału (Clock Source) jako zegar wewnętrzny (Internal Clock). TIM9 skonfigurowany natomiast tak aby generował sygnał PWM na kanale 1 oraz 2. Automatycznie sygnały zostają przypisane do pinów PE5 (CH1) oraz PE6 (CH2)

Rys. 1. Ustawienie TIM9

Jeśli potrzebny jest PWM na czterech kanałów z jednego licznika, wtedy można wykorzystać TIM4. W programie głównym wygenerowane sygnały zostaną przypisane do pinu PD12 z CH1, oraz PD14 z CH2.

PA0, ten do którego jest podłączony przycisk, dostał przerwanie EXTI0 (GPIO_EXTI0). Jeśli wykorzystuje się widok płytki ewaluacyjnej w CubeMx, to ona ma już tą opcje wybraną. 

Rys. 2, Konfiguracja wyprowadzenia PA0

Zegar został skonfigurowany, tak aby taktowanie mikrokontrolera było maksymalne dopuszczalne czyli 168 MHz

Kolejnym krokiem jest wybranie pinów wyjściowych, na które będzie przekazywany sygnał z licznika. Ja wybrałem piny do których są podłączone diody wbudowane czyli od PD12 do PD15. Tak jak w przypadku przycisku, mikrokontroler wybrany w widoku płytki Discovery ma już te piny odpowiednio skonfigurowane.
Rys. 3. Konfiguracja pinów z podłączonymi diodami

W następnym kroku należy odpowiednio ustawić timery. W tym celu trzeba przełączyć się na zakładkę Configuration.

Rys. 4. Ustawienie TIM1

Rys. 5. Ustawienie TIM9

Programowanie


Teraz po wygenerowaniu kodu należy dopisać pozostałą część programu.

W pierwszej kolejności należy dopisać funkcje obsługujące odpowiednie wyprowadzenia. Pierwsza część do obsługi sygnału PWM na pinie PD14 wygląda następująco.

  1. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
  2. {
  3.     if(keyclick==0)
  4.     {
  5.         TIM9->CCR2 = 65535/100*1;
  6.         keyclick++;
  7.     }
  8.     else if(keyclick==1)
  9.     {
  10.         TIM9->CCR2 = 65535/100*10;
  11.         keyclick++;
  12.     }
  13.     else if(keyclick==2)
  14.     {
  15.         TIM9->CCR2 = 65535/100*50;
  16.         keyclick++;
  17.     }
  18.     else if(keyclick==3)
  19.     {
  20.         TIM9->CCR2 = 65535/100*99;
  21.         keyclick++;
  22.     }
  23.     else if(keyclick==4)
  24.     {
  25.         keyclick=0;
  26.         TIM9->CCR2 = 0;
  27.     }
  28. }

Jego zadanie jest proste w momencie kliknięcia przycisku podłączonego do pinu PA0 następuje zwiększenie zmiennej keyclick, po czym następuje wejście do odpowiedniej części instrukcji warunkowej. W tym przypadku nie ma potrzeby stosowania żadnego zabezpieczenie programowego do ochrony przed drganiem styków, ponieważ został on odpowiednio zabezpieczony sprzętowo przez producenta.

W poszczególnych sekcjach zmieniana jest wartość wpisana do rejestru CCR2, w której przechowywana jest wartość jaka ma zostać wyprowadzona jako sygnał PWM.

Druga funkcja ustawia wartości na wyprowadzeniu PD12. Tak aby wartość PWM była dynamicznie zmieniana.

  1. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  2. {
  3.     if(htim->Instance == htim1.Instance)
  4.     {
  5.         TIM9->CCR1 = PWMval;
  6.         if(PWMval == 0xFFFF)
  7.         {
  8.             PWMval=0;
  9.         }
  10.         else if(PWMval<0xFFFF)
  11.         {
  12.             PWMval++;
  13.         }
  14.     }
  15. }

Linijka if(htim->Instance == htim1.Instance) sprawdza jakie źródło przerwania zostało wywołane.

Poniżej wklejam części funkcji main, które zostały dopisane, oraz części funkcji ustawienia timerów.

  1. /* USER CODE BEGIN 0 */
  2. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
  3. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
  4. uint16_t PWMval=0;
  5. uint8_t keyclick=0;
  6.  
  7. /* USER CODE END 0 */
  8.  
  9. int main(void)
  10. {
  11.  
  12.   /* USER CODE BEGIN 1 */
  13.  
  14.   /* USER CODE END 1 */
  15.  
  16.   /* MCU Configuration----------------------------------------------------------*/
  17.  
  18.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  19.   HAL_Init();
  20.  
  21.   /* Configure the system clock */
  22.   SystemClock_Config();
  23.  
  24.   /* Initialize all configured peripherals */
  25.   MX_GPIO_Init();
  26.   MX_TIM1_Init();
  27.   MX_TIM9_Init();
  28.  
  29.   /* USER CODE BEGIN 2 */
  30.    
  31.     //Wlaczenie timerow
  32.     HAL_TIM_Base_Start_IT(&htim1);
  33.     HAL_TIM_Base_Start_IT(&htim9);
  34.    
  35.     //Wlaczenie PWM
  36.     HAL_TIM_PWM_Start(&htim9, TIM_CHANNEL_1);
  37.     HAL_TIM_PWM_Start(&htim9, TIM_CHANNEL_2);
  38.    
  39.   /* USER CODE END 2 */
  40.  
  41.   /* Infinite loop */
  42.   /* USER CODE BEGIN WHILE */
  43.   while (1)
  44.   {
  45.   /* USER CODE END WHILE */
  46.  
  47.   /* USER CODE BEGIN 3 */
  48.    
  49.         HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_5));
  50.         HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_6));
  51.   }
  52.   /* USER CODE END 3 */
  53.  
  54. }
  55.  
  56. /* TIM1 init function */
  57. void MX_TIM1_Init(void)
  58. {
  59.  
  60.   TIM_ClockConfigTypeDef sClockSourceConfig;
  61.   TIM_MasterConfigTypeDef sMasterConfig;
  62.  
  63.   htim1.Instance = TIM1;
  64.   htim1.Init.Prescaler = 0;
  65.   htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  66.   htim1.Init.Period = 8399;
  67.   htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  68.   htim1.Init.RepetitionCounter = 0;
  69.   HAL_TIM_Base_Init(&htim1);
  70.  
  71.   sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  72.   HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig);
  73.  
  74.   sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  75.   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  76.   HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig);
  77.  
  78. }
  79.  
  80. /* TIM9 init function */
  81. void MX_TIM9_Init(void)
  82. {
  83.  
  84.   TIM_OC_InitTypeDef sConfigOC;
  85.  
  86.   htim9.Instance = TIM9;
  87.   htim9.Init.Prescaler = 0;
  88.   htim9.Init.CounterMode = TIM_COUNTERMODE_UP;
  89.   htim9.Init.Period = 65535;
  90.   htim9.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  91.   HAL_TIM_PWM_Init(&htim9);
  92.  
  93.   sConfigOC.OCMode = TIM_OCMODE_PWM1;
  94.   sConfigOC.Pulse = 100;
  95.   sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  96.   sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  97.   HAL_TIM_PWM_ConfigChannel(&htim9, &sConfigOC, TIM_CHANNEL_1);
  98.  
  99.   sConfigOC.Pulse = 30000;
  100.   HAL_TIM_PWM_ConfigChannel(&htim9, &sConfigOC, TIM_CHANNEL_2);
  101.  
  102.   HAL_TIM_MspPostInit(&htim9);
  103.  
  104. }
  105.  
  106. /* USER CODE BEGIN 4 */
  107.  
  108. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
  109. {
  110.     if(keyclick==0)
  111.     {
  112.         TIM9->CCR2 = 65535/100*1;
  113.         keyclick++;
  114.     }
  115.     else if(keyclick==1)
  116.     {
  117.         TIM9->CCR2 = 65535/100*10;
  118.         keyclick++;
  119.     }
  120.     else if(keyclick==2)
  121.     {
  122.         TIM9->CCR2 = 65535/100*50;
  123.         keyclick++;
  124.     }
  125.     else if(keyclick==3)
  126.     {
  127.         TIM9->CCR2 = 65535/100*99;
  128.         keyclick++;
  129.     }
  130.     else if(keyclick==4)
  131.     {
  132.         keyclick=0;
  133.         TIM9->CCR2 = 0;
  134.     }
  135. }
  136.  
  137. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  138. {
  139.     if(htim->Instance == htim1.Instance)
  140.     {
  141.         TIM9->CCR1 = PWMval;
  142.         if(PWMval == 0xFFFF)
  143.         {
  144.             PWMval=0;
  145.         }
  146.         else if(PWMval<0xFFFF)
  147.         {
  148.             PWMval++;
  149.         }
  150.     }
  151. }
  152.  
  153. /* USER CODE END 4 */