Warning
This page is located in archive.

2. GPIO

Cíle cvičení

  1. Orientovat se v systému registrů GPIO na procesorech STM32
  2. Pochopit způsoby přístupu do registrů

Co je třeba si připravit

  1. Prázdný projekt vytvořený v STM32CubeIDE.

Registry GPIO

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.

GPIOx_MODER (mode register)

Referenční manuál str. 157

Offset: 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)

GPIOx_OTYPER (output type register)

Referenční manuál str. 157

Offset: 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)

GPIOx_OSPEEDR (output speed register)

Referenční manuál str. 158

Offset: 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.

GPIOx_PUPDR (pull-up pull-down register)

Referenční manuál str. 158

Offset: 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

GPIOx_IDR (input data register)

Referenční manuál str. 159

Offset: 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.

GPIOx_ODR (output data register)

Referenční manuál str. 159

Offset: 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.

Práce s registry

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

Získání adresy registru

Přímý přístup pomocí nastavení offsetů

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;

Tento způsob adresování registrů je výhodný tam, kde potřebujeme manipulovat např. jen jedním nebo dvěma registry periferie procesoru. V případě potřeby manipulace s více registry více periferií stejného druhu se však program může lehko stát nepřehledným.

Mapování registrů pomocí struktury

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;

Nastavení pomocí bitové masky

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:

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:

// set
GPIOC->ODR |= (1 << n);
// reset
GPIOC->ODR &= ~(1 << n);  

Využití set/reset registru (GPIOx _BSRR)

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);

Využití bitového pole ve struktuře

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;

Pozor při realizaci na případy, kdy je třeba využít registry následující za ODR. Struktura musí mít takovou velikost jako celý registr! Velikost struktury lze nastavit vložením dalších proměnných.

Bit-banding

courses/a8m37mam/tutorials/02.txt · Last modified: 2020/11/07 20:15 by viteks