niedziela, 2 czerwca 2019

[0] STM32F4 - Biblioteka LL - Sterowanie pinami

W tym poście chciałbym opisać podstawową implementację bibliotek LL (ang. Low Layer Library) za pomocą CubeMx. Szybki przykład będzie obsługiwał przycisk oraz wykonywał sterowanie diodami.


[Źródło: http://www.st.com/en/evaluation-tools/stm32f4discovery.html]


Biblioteki LL są to nisko poziomowe biblioteki wygenerowane obok bibliotek HAL. Pozwalają one w bardzo łatwy sposób, za pomocą makr i prostych funkcji, odwoływać się bezpośrednio do rejestrów. Przy pominięciu mocno rozbudowanych instrukcji znanych z bibliotek HAL.

Cube MX:


Główna konfiguracja wygląda bardzo standardowo co do podobnego projektu z  bibliotekami HAL.
Czyli tak samo ustawiamy cztery diody jako wyjścia (sterowanie diodami PD12 do PD15) oraz PA0 jako wejście.


Konfiguracja zegara:


Teraz najważniejszy element czyli wybór bibliotek (Project Manager->Advanced Settings->Driver Selector):


I tym sposobem za pomocą jednego kliknięcia uzyskujemy obsługę układu na bibliotekach LL zamiast HAL. Wygenerowanie projektu wygląda standardowo.

Po wygenerowanie kodu i załadowaniu go do edytora np. System Workbench wszystko powinno się skompilować bez żadnych problemów. Konfigurajcę wygenerowanego projektu można podglądać w seksci C/C++ General -> Path And Symbols -> Symbols. Tutaj ponieważ korzystamy z bibliotek LL zamiast definicji USE_HAL_DRIVER wykorzystała została  USE_FULL_LL_DRIVER.


Projekt:


W opisanym przykładzie uruchomię diody oraz obsługę przycisku:

Konfiguracja pinów dla diod czyli jako wyjścia wykonuje się w następujący sposób:

  1. static void MX_GPIO_Init(void)
  2. {
  3.   LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
  4.   LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOH);
  5.   LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOD);
  6.   LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
  7.   LL_GPIO_ResetOutputPin(GPIOD, LL_GPIO_PIN_12|LL_GPIO_PIN_13|LL_GPIO_PIN_14|LL_GPIO_PIN_15);
  8.   GPIO_InitStruct.Pin = LL_GPIO_PIN_12|LL_GPIO_PIN_13|LL_GPIO_PIN_14|LL_GPIO_PIN_15;
  9.   GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
  10.   GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
  11.   GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  12.   GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  13.   LL_GPIO_Init(GPIOD, &GPIO_InitStruct);
  14. }

Całość prezentuje się bardzo podobnie co w bibliotekach HAL. Na początku następuje inicjalizacja struktury po czym przechodzimy do włączenia zegarów, resetu ustawianych pinów oraz głównej inicjalizacji.

Funkcja uruchamiająca diody oraz przycisk:

  1. static void MX_GPIO_Init(void)
  2. {
  3.   GPIO_InitTypeDef GPIO_InitStruct = {0};
  4.   /* GPIO Ports Clock Enable */
  5.   __HAL_RCC_GPIOH_CLK_ENABLE();
  6.   __HAL_RCC_GPIOA_CLK_ENABLE();
  7.   __HAL_RCC_GPIOD_CLK_ENABLE();
  8.   /*Configure GPIO pin Output Level */
  9.   HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);
  10.   /*Configure GPIO pin : PA0 */
  11.   GPIO_InitStruct.Pin = GPIO_PIN_0;
  12.   GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  13.   GPIO_InitStruct.Pull = GPIO_NOPULL;
  14.   HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  15.   /*Configure GPIO pins : PD12 PD13 PD14 PD15 */
  16.   GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
  17.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  18.   GPIO_InitStruct.Pull = GPIO_NOPULL;
  19.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  20.   HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
  21. }

Uruchomienie wszystkich diod na płycie STM32F4 Discovery wygląda następująco:

  1.   LL_GPIO_SetOutputPin(GPIOD, LL_GPIO_PIN_12);
  2.   LL_GPIO_SetOutputPin(GPIOD, LL_GPIO_PIN_13);
  3.   LL_GPIO_SetOutputPin(GPIOD, LL_GPIO_PIN_14);
  4.   LL_GPIO_SetOutputPin(GPIOD, LL_GPIO_PIN_15);

Funkcja ustawiająca stan wygląda następująco:

  1. __STATIC_INLINE void LL_GPIO_SetOutputPin(GPIO_TypeDef *GPIOx, uint32_t PinMask)
  2. {
  3.   WRITE_REG(GPIOx->BSRR, PinMask);
  4. }

Funkcja pozwalająca na zmianę stanu na porcie na niski:

  1. __STATIC_INLINE void LL_GPIO_ResetOutputPin(GPIO_TypeDef *GPIOx, uint32_t PinMask)
  2. {
  3.   WRITE_REG(GPIOx->BSRR, (PinMask << 16));
  4. }

Przykładowa funkcja odpowiedzialna za mruganie diodami:

  1. LL_mDelay(100);
  2. LL_GPIO_SetOutputPin(GPIOD, LL_GPIO_PIN_12);
  3. LL_GPIO_SetOutputPin(GPIOD, LL_GPIO_PIN_13);
  4. LL_GPIO_SetOutputPin(GPIOD, LL_GPIO_PIN_14);
  5. LL_GPIO_SetOutputPin(GPIOD, LL_GPIO_PIN_15);
  6. LL_mDelay(100);
  7. LL_GPIO_ResetOutputPin(GPIOD, LL_GPIO_PIN_12);
  8. LL_GPIO_ResetOutputPin(GPIOD, LL_GPIO_PIN_13);
  9. LL_GPIO_ResetOutputPin(GPIOD, LL_GPIO_PIN_14);
  10. LL_GPIO_ResetOutputPin(GPIOD, LL_GPIO_PIN_15);

Prosta obsługa diod oraz przycisku:

  1. if (LL_GPIO_ReadInputPort(GPIOA)&LL_GPIO_PIN_0) //button
  2. {
  3.   LL_GPIO_SetOutputPin(GPIOD, LL_GPIO_PIN_12);
  4.   LL_GPIO_SetOutputPin(GPIOD, LL_GPIO_PIN_13);
  5.   LL_GPIO_SetOutputPin(GPIOD, LL_GPIO_PIN_14);
  6.   LL_GPIO_SetOutputPin(GPIOD, LL_GPIO_PIN_15);
  7. }
  8. else
  9. {
  10.   LL_GPIO_ResetOutputPin(GPIOD, LL_GPIO_PIN_12);
  11.   LL_GPIO_ResetOutputPin(GPIOD, LL_GPIO_PIN_13);
  12.   LL_GPIO_ResetOutputPin(GPIOD, LL_GPIO_PIN_14);
  13.   LL_GPIO_ResetOutputPin(GPIOD, LL_GPIO_PIN_15);
  14. }

Cały kod jest do pobrania z dysku Google pod tym linkiem.