Komentator Napisano Kwiecień 3, 2018 Udostępnij Napisano Kwiecień 3, 2018 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. Link do komentarza Share on other sites More sharing options...
dejmieno Kwiecień 14, 2018 Udostępnij Kwiecień 14, 2018 "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" 🙂. 1 Link do komentarza Share on other sites More sharing options...
Treker (Damian Szymański) Kwiecień 16, 2018 Udostępnij Kwiecień 16, 2018 dejmieno, gratuluję czujności - poprawione 🙂 Dzięki! Link do komentarza Share on other sites More sharing options...
MR1979 Sierpień 12, 2019 Udostępnij Sierpień 12, 2019 (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 Edytowano Sierpień 12, 2019 przez MR1979 2 Link do komentarza Share on other sites More sharing options...
Polecacz 101 Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Produkcja i montaż PCB - wybierz sprawdzone PCBWay! • Darmowe płytki dla studentów i projektów non-profit • Tylko 5$ za 10 prototypów PCB w 24 godziny • Usługa projektowania PCB na zlecenie • Montaż PCB od 30$ + bezpłatna dostawa i szablony • Darmowe narzędzie do podglądu plików Gerber Zobacz również » Film z fabryki PCBWay
aldenham Listopad 19, 2019 Udostępnij Listopad 19, 2019 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? Link do komentarza Share on other sites More sharing options...
Elvis Listopad 24, 2019 Udostępnij Listopad 24, 2019 (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 Listopad 24, 2019 przez Elvis 1 Link do komentarza Share on other sites More sharing options...
aldenham Listopad 24, 2019 Udostępnij Listopad 24, 2019 @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). Link do komentarza Share on other sites More sharing options...
Bhoy67 Listopad 24, 2019 Udostępnij Listopad 24, 2019 @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 🙂 1 Link do komentarza Share on other sites More sharing options...
Elvis Listopad 24, 2019 Udostępnij Listopad 24, 2019 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. 1 Link do komentarza Share on other sites More sharing options...
aldenham Styczeń 3, 2020 Udostępnij Styczeń 3, 2020 @Elvis Nie gniewam się oczywiście, a przyznaję, że często muszę zaglądać do podstaw kursu. I jednocześnie odgrzewam temat po dłuższym czasie. Problem ogarnąłem, ale skorzystałem z makra __HAL_TIM_SET_COMPARE 1) Zastanawiam się jednak, czy da radę sterować jasnością diody wyłącznie sprzętowo, nie z poziomu pętli głównej programu while(1). Np. czy można zaimplementować sterowanie diodą RGB pokazane w kursie całkowicie sprzętowo, pozostawiając moc procesora wolną na inne obliczenia? Jeśli nie przez samo PWM, to może przez DMA? ------------------------------------------------------------------------------ 2) W ogóle kurs jest obszerny, ale brakuje mi trochę wyjaśnienia, co robią poszczególne makra, albo jakie dokładnie zmienne pisać w . Np. właśnie powyższe makro __HAL_TIM_SET_COMPARE jest teoretycznie opisane w instrukcji, a szczegóły są w dokumentacji projektu (HAL_Driver), jednak czytając pewne rzeczy nadal mam wątpliwości, skąd się biorą. Nie będę udawał, że wszystko jest dla mnie oczywiste, bo nie jest. Możecie polecić jakąś dodatkową lekturę, która opisze makra od podszewki? Ten kurs jest naprawdę dobry i bez tego nie wiedziałbym nic, ale fajnie byłoby wiedzieć trochę więcej. Inny przykład, HAL_TIM_PWM_ConfigChannel. W instrukcji rozpisany jest jako HAL_TIM_PWM_ConfigChannel (TIM_HandleTypeDef *htim, TIM_OC_InitTypeDef *sConfig, uint32_t Channel) a gdy już piszę kod, to wygląda on np. tak HAL_TIM_PWM_ConfigChannel(&tim4, &oc, TIM_CHANNEL_1); Czy dobrze rozumiem, że wskaźniki *htim i *sConfig zadeklarowane są w przykładach w kursu jako odpowiednio tim4 i oc? I mogę ewentualnie zamiast tim4 i oc użyć innych dowolnych nazw (oczywiście później muszę odpowiednio do nich odnosić się w kolejnych pozycjach danej struktury)? TIM_CHANNEL_1 jest rzeczywiście podany w instrukcji jako wybór kanału 1, także to jest jasne. Swoją drogą gdybym miał uczyć się programowania stm32 od zera korzystając tylko z dokumentacji, to na pewno i ja, i wielu innych śmiałków by się zniechęciło. Także jeszcze raz brawa i szacunek za ten kurs. Link do komentarza Share on other sites More sharing options...
jackg Wrzesień 16, 2020 Udostępnij Wrzesień 16, 2020 (edytowany) Czy możliwe jest , że zadanie 7.3 to 15,4 (1 kanał) i 61,6% (4 kanały). Częstotliwość sterowania 20kHz czyli 0,00005s (czas jednego taktu sterowania czyli nasze 100% ) 7,7uS to 0,0000077s stąd powyższe moje wyniki, cały czas zastanawiam się czy 100% czasu procesora to jeden takt jego zegara sterującego?? Inaczej, sprawdźmy ile czasu zajmie wykonywanie przerwań w czasie 1s: wyliczmy: (dla 1kanału) 7,7x10^(-6) x 20^3 = 0,154s x100% = 15,4% x 4(kanały) = 61,6% czasu procesora. Oczywiście dla pełnego obrazu czasu trzeba by dodać czas obsługi przerwania pełnego okresu timera , który byłby podobny i przy 4 kanałach mogło by przekroczyć 100% obciążenia procesora i zamiast 20kH realnie by pracował timer wolniej.. strzelam jakieś 18kHz. To tylko moje głośne rozumowanie ale fajnie by ktoś potwierdził mnie że nie zmierzam do nikąd 🙂 Edytowano Wrzesień 17, 2020 przez jackg Link do komentarza Share on other sites More sharing options...
Elvis Wrzesień 17, 2020 Udostępnij Wrzesień 17, 2020 Wyniki są jak najbardziej poprawne. W tym zadaniu chodziło o pokazanie jak dużo czasu może zajmować obsługa przerwań jeśli nie zadbamy o optymalizację. 1 Link do komentarza Share on other sites More sharing options...
Emerid Wrzesień 18, 2020 Udostępnij Wrzesień 18, 2020 (edytowany) Mam pytanie do zadania 7.4. Co autor miał na myśli? Czy to co zapisałem poniżej jest celem, czy może coś jeszcze innego? 1000 -> 0100 -> 0010 -> 0001 -> 1000 -> ... (1 - dioda zapalona, 0 - dioda zgaszona) Jeśli zadanie jest jak powyżej, to mam w głowie jak to zrobić przy pomocy przerwań. Jeśli okres będzie T=1s, to przy 4 przerwaniach 4*7.7us / 1 s = 0.00308 % okresu, więc całkiem ok. Jeśli miałbym zrobić powyższe zadanie przy pomocy PWM, to niestety nie potrafię sobie tego wyobrazić. PWM ma swoje wypełnienie mierzone od punktu 0, do określonego punktu (PWM1), lub alternatywnie od odkreślonego punktu do końca okresu (PWM2). Może jakaś funkcja pulse byłaby ok, ale w kursie jeszcze nie było nic takiego omawiane. Edytowano Wrzesień 18, 2020 przez Emerid Link do komentarza Share on other sites More sharing options...
Emerid Wrzesień 18, 2020 Udostępnij Wrzesień 18, 2020 TIM_OC_InitTypeDef oc; oc.OCMode = TIM_OCMODE_PWM2; oc.Pulse = 100; 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); HAL_TIM_PWM_ConfigChannel(&tim4, &oc, TIM_CHANNEL_2); HAL_TIM_PWM_ConfigChannel(&tim4, &oc, TIM_CHANNEL_3); -------------------------------------------------------- __HAL_TIM_SET_COMPARE(&tim4, TIM_CHANNEL_1, calc_pwm(b)); __HAL_TIM_SET_COMPARE(&tim4, TIM_CHANNEL_2, calc_pwm(g)); __HAL_TIM_SET_COMPARE(&tim4, TIM_CHANNEL_3, calc_pwm(r)); oc.Pulse = 100; ustawia wartość rejestru output_compare na 100 przy jego pierwotnej konfiguracji __HAL_TIM_SET_COMPARE(tim, channel, compare) jest makrem nadpisującym tylko wartość tego jednego rejestru output_compare, pozostawiając resztę konfiguracji output_compare nienaruszoną. Proszę o potwierdzenie, czy dobrze rozumiem działanie tego kodu. Link do komentarza Share on other sites More sharing options...
Harry_man Marzec 16, 2021 Udostępnij Marzec 16, 2021 Zastanawia mnie dlaczego nikt do tej pory nie zwrócił uwagi na brak bardzo istotnej konfiguracji w tym toturialu. A mianowicie - nie podłączono pinu GPIO do funkcji alternatywnej timera. Być może to kwestia domyślnego ustawienia dla wybranego pinu/timera - ale nie chce mi się już sprawdzać. W każdym bądź razie, przy strukturze inicjalizacyjnej GPIO_InitTypeDef powinno zostać uzupełnione dodatkowe pole: gpio.Alternate = GPIO_AF2_TIM3; // korzystam z TIM3 na pinach PC8 i PC9 Trochę czasu straciłem, ale ile człowiek nauczył się po drodze to jego 🙂 Link do komentarza Share on other sites More sharing options...
Pomocna odpowiedź
Bądź aktywny - zaloguj się lub utwórz konto!
Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony
Utwórz konto w ~20 sekund!
Zarejestruj nowe konto, to proste!
Zarejestruj się »Zaloguj się
Posiadasz własne konto? Użyj go!
Zaloguj się »