Search
Procesory řady STM32F4 obsahují hned několik 16b vstupně výstupních bran, např. procesor STM32F401 jich má osm, označených A-H. Vlastnosti GPIO bran lze nastavovat pomocí několika 32 bitových registrů.
V referenčním manuálu jsou uvedeny adresy jednotlivých registrů v paměti procesoru. Jsou uvedeny formou offsetu vůči začátku bloku popisujícím jednu GPIO bránu.
Referenční manuál str. 157
Offset: 0x00
0x00
Mód pinu se nastavuje dvojicemi sousedních bitů. Pokud bychom chtěli nastavit pin 2 portu B, upravíme 4. a 5. bit registru GPIOB→MODER. Pro nastavení pinu 12 portu D, upravíme 24. a 25. bit registru GPIOD→MODER.
00 – Input (pin bude vstupní) 01 – Output (pin bude výstupní) 11 – Analog (pin bude analogový vstup) 10 – Alternate function (pin bude sloužit k alternativní funkci, například bude připojen k jiné periferii v procesoru)
Offset: 0x04
0x04
Registrem určujeme, zda bude výstupní pin dvojčinný nebo s otevřeným kolektorem, který je nezbytný pro připojení k datovým sběrnicím. Každý pin dané brány je nastaven právě jedním bitem, je tedy využito dolních 16 bitů registru.
0 – Push-pull (dvojčinný) 1 – Open-drain (otevřený kolektor)
Referenční manuál str. 158
Offset: 0x08
0x08
32b registr. Opět dvojce bitů přísluší jednomu pinu. Pomocí toho registru lze nastavit maximální frekvenci výstupního signálu (samotný procesor je natolik rychlý, že by výstupní frekvence mohla být velmi vysoká, ovšem to je při delších vedeních nevyhovující a delším vedením lze pro vysoké frekvence považovat i vodič délky pár centimetrů). Ovšem snížení frekvence znamená i prodloužení doby náběžné a sestupné hrany. To ale také znamená menší dodávaný proud a tím i ušetřená energie.
00 – Low speed 01 – Medium speed 10 – Fast speed 11 – High speed
Rychlosti záleží na typu procesoru, je třeba nahlédnout do datasheetu.
Offset: 0x0C
0x0C
32b registr. Pro nastavení správných úrovní na vstupním pinu lze zapnout interní pull-up nebo pull-down rezistor. Pokud není připojen, úroveň na vstupním pinu je nedefinovaná (floating input) a může vést k nesprávnému vyhodnocení stavu brány.
00 – Źádné rezistory 01 – Pull-up (rezistor na napájecí napětí) 10 – Pull-down (rezistor na zem) 11 – nevyužitá kombinace
Referenční manuál str. 159
Offset: 0x10
0x10
16b registr. Obsahuje údaj o logické hodnotě na vstupních pinech dané brány. Pin musí být nastaven jako vstupní digitální. Tedy jaká logická úroveň se nachází například na pinu 3 portu E, zjistíme z třetího bitu registru GPIOE→IDR.
Offset: 0x14
0x14
16b registr. Nastavením bitů registru je ovládána logická úroveň jednotlivých pinů brány. Pin musí být nastaven jako digitální výstupní. Pokud chceme mít logickou úroveň HIGH na pinu 14 portu D, zapíšeme logickou jedničku na 14. bit registru GPIOD→ODR.
Při nastavování registru zpravidla pracujeme na úrovni bitů. Je třeba dbát na to, aby při operaci nastavení nebyly ovlivněny bity registru, které s danou operací nesouvisí.
Pokud je znám začátek paměťového bloku, popisujícího periferie, a offsety jednotlivých registrů vzhledem blokům vyhrazeným pro GPIOx, je možné přímo definovat adresy jednotlivých registrů:
#define PERIPH_BASE 0x40000000 #define GPIOA_BASE (PERIPH_BASE + 0x20000) #define GPIOA_MODER ((unsigned long *)(GPIOA_BASE + 0x00)) #define GPIOA_ODR ((unsigned long *)(GPIOA_BASE + 0x14))
K jednotlivým registrům pak přistupujeme pomocí ukazatele:
// všechny piny brány GPIOA nastavit na log. 1 *GPIOA_ODR = 0xFFFF;
Protože je známa adresa bloku paměti a jeho vnitřní uspořádání na registry, je možné v jazyce C vytvořit takovou strukturu, která umožní přístup k jednotlivým registrům. V případě procesorů STM32F4 jsou registry 32 bitové, je tedy třeba zvolit pro popis registru neznaménkový datový typ takového rozsahu.
typedef struct { unsigned int MODER; unsigned int TYPER; unsigned int OSPEEDR; unsigned int PUPDR; unsigned int IDR; unsigned int ODR; unsigned int BSSR; } GPIO;
Ukazatel, reprezentující registry periferie, lze pak vytvořit třeba takto:
#define GPIOA ((GPIO *) 0x40020000) #define GPIOB ((GPIO *) 0x40020400) #define GPIOC ((GPIO *) 0x40020800)
Protože se jedná o ukazatel, pro přístup ke členským proměnným struktury lze využít následující syntaxi:
// všechny piny brány GPIOA nastavit na log. 0 GPIOA->ODR = 0x0000;
Nejjednodušší technikou, jak nastavit hodnotu registru, je nastavit všechny bity najednou:
GPIOC->ODR = 0xF0FE; // ~ 0b1111000011111110
Tento příkaz nastaví stav všech pinů registru bez ohledu na jejich předcházející stav, který je zcela přepsán.
Pokud chceme nastavit (tj. log. 1) pouze vybrané pin brány GPIOC (a tedy bity registru ODR), je třeba využít logický součet OR s takovým číslem, které bude mít log. 1 na těch bitech, které chceme nastavit. Příklad nastavení osmého bitu:
OR
GPIOC->ODR |= 0x0100; // ~ 0b0000000100000000
Pro reset vybraného bitu (tj. log 0) se využívá logický součin:
GPIOC->ODR &= 0xFEFF; // ~ 0b1111111011111111
Je zřejmé, že bitová maska použitá v případě resetu bitu je negací masky použité k nastavení bitu:
GPIOC->ODR &= ~(0xFEFF); // ~ 0b0000000100000000
Používání takto vytvořených masek je možné, ovšem ne příliš praktické; daleko lepší je pro set a reset bitu na pozici n použít operaci posun doleva:
n
// set GPIOC->ODR |= (1 << n); // reset GPIOC->ODR &= ~(1 << n);
32 bitový BSSR registr je rozdělen na dvě poloviny - spodních 16 bitů slouží k nastavení příslušného pinu na log. 1 (set), horních 16 bitů pak piny nuluje (reset). Příklad pro devátý pin brány GPIOC:
// set GPIOC->BSRR = (1<<8); // reset GPIOC->BSRR = (1<<24);
Všimněte si použití operátoru = - logické hodnoty 0 v čísle, kterým se nastavuje hodnota registru, nijak neovlivňují původní hodnoty bitů. Na rozdíl od předchozího přístupu se jedná o atomickou operaci.
=
Pro reset bitu brány lze alternativně použít registr BRR, který je vytvořen jako alias hodních 16 bitů registru BSRR, není pro reset potřeba používat 16b offset:
GPIOC->BRR = (1<<8);
V jazyce C je možné definovat v rámci struktury tzv. bitové pole. Je to technika, která umožňuje využít pouze část bitové reprezentace datového typu. Spodních osm bitů registru ODR by bylo možné popsat následovně:
typedef struct { unsigned char B0:1; unsigned char B1:1; unsigned char B2:1; unsigned char B3:1; unsigned char B4:1; unsigned char B5:1; unsigned char B6:1; unsigned char B7:1; } ODR_type;
Popis celé brány se pak změní následovně:
typedef struct { unsigned int MODER; unsigned int TYPER; unsigned int OSPEEDR; unsigned int PUPDR; unsigned int IDR; ODR_type ODR; } GPIO;
A změnu hodnoty bitu je možné provádět velmi elegantně:
GPIOA->ODR.B5 = 0; GPIOA->ODR.B5 = 1;