logo stV 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;

 

Blikáme diodou s přerušením

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

 

Závěr

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  

 
0 # Clarence 2018-12-22 16:51
I benefit from looking through your websites. Thank you!


my web blog ... Dr Extenda: http://marirea-penisului-ro.eu/drextenda.html
Odpovědět | Odpovědět citací | Citovat