5. Přístup k periferiím v C

Cíle cvičení

  1. Orientovat se v systému registrů GPIOx, RCC, TIMx na procesorech STM32
  2. Pochopit způsoby přístupu do registrů
  3. Ovládání LED, tlačítka, časovače

Co je třeba si připravit

  1. Prázdný projekt vytvořený v STM32CubeIDE.
  2. Prostudovat GPIOx a RCC registry

Podklady pro cvičení

Datasheety

Projekt

Kódy pro cvičení

#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))
 
#define BITSET(reg, bit)    ((reg) |= (1U << (bit)))
#define BITCLEAR(reg, bit)  ((reg) &= (~(1U << (bit))))
#define BITTOGGLE(reg, bit) ((reg) ^= (1U << (bit)))
#define BITGET(reg, bit)    ((reg  &  (1U << bit)) >> bit)

Nastavování registrů GPIO

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

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.
courses/b2m37mam/labs/05.txt · Last modified: 2024/10/22 21:03 by nentvond