Search
Podklady pro Cvičení_6 - Skalický
Jádro ARM M4
Datasheet STM32F401
Referenční manuál STM32F401
Instrukční sada STM32F4
Podklady pro Nucleo STM32F401
Ukázkový projekt vloženého ASM do jazyka C
Ukázkový kód - práce s GPIO a časovačem
#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)
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;