Vaja 3: GPIO z STM HAL knjižnico
Namen tokratne vaje je, da spoznate s knjižnico STM HAL na primeru naprave GPIO, ki smo jo uporabljali zadna dva tedna. Knjižnica je organizirana na podoben način kot na zadnji vaji, ko smo si sami izdelali funkcije za uporabo GPIO.
Vklop ure
Za vklop ure posamezne GPIO naprave so na voljo enostavni makroji, ki so zapisani v obliki __HAL_RCC_GPIOx_CLK_ENABLE()
:
();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOI_CLK_ENABLE();
__HAL_RCC_GPIOJ_CLK_ENABLE(); __HAL_RCC_GPIOK_CLK_ENABLE
Na voljo so tudi makroji za izklop ure GPIO naprav, ki so zapisani v obliki __HAL_RCC_GPIOx_CLK_DISABLE()
.
Struktura GPIO registrov in naslovi naprav
Knjižnica ima definirano strukturo naprave, ki jo vidite spodaj. Ta vsebuje vse registre, podobno kot smo to naredili na zadnji vaji. Strukturo najdemo jo v stm32h750xx.h
.
typedef struct
{
uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */
__IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */
__IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */
__IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
__IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */
__IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */
__IO uint32_t BSRR; /*!< GPIO port bit set/reset, Address offset: 0x18 */
__IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */
__IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */
__IO } GPIO_TypeDef;
__IO
je zgolj redefinicija rezervirane besede volatile
. V eni izmed .h datotek knjižnice bomo našli vrstico typedef volatile __IO;
.
V isti datoteki so definirani tudi vsi kazalci na naprave:
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
#define GPIOH ((GPIO_TypeDef *) GPIOH_BASE)
#define GPIOI ((GPIO_TypeDef *) GPIOI_BASE)
#define GPIOJ ((GPIO_TypeDef *) GPIOJ_BASE)
#define GPIOK ((GPIO_TypeDef *) GPIOK_BASE)
Tako strukturo kot definice kazalcev na vse naprave smo na zadnji vaji napisali na popolnoma enak način.
Inicializacija GPIO
Za inicializacijo pinov uporabimo funkcijo HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
. Prvi argument je tako kot pri zadnji vaji kazalec na napravo (strukturo z registri). Uporabili bomo torej eno izmed konstant, ki so zapisane zgoraj. Drugi argument je kazalec na inicializacijsko strukturo. Tako bomo namesto s štirimi dodatnimi argumenti (pin
, mode
, otype
, speed
, pupdr
) funkcijo za inicializacijo klicali z dodatno strukturo, ki vsebuje vrednosti za inicializacijo.
Ta struktura je definirana v stm32h7xx_hal_gpio.h
:
typedef struct
{
uint32_t Pin;
uint32_t Mode;
uint32_t Pull;
uint32_t Speed;
uint32_t Alternate;
} GPIO_InitTypeDef;
Za element Pin
uporabimo eno izmed pripravljenih konstant za oznako pinov (ali njihovo kombinacijo):
#define GPIO_PIN_0 ((uint16_t)0x0001) /* Pin 0 selected */
#define GPIO_PIN_1 ((uint16_t)0x0002) /* Pin 1 selected */
#define GPIO_PIN_2 ((uint16_t)0x0004) /* Pin 2 selected */
#define GPIO_PIN_3 ((uint16_t)0x0008) /* Pin 3 selected */
#define GPIO_PIN_4 ((uint16_t)0x0010) /* Pin 4 selected */
#define GPIO_PIN_5 ((uint16_t)0x0020) /* Pin 5 selected */
#define GPIO_PIN_6 ((uint16_t)0x0040) /* Pin 6 selected */
#define GPIO_PIN_7 ((uint16_t)0x0080) /* Pin 7 selected */
#define GPIO_PIN_8 ((uint16_t)0x0100) /* Pin 8 selected */
#define GPIO_PIN_9 ((uint16_t)0x0200) /* Pin 9 selected */
#define GPIO_PIN_10 ((uint16_t)0x0400) /* Pin 10 selected */
#define GPIO_PIN_11 ((uint16_t)0x0800) /* Pin 11 selected */
#define GPIO_PIN_12 ((uint16_t)0x1000) /* Pin 12 selected */
#define GPIO_PIN_13 ((uint16_t)0x2000) /* Pin 13 selected */
#define GPIO_PIN_14 ((uint16_t)0x4000) /* Pin 14 selected */
#define GPIO_PIN_15 ((uint16_t)0x8000) /* Pin 15 selected */
#define GPIO_PIN_All ((uint16_t)0xFFFF) /* All pins selected */
Za element Mode
so na voljo spodnje nastavitve. Kot vidimo s tem elementom nastavimo ali bo pin deloval kot vhod, izhod, alternativna funkcija ali v analognem načinu. Hkrati pa, v primeru, da uporabljamo pin kot izhod, nastavimo tudi kakšen tip izhoda bomo uporabljali: push-pull (PP) ali open-drain (OD). Obstaja še nekaj dodatnih možnosti, ki niso navedena spodaj. Te bomo spoznali v nadaljevanju semestra.
// vhod
GPIO_MODE_INPUT // izhod push-pull
GPIO_MODE_OUTPUT_PP // izhod open-drain
GPIO_MODE_OUTPUT_OD // alternativna funkcija push-pull
GPIO_MODE_AF_PP // alternativna funkcija open-drain
GPIO_MODE_AF_OD // analogni način GPIO_MODE_ANALOG
Za element Pull
so na voljo spodnje nastavitve, ki sovpadajo neposredno z vrednostmi v registru PUPDR
:
// brez pull-up ali pull-down upora
GPIO_NOPULL // vklopi pull-up upor
GPIO_PULLUP // vklopi pull-down upor GPIO_PULLDOWN
Nastavitve, ki jih lahko uporabimo za Speed
podobno sovpadajo neposredno z vrednostmi v registru OSPEEDR
:
// nizka hitrost osveževanja
GPIO_SPEED_FREQ_LOW // srednja hitrost osveževanja
GPIO_SPEED_FREQ_MEDIUM // visoka hitrost osveževanja
GPIO_SPEED_FREQ_HIGH // zelo visoka hitrost osveževanja GPIO_SPEED_FREQ_VERY_HIGH
O Alternate
elementu init strukture bomo več povedali v nadaljevanju semestra, zaenkrat ga ne potrebujemo.
Inicializacija pina PB12
kot izhod tipa push-pull brez pull-up/pull-down uporov in z najnižjo frekvenco osveževanja bi zapisali kot:
(); // vklop ure naprave GPIOB
__HAL_RCC_GPIOB_CLK_ENABLE
= {0};
GPIO_InitTypeDef init .Pin = GPIO_PIN_12; // pin 12
init.Mode = GPIO_MODE_OUTPUT_PP; // izhod push-pull
init.Pull = GPIO_NOPULL; // brez pull-up/down uporov
init.Speed = GPIO_SPEED_FREQ_LOW; // nizka hitrost osveževanja
init(GPIOB, &init); // naprava GPIOB HAL_GPIO_Init
Nastavljanje vrednosti na izhodu
Za nastavljanje vrednosti na izhodu imamo na voljo dve funkciji:
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
Funkcija HAL_GPIO_WritePin
omogoča nastavljanje logične vrednosti pina na poljubno vrednost, funkcija HAL_GPIO_TogglePin
pa negira trenutno logično vrednost pina. Za stanje pina uporabimo eno izmed možnosti definiranih v enumu GPIO_PinState
:
typedef enum
{
= 0U,
GPIO_PIN_RESET
GPIO_PIN_SET} GPIO_PinState;
Namesto zgornjih vrednosti lahko uporabite tudi številski vrednosti 0 in 1. Nekaj primerov uporabe obeh funckij:
(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); // nastavimo pin PA0 na 1
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1 | GPIO_PIN_10, GPIO_PIN_RESET); // nastavimo pin PB1 in PB10 na 0
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, 0); // nastavimo pin PC1 na 0
HAL_GPIO_WritePin(GPIOK, GPIO_PIN_11); // obrnemo logično vrednost pina PK11 HAL_GPIO_TogglePin
Branje vhoda
Za branje stanja na vhodu imamo na voljo funkcijo:
(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); GPIO_PinState HAL_GPIO_ReadPin
Funkcija vrne GPIO_PIN_SET
(številska vrednost 1) v primeru, da je vrednost na vhodu podanega pina enica. V nasprotnem primeru vrne GPIO_PIN_RESET
(številska vrednost 0).
Primer LED na PD3
Koda za prižig LED diode na pinu PD3
:
();
__HAL_RCC_GPIOD_CLK_ENABLE
= {0};
GPIO_InitTypeDef init .Pin = GPIO_PIN_3;
init.Mode = GPIO_MODE_OUTPUT_PP;
init.Pull = GPIO_NOPULL;
init.Speed = GPIO_SPEED_FREQ_LOW;
init(GPIOD, &init);
HAL_GPIO_Init
(GPIOD, GPIO_PIN_3, GPIO_PIN_SET); HAL_GPIO_WritePin
Lahko bi izbrali tudi open-drain tip izhoda. V tem primeru je potrebno uporabiti pull-up upor:
();
__HAL_RCC_GPIOD_CLK_ENABLE
= {0};
GPIO_InitTypeDef init .Pin = GPIO_PIN_3;
init.Mode = GPIO_MODE_OUTPUT_OD;
init.Pull = GPIO_PULLUP;
init.Speed = GPIO_SPEED_FREQ_LOW;
init(GPIOD, &init);
HAL_GPIO_Init
(GPIOD, GPIO_PIN_3, GPIO_PIN_SET); HAL_GPIO_WritePin
HAL_Delay
HAL STM knjižnica ponuja funkcijo, ki omogoča izvedbo zakasnitve z milisekundno natačnostjo - HAL_Delay(uint32_t ms)
. Klic funkcije HAL_Delay(1000)
se bo tako izvajal točno 1000ms oz. 1s. Funkcijo lahko kličemo kadarkoli za klicema funckij HAL_Init()
in SystemClock_Config()
, ki se nahajata v programu, ko kreirajte projekt.
Spodnji program bo stanje pina PD3 obrnil vsako sekundo.
while(1) {
(1000);
HAL_Delay(GPIOD, GPIO_PIN_3, GPIO_PIN_SET);
HAL_GPIO_WritePin(1000);
HAL_Delay(GPIOD, GPIO_PIN_3, GPIO_PIN_RESET);
HAL_GPIO_WritePin}
Naloga 3
Napišite program, ki bo:
- inicializiral pin PC13 (gumb) kot vhod,
- inicializiral pina PD3 in PJ2 kot izhod tipa push-pull brez pull-up/down uporov,
- inicializiral pin PI13 kot izhod tipa open-drain s pull-up uporom.
Po inicializacij naj se izvaja naslednje:
- rdeča LED naj naj se prižge ob prvem pritisku gumba, ob drugem pritisku ugasne, ob tretjem zopet prižge, in tako dalje,
- zelena LED na pinu PD3 naj utripa s frekvenco 4Hz (0,25s naj bo prižgana, 0,25s ugasnjena).
- zelena LED na pinu PJ2 naj utripa s frekvenco 2Hz (0,5s naj bo prižgana, 0,5s ugasnjena).