Vektory přerušení
Nejdříve připravíme pole vektorů přerušení. Procesor Cortex-M3 po resetu načte do registru SP (Stack Pointer) obsah paměti na adrese 0x00000000 a do registru PC (Program Counter) obsah paměti na adrese 0x00000004.
Jinými slovy, Cortex-M3 po resetu nastaví ukazatel vrcholu zásobníku na hodnotu, která je na adrese 0x00000000 a začne provádět instrukce od adresy, která je uložena na adrese 0x00000004. Musíme se tedy postarat o to, aby na těchto dvou adresách byly uloženy správné hodnoty. Současně bychom měli mít na následujících dvou slovech adresy rutin pro obsluhu výjimek NMI a Hard Fault. Vytvoříme minimální tabulku vektorů a umístíme ji na správné místo v paměti. K tomu nám poslouží zdrojový kód, který uložíme do souboru vector.c. Pro překlad a sestavení programu budeme používat překladač GNU GCC. Použité atributy odpovídají tomuto překladači a nemusí fungovat pro jiné překladače.
extern unsigned long _stack_top; static void handler_reset(void); static void handler_dummy(void); __attribute__ ((section("vectors"))) void (* const vectorTbl[])(void) = { (void (*)(void)) (&_stack_top), /* 00 - SP initial value */ handler_reset, /* 01 - Reset handler */ handler_dummy, /* 02 - NMI handler */ handler_dummy /* 03 - Hard fault handler */ };
První položkou tabulky vektorů je adresa, která bude při resetu umístěna do registru SP (Stack Pointer). Proměnná _stack_top je deklarovaná jako externí, její umístění v paměti získá linker z linkovacího skriptu, který podrobně rozebereme v další části. Adresu proměnné _stack_top musíme přetypovat, protože tabulka vektorů je deklarovaná jako pole ukazatelů na funkce. Atribut section sděluje linkeru, aby pole vektorů umístil do paměťové sekce vectors. Skutečné umístění sekce vectors v paměti je definováno v již zmíněném linkovacím skriptu.
Následuje adresa funkce pro obsluhu resetu, která bude po resetu procesoru dosazena do registru PC (Program Counter). Procesor začne po resetu provádět funkci handler_reset() a ukazatel zásobníku bude obsahovat adresu proměnné _stack_top.
Další dvě položky jsou adresy rutin pro obsluhu výjimek NMI a HardFault, obě výjimky pro jednoduchost obsloužíme jedinou funkcí. Protože procesor Cortex-M3 provádí při obsluze výjimky úklid registrů na zásobník a pro návrat z přerušení není potřeba speciální instrukce, nemusíme použít pro zápis rutin obsluhy přerušení žádný speciální atribut a můžeme je zapisovat jako normální funkce.
extern unsigned long _text_end; extern unsigned long _data_begin; extern unsigned long _data_end; extern unsigned long _bss_begin; extern unsigned long _bss_end; extern int main(void); void handler_reset(void) { unsigned long *src; unsigned long *trg; /* copy initialized data values from Flash to RAM */ src = &_text_end; for (trg = &_data_begin; trg < &_data_end; ) { *trg++ = *src++; } /* clear unitialized data */ for (trg = &_bss_begin; trg < &_bss_end; ) { *trg++ = 0; } /* call main() */ main(); }
Funkce handler_reset() pro obsluhu resetu je velmi jednoduchá. Provede kopírování počátečních hodnot inicializovaných proměnných z Flash paměti do RAM, potom nuluje neinicializované proměnné a nakonec zavolá hlavní program main(). Adresy konce paměťové sekce kódu (_text_end), začátku a konce sekce inicializovaných proměnných (_data_begin, _data_end) a sekce neinicializovaných proměnných (_bss_begin, _bss_end) jsou definovány také v linkovacím skriptu.
Obsluhu výjimek NMI a HardFault napíšeme velmi jednoduše pouze jako nekonečný cyklus. Pokud při běhu našeho programu dojde k některé z těchto dvou výjimek, procesor se bude točit v nekonečném cyklu ve funkci handler_dummy().
void handler_dummy(void) { while (1) { } }