Vaja 4: Izjeme in prekinitve
Namen tokratne vaje je, da se spoznate s konceptom izjem in prekinitev v STM32H7, prekinitvenim krmilnikom (NVIC) ter SysTick izjemo.
Izjeme in prekinitve v STM32H7
Kot ste spoznali na predavanjih, izjeme (angl. exceptions) in prekinitve (angl. interrupt) lahko prekinejo procesor pri trenutnem delovanju. Procesor ob izjemi ali prekinitvi začne izvajati prekinitveno servisni program (PSP), ki je za prispelo izjemo/prekinitev določen. Vsaka izmed izjem ali prekinitev ima svojo prioriteto. V primeru, da se hkrati pojavita dve izjemi/prekinitvi, se bo najprej servisirala prekinitev z višjo prioriteto. Prioritete so v ARM Cortex M7 označene s številkami, pri čemer nižja številka pomeni višjo prioriteto.
ARM uporablja izraz izjema (exception) kot nadpomenko za vse dogodke, ki prekinejo delovanje procesorja, prekinitve pa tretira kot eno izmed vrst izjem. ARM pozna sledeče tipe izjem:
Reset: Izjema reset se zgodi ob vklopu ali t.i. warm resetu, ki ga lahko dosežemo na primer s pritiskom črne tipke na razvojni plošči. Ta vrsta izjeme je vedno omogočena in ima prioriteto -3. Ko se pojavi izjema Reset se procesor ustavi, ne glede na to kaj izvaja v danem trenutku. Po deaktivaciji Reset izjeme, se izvede prekinitveni servisni program. Zadnji korak prekinitveno servisnega programa za reset je običajno klic uporabniškega programa.
NMI: NMI je kratica za non-maskable interrupt (prekinitev, ki je ni mogoče maskirati/onemogočiti). Ima najvišjo prioriteto (-2) izmed vseh vrst izjem, razen izjeme Reset.
HardFault: Ta izjema se pojavi ob napaki med procesiranjem kakšne izmed preostalih izjem/prekinitev. HardFault ima prioriteto -1. Iz servisiranja izjeme HardFault se običjano ni možno vrniti v normalno izvajanje programa - potreben je Reset.
MemManage: Izjema, ki se pojavi ob kršitvah pri dostopih do pomnilniških lokacij. Zgodi se na primer, ko skušamo dostopati do pomnilniške lokacije, za katero v trenutenm načinu delovanja nimamo pravic dostopa. Za pravice dostopa do pomnilnika običajno skrbi enota, ki ji rečeme Memory Protection Unit (MPU).
BusFault: Izjema, ki se zgodi ob napakah na podatkovnem ali ukaznem vodilu. Pojavijo se ob napakah ob dostopu do pomnilnika ali pomnilniško-preslikanih registrov vhodno-izhodnih naprav. Največkrat ob dostopu do naslovov, ki niso veljavni (niso lokacije v pomnilniku ali register V/I naprave).
UsageFault: Izjema, ki se pripeti zaradi nedefiniranega ukaza, neporavnanega naslova, nepravilnega stanja pri izvajanju ukazov ali napaki pri vračanju iz prekinitveno servisnega programa. Največkrat se nam bodo pripetili ob dostopu do neporavnanih naslovov (če recimo kazalec na 32-bitno spremenljivko nastavimo na vrednost, ki ni deljiva s 4) ali deljenju z 0.
SVCall: Izjema, ki jo proži ukaz SVC - supervisor call. Operacijski sistemi uporabljajo SVC ukaz za dostop do funkcij jedra OS (kernel) ali gonilnikov naprave.
PendSV: Izjema, ki se proži programsko. Običajno jih uporabljajo operacijski sistemi, da označijo začetek menjave konteksta (context switch).
SysTick: Izjema, ki jo generira sistemski časovnik, ko doseže vrednost 0. Ponavadi se jo uporablja v operacijskih sistemih za zagotavljanje delovanja časovnega rezinjenja (time sharing). V knjižnici STM HAL se jo uporablja tudi za zagotavljanje delovanja funkcije
HAL_Delay()
.Interrupt: Prekinitve so ostale izjeme, ki jih prožijo druge periferne naprave: naprave za komunikacijo kot npr. UART, GPIO pini preko naprave EXTI, in tako dalje. V nadaljevanju semestra bomo ob spoznavanju perifernih naprav, spoznali tudi prekinitve, ki jih te naprave lahko prožijo.
Oznake izjem/prekinitev
Vsaka izjema in prekinitev imajo dve številski oznaki, oznaka izjeme (exception number) in oznaka prekinitve (interrupt request number - IRQn). Tabela s številskimi oznakami je prikazana spodaj. Kot vidite imajo prekinitve prekinitveno oznako 0+, medtem ko imajo preostale izjeme negativne prekinitvene oznake. V programih se zaradi poenostavitve uporablja zgolj oznake prekinitve - IRQn.
Oznaka izjeme | Ime | Oznaka prekinitve (IRQn) |
---|---|---|
1 | Reset | / |
2 | NMI | -14 |
3 | HardFault | -13 |
4 | MemManage | -12 |
5 | BusFault | -11 |
6 | UsageFault | -10 |
11 | SVCall | -5 |
14 | PendSV | -3 |
15 | SysTick | -1 |
16+ | prekinitve | 0+ |
Izjeme 7-10 ter 12 in 13 so rezervirane. Oznaka 12 se običajno uporablja za razhroščevanje.
V tabeli spodaj so zapisane konstante za izjeme in prekinitve v STM32H750, ki so definirane v stm32h750xx.h
. Spodnje kostante bomo uporabljali v naših programih kadar bomo upravljali s prekinitvami in izjemami.
typedef enum
{
/****** Cortex-M Processor Exceptions Numbers *****************************************************************/
= -14, /*!< 2 Non Maskable Interrupt */
NonMaskableInt_IRQn = -13, /*!< 3 Cortex-M Hard Fault Interrupt */
HardFault_IRQn = -12, /*!< 4 Cortex-M Memory Management Interrupt */
MemoryManagement_IRQn = -11, /*!< 5 Cortex-M Bus Fault Interrupt */
BusFault_IRQn = -10, /*!< 6 Cortex-M Usage Fault Interrupt */
UsageFault_IRQn = -5, /*!< 11 Cortex-M SV Call Interrupt */
SVCall_IRQn = -4, /*!< 12 Cortex-M Debug Monitor Interrupt */
DebugMonitor_IRQn = -2, /*!< 14 Cortex-M Pend SV Interrupt */
PendSV_IRQn = -1, /*!< 15 Cortex-M System Tick Interrupt */
SysTick_IRQn /****** STM32 specific Interrupt Numbers **********************************************************************/
= 0, /*!< Window WatchDog Interrupt ( wwdg1_it, wwdg2_it) */
WWDG_IRQn = 1, /*!< PVD/AVD through EXTI Line detection Interrupt */
PVD_AVD_IRQn = 2, /*!< Tamper and TimeStamp interrupts through the EXTI line */
TAMP_STAMP_IRQn = 3, /*!< RTC Wakeup interrupt through the EXTI line */
RTC_WKUP_IRQn = 4, /*!< FLASH global Interrupt */
FLASH_IRQn = 5, /*!< RCC global Interrupt */
RCC_IRQn = 6, /*!< EXTI Line0 Interrupt */
EXTI0_IRQn = 7, /*!< EXTI Line1 Interrupt */
EXTI1_IRQn = 8, /*!< EXTI Line2 Interrupt */
EXTI2_IRQn = 9, /*!< EXTI Line3 Interrupt */
EXTI3_IRQn = 10, /*!< EXTI Line4 Interrupt */
EXTI4_IRQn = 11, /*!< DMA1 Stream 0 global Interrupt DMA1_Stream0_IRQn
Prekinitveni krmilnik NVIC
Prekinitve in večino izjem lahko programsko upravljamo, jih omogočimo ali onemogočimo ali pa jim določamo prioriteto. Za upravljanje skrbi prekinitveni krmilnik, angleško Nested Vectored Interrupt Controller oz. krajše NVIC. Prioriteto določamo na dveh nivojih, vsak izmed nivojem z možnimi vrednostmi med 0 in 15. V primeru, da hkrati prispe več prekinitvenih zahtev, se najprej preveri prioriteta na prvem nivoju. V primeru, da ima ena izmed prekinitev višjo prioriteto (nižjo vrednost) na prvem nivoju, se najprej servisira ta prekintitev. V primeru, da je prioriteta na prvem nivoju enaka pa se preveri drugi nivo. V primeru ujemanja tudi na drugem nivoju, se prva servisira prekinitev z nižjo oznako.
Programski vmesnik za delo z NVIC v STM HAL je zelo enostaven, zanimive so predvsem štiri funkcije:
HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
Funkcija nastavimo prioriteto na obeh nivojih za podano prekinitveno oznako. Primera:
(EXTI2_IRQn, 12, 11);
HAL_NVIC_SetPriority(SysTick_IRQn, 15, 0); HAL_NVIC_SetPriority
HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
Funkcija omogoči prekinitev za podano oznako. Primera:
(RTC_WKUP_IRQn);
HAL_NVIC_EnableIRQ(EXTI4_IRQn); HAL_NVIC_EnableIRQ
HAL_NVIC_DisableIRQ(IRQn_Type IRQn)
Funkcija onemogoči prekinitev za podano oznako. Primera:
(RTC_WKUP_IRQn);
HAL_NVIC_DisableIRQ(EXTI4_IRQn); HAL_NVIC_DisableIRQ
HAL_NVIC_SystemReset(void)
Funkcija programsko sproži reset.
Prekinitveno servisni programi
Če je izjema/prekinitev omogočena, NVIC pošlje oznako izjeme/prekinitve procesorju. Začetni naslovi (imena funckij v asm ali C) prekinitveno servisnih programov se nahajajo v t.i. vektorski tabeli, ki se nahaja v datoteki startup_stm32h750xbhx.s
. Začetni del vektorske tabele je prikazan spodaj. Prvi element vektorske tabele je skladovni kazalec (stack pointer), temu pa sledi seznam začetnih naslovov vseh prekinitveno servisnih programov.
g_pfnVectors:
word _estack
.word Reset_Handler
.
word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
.
*/
/* External Interrupts word WWDG_IRQHandler /* Window WatchDog */
.word PVD_AVD_IRQHandler /* PVD/AVD through EXTI Line detection */
.word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */
.word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */ .
Če se zgodi izjema HardFault
, se bo torej izvedla funkcija z imenom HardFault_Handler
. Ta je lahko napisana v zbirniku ali C-ju. Spodaj je podana tabela z oznakami in pripadajočimi imeni prekinitveno servisnih programov za izjeme in nekaj prekinitev.
Oznaka izjeme | Prekinitveno servisni program |
---|---|
Reset (ni oznake v HAL) | Reset_Handler |
NonMaskableInt_IRQn |
NMI_Handler |
HardFault_IRQn |
HardFault_Handler |
MemoryManagement_IRQn |
MemManage_Handler |
BusFault_IRQn |
BusFault_Handler |
UsageFault_IRQn |
UsageFault_Handler |
SVCall_IRQn |
SVC_Handler |
PendSV_IRQn |
PendSV_Handler |
SysTick_IRQn |
SysTick_Handler |
WWDG_IRQn |
WWDG_IRQHandler |
PVD_AVD_IRQn |
PVD_AVD_IRQHandler |
TAMP_STAMP_IRQn |
TAMP_STAMP_IRQHandler |
RTC_WKUP_IRQn |
RTC_WKUP_IRQHandler |
FLASH_IRQn |
FLASH_IRQHandler |
RCC_IRQn |
RCC_IRQHandler |
EXTI0_IRQn |
EXTI0_IRQHandler |
EXTI1_IRQn |
EXTI1_IRQHandler |
… | … |
Nekateri prekinitveno servisni programi so realizirani v zbirniku (na primer Reset_Handler
). Te najdemo v datoteki startup_stm32h750xbhx.s
. PSP-je v jeziku C pa najdemo v stm32h7xx_it.c
. Tam jih bomo pisali tudi mi.
Izjema SysTick
Kot že omenjeno zgoraj, izjemo SysTick
generira sistemski časovnik, ko doseže vrednost 0. Ponavadi se to uporablja v operacijskih sistemih za zagotavljanje delovanja časovnega rezinjenja (time sharing).
V funkciji HAL_Init()
, ki jo uporabljamo kadar se poslužujemo knjižnice STM HAL, se inicializira sistemski časovnik in omogoči izjemo SysTick
s prioriteto (15, 0)
in frekvenco 1kHz. Izjema se torej proži vsako milisekundo. Funkcija SysTick_Handler
se torej izvede vsako milisekundo. V stm32h7xx_it.c
najdemo realizacijo PSP-ja:
void SysTick_Handler(void)
{
();
HAL_IncTick
}
Klic HAL_IncTick();
omogoča delovanje funckiji HAL_Delay()
, ki smo jo uporabili na zadnji vaji.
Naloga 4
S pomočjo izjeme
SysTick
realizirajte funkcijoMy_Delay(int ms)
, ki deluje na enak način kotHAL_Delay(int ms)
, ki smo jo uporabljali na zadnjih vajah.V
while
zanki na koncumain()
funkcije realizirajte utripanje LED diod. Ob resetu naj LED utripajo z zakasnitvijo treh sekund:
while(1) {
// prizgi LED
(zakasnitev);
My_Delay// ugasni LED
(zakasnitev);
My_Delay}
- Če držimo gumb za več kot 1 sekundo, se zakasnitev pri utripanju LED diod zmanjša za tretjino. Iz treh sekund torej na dve sekundi, potem na 1,33 sekunde, in tako naprej.