Home Programování C/C++ (MCU) STM32F4 Discovery - Tutorial (06) Nested vectored interrupt controller – NVIC
V tomto článku se podíváme na poměrně užitečnou věc a to přerušení. Přerušení je nedílnou součástí číslicové elektroniky a jeho použití značně ulehčuje ošetření některých situací nebo vykonání určitých úkonů v co nejkratším čase od vzniku požadavku.
Jak se přerušení chová je dáno jádrem ARM a proto přesnější popis přerušení najdeme v dokumentaci k jádru ARM. NVIC se dá volně přeložit jako řadič vektorů vnořených přerušení. To znamená že ARM umožňuje nastavovat priority konkrétním přerušením, podle kterých jsou vykonávány.
STM32F4xx využívá 82 kanálů přerušení a všem přerušením lze nastavit prioritu od 0 do 16 (jsou využity 4 bity pro prioritu (ARM umožňuje využít 8 bitů pro prioritu, ale u STM32F4xx nejsou využity všechny)).
Nastavení priorit má výhodu v tom, že se vždy vykonává rutina přerušení s vyšší prioritou. Důležité je poznamenat, že čím nižší číslo priority, tím větší má přerušení prioritu. Tedy pokud nastane přerušení A s prioritou 1 a po nějakém čase i přerušení B s prioritou 0, tak se rutina pro přerušení A pozastaví a pokračuje se v ní až po vykonání přerušení B. Naopak přerušení se stejnou nebo menší prioritou čekají na dokončení již probíhající rutiny přerušení.
Podívejme se na strukturu, kterou budeme využívat pro nastavení registrů (ta se nachází v souboru core_cm4.h):
typedef struct { __IO uint32_t ISER[8]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ uint32_t RESERVED0[24]; __IO uint32_t ICER[8]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ uint32_t RSERVED1[24]; __IO uint32_t ISPR[8]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ uint32_t RESERVED2[24]; __IO uint32_t ICPR[8]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ uint32_t RESERVED3[24]; __IO uint32_t IABR[8]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ uint32_t RESERVED4[56]; __IO uint8_t IP[240]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ uint32_t RESERVED5[644]; __O uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ } NVIC_Type;
ISER – Interrupt Set Enable Register
Pomocí tohoto registru povolujeme konkrétní přerušení. Každá periferie má své číslo přerušení (nebo i více pokud může generovat více přerušení), které najdeme v souboru stm32f4xx.h na řádcích 147 až 239. Takže například přerušení z časovače TIM7 má číslo 55. Pro povolení tohoto přerušení musíme nastavit 55. bit v registru ISER. Ten je řešen jako pole o velikosti 8x32 bit. Takže potřebujeme nastavit 55-32 = 23 bit v ISER[1].
NVIC->ISER[TIM7_IRQn >> 0x05] |= (0x01 << (TIM7_IRQn & 0x1F));
ICER - Interrupt Clear Enable Register
S tímto registrem se zachází úplně stejně jako s registrem ISER. Jeho funkce je opačná. Nastavením příslušeného bitu přerušení zakážeme. Například pro zakázání přerušení od časovače TIM7 napíšeme:
NVIC->ICER[TIM7_IRQn >> 0x05] |= (0x01 << (TIM7_IRQn & 0x1F));
ISPR - Interrupt Set Pending Register
Opět zacházení s registrem obdobné jako s předchozími registry. Díky tomuto registru mužeme „uměle“ generovat přerušení od libovolné periferie i když daná periferie, přerušení negenerovala.
ICPR - Interrupt Clear Pending Register
Opět zacházení s registrem obdobné jako s předchozími registry. Naopak od ISPR registru, pomocí tohoto registru můžeme rušit požadavky na přerušení. Takže pokud probíhá přerušení A a v pořadí čeká přerušení B, může pomocí tohoto registru přerušení B zrušit.
IABR - Interrupt Active bit Register
V tomto registru je vždy aktivní ten bit, jemuž odpovídá přerušení, které právě probíhá. Opět čtení nebo zápis je obdobný předchozím registrům.
IP - Interrupt Priority Register
Pomocí tohoto registru nastavujeme prioritu přerušení. I když se jedná o pole 240x8 bit, není celá šířka využita. Přerušeních je pouze 82 a prioritu lze nastavit jen 4 bity (horní 4 bity bytu, spodní 4 bity jsou ignorovány). Index pole odpovídá číslu přerušení. Takže pokud bychom chtěli dát nejnižší prioritu přerušení z časovače TIM7, napsali bychom:
NVIC->IP[TIM7_IRQn] = 0xF0;
Teď si rozblikáme diodu, ale nebudeme sledovat stav časovače. Místo toho povolíme přerušení a diodu budeme rozvěšet/zhášet v rutině přerušení.
K tomu použijeme příklad z minula. Nastavení GPIO je shodné (tedy piny 12, 13, 14 a 15 jako výstupní). Nastavení časovače je obdobné:
// Rreset casovace TIM7 RCC->APB1RSTR |= RCC_APB1RSTR_TIM7RST; RCC->APB1RSTR &= ~RCC_APB1RSTR_TIM7RST; // Povolení hodin pro casovac TIM7 RCC->APB1ENR |= RCC_APB1ENR_TIM7EN; // Nastaveni // Pozn.: casovac TIM6 je 16bit TIM7->CR1 |= TIM_CR1_ARPE; // Povoleni prednacteni auto-reload TIM7->ARR = 65523; // Auto-reload hodnota, pri ktere se ma citac resetovat TIM7->PSC = 1281; // Delicka hodinoveho signalu TIM7->EGR = TIM_EGR_UG; // Aktualizace (update event) TIM7->SR = 0x0000; // Vrácení status bitu na log 0 TIM7->DIER |= TIM_DIER_UIE; // povoleni generováni preruseni TIM7->CR1 |= TIM_CR1_CEN; // Spusteni casovace TIM6 // Povoleni preruseni NVIC->ISER[TIM7_IRQn >> 0x05] |= (0x01 << (TIM7_IRQn & 0x1F));
Změna je jen malá. Povolení generování signálu přerušení v registru TIM7_DIER. Dále je také potřeba povolit přerušení od časovače v registru NVIC. Tím jsme povolili generování přerušení. Vytváření funkcí, které se budou vykonávat při přerušení, je velice jednoduché. Stačí vytvořit funkci void s názvem přerušení, který najdeme v souboru startup_stm32f4xx.s (tam jsou vytvořeny všechny vektory přerušení) a vložit ji před funkci main:
void TIM7_IRQHandler(void) { if(TIM7->SR & TIM_SR_UIF) { if(GPIOD->ODR & GPIO_ODR_ODR_15) GPIOD->ODR &= ~GPIO_ODR_ODR_15; else GPIOD->ODR |= GPIO_ODR_ODR_15; TIM7->SR = 0x0000; // Vrácení status bitu na log 0 } }
Tato funkce se vykoná vždy, když nastane přerušení. To v našem případě nastane přibližně každou sekundu. Nesmíme zapomenou nulovat bity ve status registru TIM7_SR, jinak by další přerušení bylo generováno okamžitě po skončení prvního přerušení. Samotná funkce main pak vypadá stejně jako v minulém příkladě, jen smažeme obsah cyklu while.
while(1);
Dnes jsme si ukázali jak jednoduše povolit a nastavit přerušení od periferie. Příště si ukážeme jak povolit a nastavit externí přerušení. Tedy přerušení generována při náběžné nebo sestupné hraně na nějakém GPIO portu/pinu.
Projekt ke stažení ZDE.
Komentáře
my web blog ... Dr Extenda: http://marirea-penisului-ro.eu/drextenda.html