STM32 i HAL -- pierwsze podejście, pierwsze

Hej,

Tak jak pisałem -- zacząłem trochę rzeźbić w STM32. Programator jeszcze nie przyszedł (zdecydowałem, że jednak nie użyję płytki Nucleo tylko od razu wrzucę MCU w projektowany układ), ale zacząłem pisać kod na sucho.

I tu pojawia się kilka pytań / problemów.

  1. STM32CubeMX nie umożliwia ustawienia pull-upa na wejściowym pinie. Zakładam, że to błąd. Można to obejść edytując wygenerowany main.c (co i tak zostanie nadpisane przy najbliższej zmianie w CubeMX) lub konfigurując port jeszcze raz, już po tej konfiguracji z main.c. Czy jest bardziej elegancki sposób? Może edycja pliku .ioc? Jest tekstowy i widzę w nim:

PA0-WKUP.GPIOParameters=GPIO_Label PA0-WKUP.GPIO_Label=KEY PA0-WKUP.Locked=true PA0-WKUP.Signal=GPIO_Input

Może tu coś można dopisać?

  1. Podobnie jest z pinami wyjściowymi -- nie widzę nigdzie możliwości ustawienia ich domyślnego stanu. HAL konfiguruje je jako wyjścia i tyle. W jakim stanie te wyjścia będą zaraz po konfiguracji i czy można to zmienić? Znów, fragment pliku .ioc:

PC13-ANTI_TAMP.GPIOParameters=GPIO_Label PC13-ANTI_TAMP.GPIO_Label=LED PC13-ANTI_TAMP.Locked=true PC13-ANTI_TAMP.Signal=GPIO_Output

  1. Czy ja dobrze widzę, że HAL nie udostępnia możliwości szybkiego przełączania pinu między wejściem i wyjściem? Tak się składa, że w pierwszym projekcie, do którego chcę użyć STM32, potrzebuję trzech stanów (niski, wysoki i wysokiej impedancji). Akurat tutaj nie potrzebuję tego szybko (co 4 ms), więc sztuczka z wywoływaniem (stosunkowo ciężkiej) funkcji HAL_GPIO_Init() się uda, ale nie wyobrażam sobie tego w bardziej krytycznych czasowo sytuacjach.

A może w ogóle obsługa GPIO przez HAL to zabawka dla początkujących i nikt, kto programuje na serio, nie korzysta z tego, tylko pisze bezpośrednio do portów?

  1. Czy ja dobrze widzę, że HAL nie umożliwia agregowania pinów? Chciałbym jednocześnie zmienić stan konkretnych pinów w porcie -- czy da się to zrobić przez HAL, czy trzeba pisać bezpośrednio do portu?

  1. Mamy piękny enum GPIO_PinState, a w nim wartości GPIO_PIN_SET oraz GPIO_PIN_RESET. Czy HAL umożliwia takie skonfigurowanie portu, żeby port był zanegowany (czyli np. pisząc PIN_SET chcemy tak naprawdę ustawić tam stan niski, i tak samo odczytując)? Nie widzę nic takiego, a skoro już wprowadzili HAL, to wydaje mi się to rozsądne i logiczne.

  2. Szablon generowany przez CubeMX jest wyindentowany dwiema spacjami. Ja mam inny styl pisania. Czy jest możliwość zmiany indentacji wygenerowanego kodu w taki sposób, żeby kolejna generacja kodu mi tego nie przywróciła? Na razie poradziłem sobie tak, że po prostu przeniosłem swój kod do osobnego pliku i wołam go z wygenerowanego main.c, ale burzy mi to poczucie estetyki.

  1. Pytanie bardziej o sam mikrokontroler. W jakim stanie będą piny, gdy procesor wejdzie w stan resetu (bo np. będę wrzucał nowy program przez programator)? Piny zostaną tak, jak są, czy przejdą w stan wysokiej impedancji? Może można jakoś wymusić ich konkretny stan przed samym programowaniem? Mam do pinów podłączony LCD, więc z oczywistych powodów nie chcę na nich DC.

Chyba na razie tyle z pytań...

Pozdr.

Reply to
Arnold Ziffel
Loading thread data ...

Arnold Ziffel snipped-for-privacy@hooterville.invalid napisał(a):

Umożliwia. System Core -> GPIO. Klikamy w pin i w dolnej tabelce można wyklikać pull-up lub pull-down.

Wydaje mi się, że nie ma możliwości wyklikania tego i trzeba ręcznie. Ponieważ stan rejestrów wyjściowych nie jest modyfikowany, piny będą w stanie niskim.

HAL słynie ze swoich ciężkich struktur i funkcji konfigurujących. W pewnym momencie ST zorientowało się, że coś jest nie tak i udostępniło obok HAL-a sterowniki Low Level. W wygenerowanym kodzie, w katalogu HAL-a, oprócz plików stm32*_hal_* są też pliki stm32*_ll_*. Obejrzyj sobie jakie funkcje zawierają. Są właśnie taką lekką alternatywą. Poza tym te ciężkie funkcje inicjalizują dużo rzeczy naraz i nie pozwalają zmienić tylko jednej bez ruszania innych.

Są ludzie gardzący HAL-em, można ich spotkać na Elektrodzie. Jak zwykle trochę słusznie a trochę nie. Ja używam HAL-a, ale trzeba rozumieć jak działa i jaka jest filozofia jego twórców. W każdym razie HAL jest popularny i moim zdaniem nie trzeba go unikać. Z drugiej strony rejestry STM32 nie są jakoś dużo bardziej skomplikowane od AVR i można też jechać bezpośrednio na rejestrach.

W przypadku GPIO HAL jest bardzo prostą nakładką i masz to samo jak przy pisaniu bezpośrednio:

void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState) { if (PinState != GPIO_PIN_RESET) { GPIOx->BSRR = (uint32_t)GPIO_Pin; } else { GPIOx->BRR = (uint32_t)GPIO_Pin; } }

W zależności od tego, czy na pinie ma być stan wysoki czy niski, jedynka jest zapisywana do rejestru ustawiającego lub zerującego bit w rejestrze wyjściowym. GPIO_Pin to maska bitowa zawierająca listę pinów, na których operujesz. Tak więc agregacja jest, możesz ustawić/wyzerować w jednej linijce wszystkie piny portu. Przy czym ta agregacja jest taka naprawdę sprzętowa, HAL jedynie daje proste opakowanie. Swoją drogą to fajny ficzer STM32, że pinami portu możesz sterować atomowo, nie ma read-modify-write.

O ile wiem, to nie ma czegoś takiego. Te funkcje do operowania na GPIO to prawie jakbyś pisał bezpośrednio do rejestrów.

Też chciałbym wiedzieć, na razie przerabiam ręcznie.

To jakiś specjalny LCD, że napięcie stałe mu szkodzi?

Jeśli mikrokontroler wejdzie w programowy reset, to peryferia działają dalej, np. PWM. Przy czym ST-Link może resetować uC programowo i sprzętowo.

Reply to
Grzegorz Niemirowski

A widzisz, faktycznie :) Robiłem to z menu pojawiającego się po kliknięciu pinu i tam nie było. Dzięki.

Jednak się dało -- we wspomnianym w p. 1 menu. Wygenerował:

/*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);

Hmm, nie mam nic takiego.

formatting link

A można mieszać jedno z drugim, czy HAL trzyma jakiś swój stan, który się rozjedzie, gdy zacznę mu pisać po rejestrach?

A, i to jest kluczowa informacja :)

Właśnie widzę -- fajna rzecz, często pisząc na AVR musiałem wrzucać atomic blocks właśnie przez nieatomowość prostego wystawienia stanu na porcie.

Hmm, chyba mentalnie umieściłem HAL wyżej, niż faktycznie jest.

Ja zwykle piszę sobie różne pomocnicze funkcje i np. moja funkcja "set LED" ma zapalić diodę, a jak ta dioda jest podłączona i jaki stan trzeba wystawić, to jest już zmartwienie tej funkcji. HAL widziałem mniej więcej na tym poziomie.

I za każdym razem po przegenerowaniu kodu odpalasz astyle lub coś takiego?

Najzwyklejszy LCD bez wbudowanego kontrolera. One degradują się od DC.

Stąd zresztą potrzeba przełączania GPIO raz jako wejście, a raz jako wyjście (chcę podłączyć do pinu rezystory, ustalające na nim połowę napięcia zasilającego, gdy STM odłączy od niego swoje drivery). Taki projekt badawczy, żeby zobaczyć, czy uda się uzyskać dostateczny kontrast sterując w taki sposób LCD (mający 4 backplane'y). Jak się nie uda, to spróbuję z PWM a jak i to się nie uda to trudno, mam już w koszyku PCF8566.

Hm, ciekawe. To co dokładnie dzieje się podczas wgrywania wsadu przez ST-Link? Peryferia działają, ale program się zatrzymuje i I/O (o ile nie jest sprzętowo kontrolowane np. przez timer z PWM) zatrzymuje się w takim stanie, w jakim było?

Reply to
Arnold Ziffel

<snip>

Chyba duzo ludzi uzywa HAL-a. Czesc uzywa interfejs low level. Ja zaczelem od biblioteki 'libopencm3', z tym ze sporo rzeczy robilem bezposrednio.

Co do szybkiego przelaczania: mysle ze filozofia wielu programow zaklada konfiguracje na starcie a potem brak zmian. W STM32 konfiguracja moze byc dosc "ciezka": na starcie wiekszosc urzadzen jest wylaczona (dla oszczednosci energii), zeby je uaktywnic trzeba w paru miejscach zrobic odpowiednie ustawienia. Np. dla GPIO jest lock register: mozesz zablokowac zmiany konfiguracji az do nastepnego resetu.

Normalnie chcesz zeby zapis pin(ow) to byla 1-3 instrukcje maszynowe: w ogolnej wersji jedna umieszcza adres portu w rejestrze procesora, druga umieszcza wartosc w rejestrze procesora, trzecia presyla wartosc z rejestru procka do portu. W wielu sytuacjach potrzebne sa juz w rejestrach procesora, wtedy wystarczy pojedyncza instrukcja. Jak masz odpowiednie makra (czy funkcje inline) to tak bedzie (nie wiem czy HAL to robi). Negacje mozesz wrzucic w miare prosta makrologia, ale to moze (nie musi) dodac dodatkowe instrukcje wiec nie jest dobre jako defaultowy ficzer.

W datasheet jest stan rejestrow po resecie. We wszystkich modelach STM32 ktore widzialem normaly stan po resecie to bylo "input floating".

Ja uzywalem STlink (sprzet) z opensourcowym programem stlink. On _nie_ resetowal procka do programowania. Jak byla jakas dziwna konfiguracja urzadzen to zostawala po programowaniu

Tak a propo: duzo modeli ma bootloader. Jak odpowiednio ustawisz piny (i ewentualnie flagi konfiguracji) to bootloader startuje po resecie. Niezaleznie od tego czy bootloader wystatartowal mozesz programowac przez STlink (SWD). Ale stan procka sie rozni: bootloader konfiguruje niektore urzadzenia.

Reply to
antispam

Ale jest coś takiego jak rejestry SET/CLR/INV (w pic32/mips), dzięki którym za pomocą jednej operacji atomowej można ustawić, skasować lub przestawić dowolny bit na porcie czy rejestrze? Np. PORTESET=16 ustawi bit na 4 pinie portu E albo TRISEINV=16 przestawi bit kierunku na pinie 4 portu E.

Reply to
Marek

W dniu 2022-07-26 o 08:14, Marek pisze:

W nowych avr-ach (mega 0) też to już jest. Nawet toggle można atomowo robić.

Reply to
Janusz

Marek snipped-for-privacy@fakeemail.com napisał(a):

Jest takie coś, nazywa się bitbanding.

#include "stm32f4xx.h"

#define BITBAND_PERI_REF 0x40000000 #define BITBAND_PERI_BASE 0x42000000 #define BITBAND_PERI(a,b) ((BITBAND_PERI_BASE + (a-BITBAND_PERI_REF)*32 + (b*4)))

#define GPIOA_ODR ((unsigned int)&(GPIOA->ODR))

#define GPIOA_ODR5 *((volatile unsigned char *) (BITBAND_PERI(GPIOA_ODR,

5))) #define GPIOA_ODR6 *((volatile unsigned char *) (BITBAND_PERI(GPIOA_ODR, 6)))

void main(void) { RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //włącz GPIOA GPIOA->MODER |= GPIO_MODER_MODER5_0; //LED1 - output GPIOA->MODER |= GPIO_MODER_MODER6_0; //LED2 - output GPIOA->PUPDR |= GPIO_PUPDR_PUPDR7_0; //przycisk - pull up while(1) { if (GPIOA->IDR & GPIO_IDR_IDR_7) { //przycisk puszczony GPIOA_ODR5 = 1; GPIOA_ODR6 = 0; } else { //naciśnięty GPIOA_ODR5 = 0; GPIOA_ODR6 = 1; } } }

Reply to
Grzegorz Niemirowski

Kiedyś było nieco inaczej - przy generowaniu kodu zaklikiwało się checkboksy HAL i/lub LL.

Teraz w Project Manager, Advanced Settings w panelu Driver Selector dla każdego peryferia wybierasz, czy chcesz mieć HAL, czy LL. Nie można wybrać obu, ale jak testowo wybrałem dla czegoś tam HAL, a dla czegoś innego LL, to pojawiły się pliki *hal* i *ll*.

Szczegółów nie podam, ja z tych programujących na rejestrach. Kiedyś, w czasach przed HAL i LL była Standard Pheriperal Library

- w licencji było, że służy tylko jako przewodnik dla klientów i nie należy jej stosować we własnych projektach. Nie żałuję - jak ktoś już pisał, rejestry w STM32 są całkiem sensowne. HAL i LL traktuję jak SPL kiedyś - jako przewodnik.

Pozdrowienia, MKi

Reply to
MKi

Arnold Ziffel snipped-for-privacy@hooterville.invalid napisał(a):

To może być kwestia ustawień opcji generowania kodu. Zobacz sobie folder rezpozytorium Cube: C:\Users\Arnold\STM32Cube\Repository\STM32Cube_FW_F4_V1.27.0\Drivers\STM32F4xx_HAL_Driver\Src

Miganie LED-em low level:

formatting link

HAL prawie nic nie trzyma z wyjątkiem zmiennej uwTick od liczenia ticków 1 ms oraz aktualnego taktowania rdzenia w zmiennej SystemCoreClock. Do tego w strukturach peryferiów trzyma sobie np. lock. Ogólnie można mieszać. Ja np. w aktualnym projekcie używam HAL-a ale jak chciałem ustawić wartość PWM, to napisałem zwyczajnie: TIM1->CCR4 = value; zamiast: __HAL_TIM_SET_COMPARE(&tim1, TIM_CHANNEL_4, value);

Tutaj nie ma czegoś takiego. HAL ma po prostu ukrywać rejestry i dawać przenośność kodu. Dla mnie główna wartość leży w gotowych sterownikach peryferiów, np. jest gotowa funkcja HAL_I2C_Mem_Read().

Ja akurat używam Cube jako dodatkowe narzędzie. Wypluwa mi kod w innym miejscu i przeklejam interesujące fragmenty do projektu.

Tutaj mogło mi się pomylić. Sprawdziłem teraz na STM32CubeProgrammer i tam kliknięcie Connect powoduje wyłączenie PWM niezależnie od wyboru jednego z trzech trybów resetu. Natomiast jeśli w Eclipse zatrzymam program, to PWM działa dalej.

Reply to
Grzegorz Niemirowski

Są rejestry, BRR i BSRR, do ustawiania pojedyńczych bitów na porcie.

Reply to
jacek pozniak

W dniu 26.07.2022 o 10:09, Grzegorz Niemirowski pisze:

To ustawiasz w rejestrach DBGMCU - dla każdego peryferia (peryferium?) masz bit określający, czy ma stanąć czy działać dalej przy zatrzymaniu programu w IDE.

Reply to
MKi

W dniu 26.07.2022 o 09:40, Grzegorz Niemirowski pisze:

Ale chyba nie we wszystkich STM32. Bodajże STM32F0 tego nie ma. Nie wiem, jak inne.

Pozdrowienia, MKi

Reply to
MKi

Jeśli możesz, odezwij się, proszę, na priv.

W dniu 2022-07-26 o 10:00, MKi pisze:

Reply to
stary grzyb

Tu, a dokładniej w:

~/STM32Cube/Repository/STM32Cube_FW_F4_V1.27.1/Drivers/STM32F4xx_HAL_Driver/Src

mam pliki *_ll_*

Faktycznie, dużo lżejsze niż HAL.

Ok, teraz jasne.

O, no to jest super. O ile jest napisane sensownie.

Ciekawe... chyba będę musiał to po prostu rozpoznać bojem przed podłączeniem wyświetlacza. Tak czy inaczej chcę to zrobić, żeby sprawdzić, czy wszystko jest generowane tak jak powinno.

Reply to
Arnold Ziffel

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.