Skocz do zawartości
Komentator

Kurs STM32 F4 - #8 - Zaawansowane funkcje liczników

Pomocna odpowiedź

Witam,

W jednym miejscu coś mi się nie zgadza, kiedy jest fragment artykułu, gdzie jest generowanie sygnału PWM z wykorzystaniem DMA użyta jest funkcja:

HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_3, &Duty, 1);

Gdzie zmienna Duty jest typu uint16_t, natomiast funkcja HAL_TIM_PWM_START_DMA wymaga wskaźnika na uint32_t :

HAL_StatusTypeDef HAL_TIM_PWM_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData, uint16_t Length);

Zatem czy jeśli chcemy żeby zmienna Duty pozostała uint16_t, nie powinno się zrobić następującego rzutowania :

HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_3,(uint16_t*) &Duty, 1);

?

Udostępnij ten post


Link to post
Share on other sites

@PiotrPierPeter witam na forum 🙂

@Elvis, doradzisz w tej kwestii? Wołam Cie, bo wiem, że niedawno analizowałeś dokładniej ten kurs 😉

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Jestem w tej chwili w pracy i nie mam dostępu do CubeMX - mogę więcej napisać wieczorem, teraz napiszę tak "z głowy", bez kodu i testowania.

W programie widzimy że Duty ma typ uint16_t, natomiast HAL_TIM_PWM_Start oczekuje wskaźnika do uint32_t. W rzeczywistości jest to błąd, albo chociaż niedoskonałość biblioteki HAL - w końcu za pomocą DMA można przesyłać różne typy danych, nie tylko uint32_t. Poprawnie zaprojektowane biblioteki (przykładowo zgodne z POSIX) używają w takim przypadku typu void*. Ale ktoś, kto projektował Cube HAL chciał być bardziej poprawny i użył uint32_t...

Typ wskaźników w rzeczywistości nie ma większego znaczenia - stąd w C częste użycie void* chociażby w funkcjach systemowych - read, write itd.

Nie testowałem, ale poprawnie program powinien wyglądać raczej tak:

HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_3, (uin32_t*) &Duty, 1);

W tej wersji po prostu informujemy kompilator że wiemy co robimy i specjalnie zmieniamy typ wskaźnika - co kompilator powinien uszanować i wyłączyć ostrzeżenie.

Natomiast jak napisałem na początku - obie wersje działają tak samo, a ze zmienną Duty nic złego się nie dzieje.

  • Lubię! 1
  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites

@PiotrPierPeter super, że problem rozwiązany. Powodzenia w dalszych eksperymentach!

PS Pamiętaj, że pomocne posty można odpowiednio oceniać (ikona serca w prawym dolnym rogu wiadomości). Dzięki temu można podziękować bezpośrednio autorowi i (co czasami ważniejsze) wyróżnić daną odpowiedź, aby inni widzieli od razu, że jest ona rozwiązaniem opisywanego problemu. Zachęcam do korzystania z tej opcji 😉

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites
Dnia 5.02.2019 o 00:46, misklap napisał:

Trochę pogooglowałem i znalazłem rozwiązanie.

Na wstępie mała obserwacja: z opisu wynika, że w main() powinny być linie: 


HAL_TIM_Base_Start(&htim4);
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_3);

Podczas gdy wystarcza tylko ta druga, żeby działało ze starym firmwarem.

Żeby zadziałało zarówno ze starym i nowym firmwarem, należy:

a) zamienić nagłówek naszej funkcji z:


void HAL_SYSTICK_Callback(void)

na


void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)


b) w main() zamienić wywołanie


HAL_TIM_Base_Start(&htim4);

na


HAL_TIM_Base_Start_IT(&htim4);

c) w CubeMX ustawić:
TIM4|NVIC Settings TIM4 global interrupt|Enabled = true

i wygenerować kod. 

(W przypadku starego firmware'u, spowoduje to wygenerowanie w deklaracjach, linii:


static void SystemClock_Config(void);

oraz jej nadmiarowe (drugie) wywołanie w main():


SystemClock_Config();

które należy usunąć).

 

Wydaje mi się, że kod działający w starym i nowym firmwarze jest bardziej zgodny z (przynajmniej moimi 🙂) oczekiwaniami, a ten stary działał na skutek jakichś błędów w CubeMX


 

@misklap dzięki za informacje! Od wczoraj próbowałem rozwiązać ten problem.

Z pomocą przychodzi tutaj Debugger. Po ustawieniu breakpointa w funkcji void HAL_SYSTICK_Callback(void) widać że program nigdy nie używa tego przerwania. Dlaczego? Ponieważ STM32CubeMX v5.x nie umieszcza funkcji obsługi tego przerwania HAL_SYSTICK_IRQHandler(); wewnątrz void SysTick_Handler(void) (w pliku stm32f4xx_it.c). Po dopisaniu brakującej linijki wszystko działa tak jak opisano w kursie.

PS. Wybaczcie jeżeli pogwałciłem jakiekolwiek normy w trakcie pisania tego (pierwszego) postu 😉

 

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

@gekon83 witam na forum i dzięki za aktualizację z rozwiązaniem problemu 🙂 Niestety w tym kursie często pojawiają się jakieś drobne problemy wynikające z aktualizacji CubeMX, które bywają mniej lub bardziej "dopracowane". Na szczęście udaje nam się wspólnymi siłami odnajdywać rozwiązania tych zagadek 😉

Udostępnij ten post


Link to post
Share on other sites

Witam forumowiczów 🙂

Próbowałem właśnie wykonać część pierwszą (tę najprostszą, bez DMA), jednakże po zaprogramowaniu kod nie wykazuje żadnego działania. Ustawiłem zegar na maksymalną częstotliwość, podłączyłem czerwoną diodę na kanał 3 timera 4 oraz wpisałem kod w odpowiednie miejsca, lecz mimo nic się nie dzieje. Korzystam z najnowszej wersji Eclipse oraz CubeMX. Czy ma ktoś może pomysł na rozwiązanie tego problemu ? Próbowałem stosować również  rozwiązania z komentarzy i nic nie pomogło.

Z góry dzięki 😉

Udostępnij ten post


Link to post
Share on other sites

@laxin98 witam na forum!

Pokaż może jakieś zrzuty ekranu i/lub swój program, bo bez tego ciężko jakkolwiek ruszyć z pomocą 😉

Udostępnij ten post


Link to post
Share on other sites

Witam, 

Oczywiście wybacz, dodaje zdjęcia z CubeMX zegarów, bo nie jestem ich pewien. Dodaję też kod maina. Wydaje mi się, że zrobiłem wszystko zgodnie z instrukcją. Tak jak mówiłem są tam zmiany, które jeden kolega opisał jakieś dwa komentarze wyżej. Płytka została zakupiona na waszej stronie i do niej był dodany ten kurs, więc sprzęt powinien być odpowiedni.

111.jpg

main.rar

Udostępnij ten post


Link to post
Share on other sites
Dnia 15.10.2019 o 22:01, RFM napisał:

Praktycznie nigdy nie korzystam z  Callback jakiejkolwiek funkcji. Zawsze swój kod wstawiałem w


  /* USER CODE BEGIN SysTick_IRQn 0 */
 
  /* USER CODE END SysTick_IRQn 0 */

więc problemu nie mam. W Callback  trzeba sprawdzać źródło przerwania (np który uart wywołał przerwanie, który pin EXTI) a wstawiając swój kod w handler nie trzeba nic sprawdzać lub mniej niż w przypadku Callback.

Też mnie to zastanawia. Takie sprawdzanie źródła przerwania zajmuje niepotrzebnie czas. 

Czy mógłby ktoś ten temat rozwinąć. Jak stosować SysTick_IRQn?

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

@jaceksz73 Wystarczy, że wstawisz swój kod w części "user code" w funkcji przerwania w pliku przerwań. Plik ten w drzewie projektu jest najczęściej obok pliku main.c (przynajmniej w środowisku SW4STM).

Edytowano przez PrimeSoul
  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Jak uruchomić kilka kanałów timera w trybie DMA funkcją: HAL_TIM_PWM_Start_DMA(TIM_HandleTypeDef * htim, uint32_t Channel, uint32_t * pData, uint16_t Length) ?

Przykładowe wywołanie:

uint32_t PrawoPWM = 100;
uint32_t LewoPWM = 100;

HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_1 , &PrawoPWM, 1);
HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_2 , &LewoPWM, 1);

Uruchamia tylko kanał 1. 

Natomiast wywołanie:

//HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_1 , &PrawoPWM, 1);
HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_2 , &LewoPWM, 1);

Uruchamia kanał 2. 

To pytanie pojawiło się kilka razy, ale nie znalazłem odpowiedzi.

Generalnie pytanie jest o ostani parametr tej funkcji czyli "length". Co on tak naprawdę oznacza i jak się go wykorzystuje?

I dlaczego tylko pierwszy kanał, który startujemy uruchamia się?

Dokumentacja mówi: Length:  The length of data to be transferred from memory to TIM peripheral.

Niestety nie rozumiem tego.

Udostępnij ten post


Link to post
Share on other sites
(edytowany)


Tak samo jak w części #6, CKD  nie jest preskalerem licznika, w kursie jest błąd, który kosztował mnie trochę czasu do rozgryzienia - okazuje się, że wzór na częstotliwość licznika w kursie jest niepoprawny. Zamiast 
FREQ = TIM_CLK/(ARR+1)(PSC+1)(CKD+1)
Powinno być:
FREQ = TIM_CLK/(ARR+1)(PSC+1)(RCR+1)
Gdzie RCR to repetiton counter i dzięki niemu możemy przerwania otrzymywać nie co każde zliczenie licznika, lecz dopiero po kilku zliczeniach. Nie jest to jednak to samo, co preskaler - dla sygnału PWM ta sztuczka nie zadziała. 
Rejestr CKD jest preskalerem... do licznika dead time w trybach input capture.

Źródło:
https://www.st.com/resource/en/application_note/dm00042534-stm32-crossseries-timer-overview-stmicroelectronics.pdf (s. 11)

Edytowano przez Bobalke

Udostępnij ten post


Link to post
Share on other sites

@Bobalke witam na forum i dziękuję za zgłoszenie 🙂 Sprawdzimy temat i naniesiemy zmianę podczas aktualizacji kursu.

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!

Anonim
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...