Skocz do zawartości
Komentator

Kurs STM32 F1 HAL - #7 - liczniki (timery) w praktyce, PWM

Pomocna odpowiedź

html_mig_img
Przed nami kolejna część kursu programowania STM32 z użyciem HAL. Tym razem poznamy podstawy sprzętowych liczników (timerów).Po krótkim wstępie teoretycznym przejdziemy do ćwiczeń praktycznych, podczas których przyda nam się umiejętność generowania sygnału PWM.

UWAGA, to tylko wstęp! Dalsza część artykułu dostępna jest na blogu.

Przeczytaj całość »

Poniżej znajdują się komentarze powiązane z tym wpisem.

Udostępnij ten post


Link to post
Share on other sites

"Okazuje się, że timer TIM4 udostępnia wyjścia PWM na pinach PB6, PB7, PB8 oraz PB8,"

Wkradł się chochlik ma być "PB8 oraz PB9" 🙂.

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

dejmieno, gratuluję czujności - poprawione 🙂 Dzięki!

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Zamiast pisać:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
 if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET)
 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
 else
 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
}

Można napisać:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
}

Zrobiłem też mały eksperyment i postanowiłem sprawdzić jaka jest największa częstotliwość wywołania przerwania TIM2_IRQHandler. Okazuje się że nie za duża (też zależy do jakich zastosowań). Przy taktowaniu 64MHz udało mi się uzyskać stabilne 90kHz (czyli przerwanie wywoływane z częstotliwością 180kHz bo na pełen okres w przebiegu prostokątnym potrzeba dwóch wywołań)

Dalsze zmniejszanie wartości Init.Prescaller lub Init.Period powodowało że układ oscylował z częstotliwością około 98KHz i wartość ta nie korespondowała z teoretyczną wartością wyliczoną dla zadanej konfiguracji Prescaller/Period.

W przypadku sprzętowego PWM można podkręcić timer nawet do 4MHz (dla 64MHz zegara). Nawet przy tak dużej częstotliwości otrzymamy w miarę regularny sygnał prostokątny. Na oscyloskopie jednak widać już dość znaczący wpływ "signal overshoot" na poziomie 0.7V.

Do większości zastosowań wystarczy jednak PWM o częstotliwości rzędu kHz.

Pozdrawiam

PWM_4MHz.png

Edytowano przez MR1979
  • Lubię! 2

Udostępnij ten post


Link to post
Share on other sites

Ok, to jak rozwiązać zadanie 7.4? Próbowałem przypisać do oc.Pulse wartość zmiennej 32 bitowej (np. pulseval1) i sterować jej wartością w pętli od 0 do 999, ale to nie działa. Umieściłem zmienną pulseval1 wewnątrz while(1), ale wartość oc.Pulse się nie aktualizuje. Czego mi brakuje? Bo przypisanie wartości (ale stałej) do zmiennej pulseval1 poza while(1) zmienia wartość oc.Pulse. Poniżej fragment kodu, który nie działa.

 TIM_OC_InitTypeDef oc;
  oc.OCMode = TIM_OCMODE_PWM1;
  oc.Pulse = pulseval1;
  oc.OCPolarity = TIM_OCPOLARITY_HIGH;
  oc.OCNPolarity = TIM_OCNPOLARITY_LOW;
  oc.OCFastMode = TIM_OCFAST_ENABLE;
  oc.OCIdleState = TIM_OCIDLESTATE_SET;
  oc.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  HAL_TIM_PWM_ConfigChannel(&tim4, &oc, TIM_CHANNEL_1);

  oc.Pulse = pulseval2;
  HAL_TIM_PWM_ConfigChannel(&tim4, &oc, TIM_CHANNEL_2);

  oc.Pulse = pulseval3;
  HAL_TIM_PWM_ConfigChannel(&tim4, &oc, TIM_CHANNEL_3);

  oc.Pulse = pulseval4;
  HAL_TIM_PWM_ConfigChannel(&tim4, &oc, TIM_CHANNEL_4);

  HAL_TIM_PWM_Start(&tim4, TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&tim4, TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&tim4, TIM_CHANNEL_3);
  HAL_TIM_PWM_Start(&tim4, TIM_CHANNEL_4);
 while (1) {

	  pulseval1 = 500;
	
 }
}

Czy da się w ogóle dynamicznie zmienić wartość oc.Pulse z pętli while(1) korzystając ze sprzętowego PWM?

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

@aldenham Wydaje mi się, że powinieneś zacząć od nadrobienia pewnych zaległości z samego języka C. Operator = przypisuje zmiennej po lewej stronie wartość, która jest wynikiem wyrażenia po prawej, czyli:

oc.Pulse = pulseval1;

Taka instrukcja oznacza odczytanie wartości zmiennej pulseval1 i wstawienie tej wartości do oc.Pulse. Jednak od tego momentu pulseval1 jest zupełnie niezależna od oc.Pulse. Można do niej przypisywać dowolne wartości, odczytywać, nawet sama zmienna może przestać istnieć - a wartość oc.Pulse pozostanie bez zmian.

Inna sprawa, że nawet zmiana wartości oc.Pulse nic by nie zmieniła - to jest tylko kolejna zmienna, dopiero jej przekazanie do funkcji HAL powoduje zmianę wypełnienia PWM.

Ale jak napisałem, proponowałbym zacząć od powtórki samego języka C, opanowanie działania zmiennych, przypisań itd. A później wrócić do kursu STM32.

Edytowano przez Elvis
  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

@Elvis

Myślę, że do tak prostych podstaw nie muszę się cofać 🙂 Rozumiem, co oznacza operator przypisania i wiem, czym różni się np. od operatora ==. Poza tym jakoś dotarłem do tego momentu w kursie rozwiązując wszystkie wcześniejsze zadania, czyli coś tam musiałem umieć zaprogramować.

Dotarłem trochę dalej w moich przemyśleniach i wykoncypowałem, żeby rzeczywiście przypisać wartość do oc.Pulse w pętli while(1) (nie wiem, dlaczego wcześniej tak nie zrobiłem). Kod wygląda teraz tak i działa częściowo:

  tim4.Instance = TIM4;
  tim4.Init.Period = 1000 - 1;
  tim4.Init.Prescaler = 8 - 1;
  tim4.Init.ClockDivision = 0;
  tim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  tim4.Init.RepetitionCounter = 0;
  tim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  HAL_TIM_PWM_Init(&tim4);

  TIM_OC_InitTypeDef oc;
  oc.OCMode = TIM_OCMODE_PWM1;

  oc.OCPolarity = TIM_OCPOLARITY_HIGH;
  oc.OCNPolarity = TIM_OCNPOLARITY_LOW;
  oc.OCFastMode = TIM_OCFAST_ENABLE;
  oc.OCIdleState = TIM_OCIDLESTATE_SET;
  oc.OCNIdleState = TIM_OCNIDLESTATE_RESET;


  oc.Pulse = pulseval2;
  HAL_TIM_PWM_ConfigChannel(&tim4, &oc, TIM_CHANNEL_2);

  oc.Pulse = pulseval3;
  HAL_TIM_PWM_ConfigChannel(&tim4, &oc, TIM_CHANNEL_3);

  oc.Pulse = pulseval4;
  HAL_TIM_PWM_ConfigChannel(&tim4, &oc, TIM_CHANNEL_4);


  HAL_TIM_PWM_Start(&tim4, TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&tim4, TIM_CHANNEL_3);
  HAL_TIM_PWM_Start(&tim4, TIM_CHANNEL_4);

  pulseval1 = 0;

 while (1) {
	 while(pulseval1 <= 999)
	 {
		 pulseval1++;
	 }
	  oc.Pulse = pulseval1;
	  HAL_TIM_PWM_ConfigChannel(&tim4, &oc, TIM_CHANNEL_1);
	  HAL_TIM_PWM_Start(&tim4, TIM_CHANNEL_1);
 }

Skupiłem się tutaj póki co tylko na kanale 1 i nie usuwałem kodu dla pozostałych kanałów. Pewnie działałoby to też bez zmiennej pulseval1, ale stwierdziłem, że bezpieczniej jest sterować jej wartością, a później przypisać jej wartość do oc.Pulse i dalej wstawić do kanału 1.

Rzeczywiście taki kod uruchamia diodę, ale nie powoduje "łagodnego narastania". Wygląda to tak, jakby oc.Pulse osiągało od razu wypełnienie graniczne z pętli, czyli tutaj wartość 999  (na razie dla testu pętla miała liczyć tylko w górę).

while(pulseval1 <= 999)

Na pewno mam gdzieś prosty błąd, którego nie widzę, a jest oczywisty dla Ciebie @Elvis, czy kogokolwiek z większym doświadczeniem, niż moje. Będę wdzięczny za podpowiedź, nie rozwiązanie (chciałbym dojść do tego w pewnym stopniu sam).

Udostępnij ten post


Link to post
Share on other sites

@aldenham, jeśli można wtrącić się z szybką odpowiedzią 🙂 Zobacz na kod, który masz w głównej pętli programu. Za pierwszym razem program "wchodzi" do tej pętli z wartością zmiennej == 0. Potem w kolejnym while'u ta zmienna ustawia się ostatecznie na 1000. Dopiero potem ten tysiąc jest wpisany do pola oc.Pulse. W kolejnych iteracjach ta pętla while już nie jest wyknywana, zmienna ma stale wartość 1000. To jest właśnie błędem, że za szybko zamknąłeś tę pętlę, która inkrementuje pulseval1 🙂

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Nie gniewaj się za moje uwagi, wcześniejszy program wygladał jakbyś nie za bardzo zrozumiał instrukcję przypisania, były już takie nieporozumienia na forum więc to nie jest niemożliwe 🙂 Teraz masz w programie źle nawiasy, w pętli while zwiększasz pulseval1, ale używasz jej dopiero jak osiagnie 1000.

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Gość
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.


×
×
  • Utwórz nowe...