{"id":6295,"date":"2019-07-05T11:50:31","date_gmt":"2019-07-05T09:50:31","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=6295"},"modified":"2023-08-10T15:21:47","modified_gmt":"2023-08-10T13:21:47","slug":"na-poczatku-byl-chaos-czyli-o-tablicy-wektorow-przerwan-w-arm-cortex-m3-i-m4","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/na-poczatku-byl-chaos-czyli-o-tablicy-wektorow-przerwan-w-arm-cortex-m3-i-m4\/","title":{"rendered":"Na pocz\u0105tku by\u0142 chaos. Czyli o tablicy wektor\u00f3w przerwa\u0144 w ARM Cortex M3 i M4"},"content":{"rendered":"\n<p>Pocz\u0105tki zwykle bywaj\u0105 trudne. Nie inaczej jest ze zrozumieniem zasad wykonywania programu na mikrokontrolerze przez osob\u0119 bez wykszta\u0142cenia i do\u015bwiadczenia w tym konkretnym kierunku. Dawno temu, kiedy jako student automatyki stawia\u0142em pierwsze kroki w \u015bwiecie system\u00f3w wbudowanych wiele aspekt\u00f3w stanowi\u0142o na po\u0142y techniczn\u0105, na po\u0142y mitologiczn\u0105 zagadk\u0119.<\/p>\n\n\n\n<p>W czasie studi\u00f3w wyt\u0142umaczono mi bowiem bardzo og\u00f3lne podstawy: funkcja main, p\u0119tla niesko\u0144czona, przerwania. Jednak nie wspomniano ni s\u0142owem o tym jak i dlaczego wykonywanie programu opiera si\u0119 o funkcj\u0119 main, ani te\u017c w jaki spos\u00f3b platforma mapuje konkretne zdarzenie (przerwanie) na konkretn\u0105 funkcj\u0119 obs\u0142ugi.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Na pocz\u0105tku by\u0142 chaos<\/h2>\n\n\n\n<p>Niczym w Mitologii Jana Parandowskiego, wszystko bierze sw\u00f3j pocz\u0105tek w chaosie. Aby jednak wspomniany chaos sta\u0142 si\u0119 troch\u0119 bardziej przyst\u0119pny dla zwyk\u0142ego \u015bmiertelnika to przyjmijmy, \u017ce zaczyna si\u0119 on zawsze pod pewnym (roboczo przyjmijmy, \u017ce tym samym) adresem w pami\u0119ci programu.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wektor resetu<\/h2>\n\n\n\n<p>Tym sposobem dochodzimy do tzw. wektora resetu. Jest to adres w pami\u0119ci programu, od kt\u00f3rego zaczyna si\u0119 jego wykonywanie. Adres ten mogliby\u015bmy uczyni\u0107 sta\u0142ym i niezmiennym (np. przyjmuj\u0105cym warto\u015b\u0107 zero), jednak takie podej\u015bcie ograniczy mocno swobod\u0119 budowania pliku wykonywalnego. Istnieje jeszcze szereg innych adres\u00f3w, kt\u00f3re nale\u017ca\u0142oby zna\u0107 i uczyni\u0107 niezmiennymi.\u00a0 Na potrzeby dalszych rozwa\u017ca\u0144 przyjmijmy, \u017ce adres ten jest nam znany po zbudowaniu pliku wykonywalnego.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Przerwania<\/h2>\n\n\n\n<p>Obs\u0142uga przerwania r\u00f3wnie\u017c opiera si\u0119 o wykonywanie konkretnego fragmentu kodu w odpowiedzi na zaistnia\u0142e zdarzenie. W systemie takich zdarze\u0144 mo\u017cemy mie\u0107 kilkana\u015bcie\/kilkadziesi\u0105t. W momencie zaistnienia zdarzenia nale\u017cy sprawnie prze\u0142\u0105czy\u0107 kontekst z wykonywania g\u0142\u00f3wnego programu, na obs\u0142ug\u0119 przerwania.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wyj\u0105tki procesora<\/h2>\n\n\n\n<p>Do mitologicznego Tartaru &#8211; uosabianego w tym przypadku przez Hard Fault\/Bus\/Mem Manage Handler, mo\u017cemy zosta\u0107 zepchni\u0119ci na skutek szeregu niekorzystnych zdarze\u0144: odwo\u0142ania do adresu zerowego, wykonania nieznanej instrukcji przez CPU, pr\u00f3by dost\u0119pu do chronionego\/niedost\u0119pnego adresu. W ka\u017cdym z tych przypadk\u00f3w nieszcz\u0119sny program jest spychany w jedn\u0105 z czarnych otch\u0142ani. Kolejny ju\u017c raz nale\u017cy wiedzie\u0107, gdzie owa czarna dziura (pod jakim adresem) si\u0119 znajduje.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Adres pocz\u0105tku stosu<\/h2>\n\n\n\n<p>Ostatnim elementem naszej mityczno-mitologicznej uk\u0142adanki jest stos. W pierwszym kroku wykonywania programu adres (pocz\u0105tku) stosu powinien zosta\u0107 za\u0142adowany do sprz\u0119towego rejestru, pe\u0142ni\u0105cego funkcj\u0119 wska\u017anika stosu. St\u0105d warto\u015b\u0107 ta powinna by\u0107 znana jeszcze przed uruchomieniem programu.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Tablica wektor\u00f3w przerwa\u0144<\/h2>\n\n\n\n<p>Czysto hipotetycznie wszystkie w\/w warto\u015bci mogliby\u015bmy uzna\u0107 za sta\u0142e w ramach jednej platformy\/mikrokontrolera\/rodziny mikrokontroler\u00f3w.<\/p>\n\n\n\n<p>Dla przyk\u0142adu:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pocz\u0105tek programu m\u00f3g\u0142by by\u0107 zawsze umieszczony pod adresem 0x00000000,<\/li>\n\n\n\n<li>funkcje obs\u0142ugi przerwa\u0144 mogliby\u015bmy rozmie\u015bci\u0107 odpowiednio od adresu 0x00001000 co 100 bajt\u00f3w,<\/li>\n\n\n\n<li>podobnie z rozmieszczeniem funkcji obs\u0142ugi wyj\u0105tk\u00f3w<\/li>\n\n\n\n<li>pocz\u0105tek stosu m\u00f3g\u0142by za\u015b znale\u017a\u0107 si\u0119 zawsze na ko\u0144cu dost\u0119pnego nam obszaru pami\u0119ci RAM<\/li>\n<\/ul>\n\n\n\n<p>Odnosz\u0105c si\u0119 do bardziej \u017cyciowego przyk\u0142adu mogliby\u015bmy powiedzie\u0107, \u017ce tablica wektor\u00f3w przerwa\u0144 jest swoistym spisem tre\u015bci w ksi\u0105\u017cce (w naszym przypadku ksi\u0105\u017ck\u0105 jest kompletny plik wykonywalny).<\/p>\n\n\n\n<p>Taka mocno usztywniona wizja pliku wykonywalnego ogranicza swobod\u0119 przy jego tworzeniu. Co je\u017celi funkcje obs\u0142ugi przerwa\u0144 nie zmieszcz\u0105 si\u0119 we wspomnianych 100 bajtach? Albo z jakiego\u015b powodu chcieliby\u015bmy wykorzysta\u0107 koniec pami\u0119ci RAM do przechowywania innych danych? Wracaj\u0105c do przyk\u0142adu z ksi\u0105\u017ck\u0105 &#8211; to tak jakby ka\u017cdy rozdzia\u0142 w ksi\u0105\u017cce mia\u0142 si\u0119 rozpocz\u0105\u0107 na wcze\u015bniej ustalonej stronie. Ustalonej przed napisaniem tre\u015bci ksi\u0105\u017cki.<\/p>\n\n\n\n<p>Potrzebujemy wi\u0119c rozwi\u0105zania, kt\u00f3re zapewni nam z jednej strony elastyczno\u015b\u0107 w rozmieszczaniu poszczeg\u00f3lnych funkcji w pliku wykonywalnym, a z drugiej pozwoli na \u0142atwe ich znalezienie.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Tablica wektor\u00f3w przerwa\u0144 w ARM Cortex M3\/M4<\/h2>\n\n\n\n<p>W przypadku mikrokontroler\u00f3w z rdzeniem ARM Cortex M3\/M4 tablic\u0119 wektor\u00f3w przerwa\u0144 zorganizowano w spos\u00f3b nast\u0119puj\u0105cy:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>umieszczono j\u0105 w pami\u0119ci programu pod adresem (domy\u015blnym) 0x08000000,<\/li>\n\n\n\n<li>zafiksowano w niej kolejno\u015b\u0107 wyst\u0119powania poszczeg\u00f3lnych warto\u015bci (adres\u00f3w): pocz\u0105tku stosu, wektora resetu, funkcji obs\u0142ugi poszczeg\u00f3lnych przerwa\u0144<\/li>\n\n\n\n<li>same warto\u015bci (adresy) obliczane s\u0105 na etapie budowania pliku wynikowego i umieszczane w tablicy pod konkretnym offsetem<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image aligncenter wp-image-7571 size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2018\/11\/WektorPrzerwa\u0144jpg-1.jpg\"><img decoding=\"async\" width=\"362\" height=\"753\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2018\/11\/WektorPrzerwa\u0144jpg-1.jpg\" alt=\"Tablica wektor\u00f3w przerwa\u0144\" class=\"wp-image-7571\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2018\/11\/WektorPrzerwa\u0144jpg-1.jpg 362w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2018\/11\/WektorPrzerwa\u0144jpg-1-144x300.jpg 144w\" sizes=\"(max-width: 362px) 100vw, 362px\" \/><\/a><figcaption class=\"wp-element-caption\">Ryc. 1 Przyk\u0142adowy wygl\u0105d tablicy wektor\u00f3w przerwa\u0144 wraz z warto\u015bciami offset\u00f3w.<\/figcaption><\/figure>\n\n\n\n<p>Tak opracowane rozwi\u0105zanie jest proste, a jednocze\u015bnie wystarczaj\u0105co elastyczne. Nasz spis tre\u015bci umieszczony jest na pocz\u0105tku ksi\u0105\u017cki (adres 0x08000000), a poszczeg\u00f3lne rozdzia\u0142y zaczynaj\u0105 si\u0119 pod zmiennymi (per ksi\u0105\u017cka) numerami stron. Oczywi\u015bcie kolejno\u015b\u0107 rozdzia\u0142\u00f3w jest zawsze taka sama, ale to ju\u017c nie blokuje nas przed przygotowaniem optymalnego pliku wykonywalnego.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Uruchamianie programu<\/h2>\n\n\n\n<p>Z chwil\u0105 uruchomienia mikrokontrolera wspomniany wcze\u015bniej spis tre\u015bci, jest wykorzystywany do pobrania i zainicjowania dw\u00f3ch kluczowych warto\u015bci:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>adresu procedury resetu (reset handler&#8217;a)<\/li>\n\n\n\n<li>wska\u017anika stosu, znajduj\u0105cego si\u0119 pod offsetem 0x0000 w tablicy wektor\u00f3w przerwa\u0144<\/li>\n<\/ul>\n\n\n\n<p>Adres reset handler&#8217;a \u0142adowany jest sprz\u0119towo do rejestru licznika programu (PC). Z kolei adres pocz\u0105tku stosu \u0142adowany jest ju\u017c wewn\u0105trz procedury resetu. W ostatnim kroku procedury resetu wywo\u0142ywana jest funkcja main().<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full wp-image-7670\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/07\/Reset.jpg\"><img decoding=\"async\" width=\"766\" height=\"212\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/07\/Reset.jpg\" alt=\"Start programu\" class=\"wp-image-7670\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/07\/Reset.jpg 766w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/07\/Reset-300x83.jpg 300w\" sizes=\"(max-width: 766px) 100vw, 766px\" \/><\/a><figcaption class=\"wp-element-caption\">Ryc.2 Warto\u015bci wyci\u0105gane z tablicy wektor\u00f3w przy starcie programu wraz przyk\u0142adowym Reset Handler&#8217;em<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Obs\u0142uga przyk\u0142adowego przerwania<\/h2>\n\n\n\n<p>W momencie wyst\u0105pienia przerwania na platformie wykorzystuj\u0105cej rdze\u0144 Cortex M3\/M4 nast\u0119puje automatyczny, sprz\u0119towy zapis aktualnego kontekstu na stosie. W ramach kontekstu zapisywany jest rejestr statusu (xPSR), licznik programu (PC), rejestr powrotu (LR) wraz z czterema pierwszymi rejestrami og\u00f3lnego przeznaczenia (R0-R3) oraz wska\u017anikiem stosu (R12\/SP).<\/p>\n\n\n\n<p>Nast\u0119pnie wykonywany jest skok do konkretnej procedury obs\u0142ugi przerwania. A wi\u0119c z tablicy wektor\u00f3w, spod konkretnego offsetu, wyci\u0105gany jest adres procedury obs\u0142ugi danego przerwania i \u0142adowany do licznika programu (PC). Po obs\u0142u\u017ceniu przerwania, kontekst jest odtwarzany ze stosu, co powoduje za\u0142adowanie licznika programu adresem miejsca, z kt\u00f3rego nast\u0105pi\u0142 skok do przerwania.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full wp-image-7668\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/07\/Kontekst-1.jpg\"><img decoding=\"async\" width=\"803\" height=\"643\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/07\/Kontekst-1.jpg\" alt=\"Procedura skoku do obs\u0142ugi przerwania z funkcji main() z zapisaniem kontekstu na stosie\" class=\"wp-image-7668\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/07\/Kontekst-1.jpg 803w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/07\/Kontekst-1-300x240.jpg 300w\" sizes=\"(max-width: 803px) 100vw, 803px\" \/><\/a><figcaption class=\"wp-element-caption\">Ryc. 3 Procedura skoku do obs\u0142ugi przerwania z funkcji main() z zapisaniem kontekstu na stosie<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Relokowanie tablicy wektor\u00f3w przerwa\u0144<\/h2>\n\n\n\n<p>Wart\u0105 wzmianki funkcjonalno\u015bci\u0105 procesor\u00f3w rodziny Cortex M3\/M4, jest mo\u017cliwo\u015b\u0107 relokowania tablicy wektor\u00f3w przerwa\u0144 podczas pracy programu. S\u0142u\u017cy do tego rejestr VTOR (Vector Table Offset Register).<\/p>\n\n\n\n<p>Nale\u017cy przy tym bezwzgl\u0119dnie pami\u0119ta\u0107, i\u017c 7 najm\u0142odszych bit\u00f3w wspomnianego rejestru nie mo\u017ce by\u0107 u\u017cyte. St\u0105d adres tablicy wektor\u00f3w musi by\u0107 odpowiednio wyr\u00f3wnany.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full wp-image-7593\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2018\/11\/VTOR_ARM.jpg\"><img decoding=\"async\" width=\"854\" height=\"386\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2018\/11\/VTOR_ARM.jpg\" alt=\"Struktura rejestru VTOR dla Cortex M3\/M4\" class=\"wp-image-7593\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2018\/11\/VTOR_ARM.jpg 854w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2018\/11\/VTOR_ARM-300x136.jpg 300w\" sizes=\"(max-width: 854px) 100vw, 854px\" \/><\/a><figcaption class=\"wp-element-caption\">Ryc. 4 Struktura rejestru VTOR dla Cortex M3\/M4<\/figcaption><\/figure>\n\n\n\n<p>Domy\u015blnie tablica wektor\u00f3w przerwa\u0144 umieszczona jest pod adresem 0x08000000 i warto\u015b\u0107 rejestru VTOR okre\u015bla offset wzgl\u0119dem pocz\u0105tku pami\u0119ci (adres 0x00000000) pod jakim w danym momencie znajduje si\u0119 wspomniana tablica. A skoro mapa pami\u0119ci jest ci\u0105g\u0142a to mo\u017cemy umie\u015bci\u0107 nasze wektory zar\u00f3wno w pami\u0119ci programu, jak i w RAMie. W tym celu po zapisaniu nowej tablicy wektor\u00f3w w pami\u0119ci ustawiamy rejestr VTOR na adres, odpowiadaj\u0105cy pocz\u0105tkowi tablicy.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full wp-image-7590\"><img decoding=\"async\" width=\"636\" height=\"564\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/06\/VTOR.jpg\" alt=\"U\u017cycie rejestru VTOR, aby wskazywa\u0142a na now\u0105 tablic\u0119 wektor\u00f3w\" class=\"wp-image-7590\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/06\/VTOR.jpg 636w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/06\/VTOR-300x266.jpg 300w\" sizes=\"(max-width: 636px) 100vw, 636px\" \/><figcaption class=\"wp-element-caption\">Ryc. 5 Przyk\u0142adowe u\u017cycie rejestru VTOR celem wskazania innej tablicy wektor\u00f3w<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Przyk\u0142ad programistyczny<\/h2>\n\n\n\n<p>W za\u0142\u0105czonym, przyk\u0142adowym projekcie przyjrzymy si\u0119 w pierwszej kolejno\u015bci skryptowi linkera (STM32L432KCUx_FLASH.ld). Tam znajdziemy informacj\u0119 o rozmieszczeniu poszczeg\u00f3lnych modu\u0142\u00f3w w pami\u0119ci programu. W okolicach linii 76 znajdziemy wylistowane sekcje pami\u0119ci. Pierwsz\u0105 z umieszczonych w pami\u0119ci programu sekcji jest w\u0142a\u015bnie tablica wektor\u00f3w (isr_vector).<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full wp-image-7588\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/06\/Pocz\u0105tek-pami\u0119ci.jpg\"><img decoding=\"async\" width=\"380\" height=\"169\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/06\/Pocz\u0105tek-pami\u0119ci.jpg\" alt=\"Tablica wektor\u00f3w w pami\u0119ci programu\" class=\"wp-image-7588\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/06\/Pocz\u0105tek-pami\u0119ci.jpg 380w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/06\/Pocz\u0105tek-pami\u0119ci-300x133.jpg 300w\" sizes=\"(max-width: 380px) 100vw, 380px\" \/><\/a><figcaption class=\"wp-element-caption\">Ryc. 6 Lokalizacja tablicy wektor\u00f3w w pami\u0119ci programu<\/figcaption><\/figure>\n\n\n\n<p>Sama tablica jest zdefiniowana w pliku startup_stm32l432xx.s. Id\u0105c od szczytu tablicy wektor\u00f3w (g_pfnVectors) widzimy kolejno warto\u015bci reprezentowane przez adresy:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pocz\u0105tku stosu,<\/li>\n\n\n\n<li>wektora resetu,<\/li>\n\n\n\n<li>funkcji obs\u0142ugi wyj\u0105tk\u00f3w<\/li>\n\n\n\n<li>funkcji obs\u0142ugi przerwa\u0144.<\/li>\n<\/ul>\n\n\n\n<p>Jednej z nich przyjrzymy si\u0119 w szczeg\u00f3\u0142ach. Jest ni\u0105 umieszczony w linii 188 adres funkcji obs\u0142ugi przerwa\u0144 od Timer&#8217;a 1 (TIM1_UP_TIM16_Original).<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full wp-image-7589\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/06\/TablicaWektor\u00f3w.jpg\"><img decoding=\"async\" width=\"388\" height=\"705\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/06\/TablicaWektor\u00f3w.jpg\" alt=\"Struktura tablicy wektor\u00f3w\" class=\"wp-image-7589\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/06\/TablicaWektor\u00f3w.jpg 388w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/06\/TablicaWektor\u00f3w-165x300.jpg 165w\" sizes=\"(max-width: 388px) 100vw, 388px\" \/><\/a><figcaption class=\"wp-element-caption\">Ryc. 7 Struktura tablicy wektor\u00f3w przerwa\u0144<\/figcaption><\/figure>\n\n\n\n<p>Na marginesie. Nie musimy definiowa\u0107 od razu wszystkich funkcji obs\u0142ugi przerwa\u0144. Zamiast tego mo\u017cemy pos\u0142u\u017cy\u0107 si\u0119 sprytn\u0105 konstrukcj\u0105 wykorzystuj\u0105c\u0105 adnotacj\u0119 .weak oraz alias mi\u0119dzy tak utworzonym symbolem (nazw\u0105 funkcji), a domy\u015blnym, wsp\u00f3lnym dla wszystkich niewykorzystywanych przerwa\u0144,\u00a0handlerem (Default_Handler).<\/p>\n\n\n\n<p>Tak przygotowana &#8222;s\u0142aba&#8221; funkcja mo\u017ce by\u0107 w dowolnym momencie zdefiniowana w innym pliku \u017ar\u00f3d\u0142owym i ju\u017c jako &#8222;silny&#8221; symbol, u\u017cyta zamiast domy\u015blenia handlera. Takie rozwi\u0105zanie ma t\u0119 zalet\u0119, \u017ce nie wymaga modyfikowania kodu \u017ar\u00f3d\u0142owego tabeli wektor\u00f3w oraz usuwania domy\u015blnego handlera. Po prostu definiujemy funkcj\u0119 obs\u0142ugi o nazwie takiej samej jak uprzednio u\u017cywana &#8222;s\u0142aba&#8221; funkcja i wszystkim zajmuj\u0119 si\u0119 ju\u017c linker.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter is-resized size-full wp-image-7661\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/07\/Weak.jpg\"><img decoding=\"async\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/07\/Weak.jpg\" alt=\"S\u0142aby handler\" class=\"wp-image-7661\" width=\"692\" height=\"75\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/07\/Weak.jpg 692w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/07\/Weak-300x33.jpg 300w\" sizes=\"(max-width: 692px) 100vw, 692px\" \/><\/a><figcaption class=\"wp-element-caption\">Ryc. 8 Przyk\u0142ad weak handlera dla przerwania od modu\u0142u RTC<\/figcaption><\/figure>\n\n\n\n<p>Na potrzeby przyk\u0142adu utworzymy jeszcze dwie, prawie identyczne tablice wektor\u00f3w. Pierwsz\u0105 z nich umie\u015bcimy na ko\u0144cu u\u017cytego obszaru w pami\u0119ci FLASH (g_pfnVectorsRelocatedFlash, linia 254 w pliku&nbsp;stm32l432xx.s). Drug\u0105 za\u015b umie\u015bcimy na pocz\u0105tku pami\u0119ci RAM (g_pfnVectorsRelocatedRAM, linia 361 w pliku stm32l432xx.s).<\/p>\n\n\n\n<p>Wszystkie trzy tablice wektor\u00f3w s\u0105 praktycznie identyczne. R\u00f3\u017cni\u0105 si\u0119 tylko jednym, subtelnym elementem &#8211; adresem funkcji obs\u0142ugi przerwa\u0144 od Timer&#8217;a 1. S\u0105 to kolejno funkcje:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em> TIM1_UP_TIM16_Original <\/em>dla tablicy wektor\u00f3w na pocz\u0105tku FLASH&#8217;a<\/li>\n\n\n\n<li><em> TIM1_UP_TIM16_RelocatedFlash <\/em>dla tablicy wektor\u00f3w na ko\u0144cu FLASH&#8217;a<\/li>\n\n\n\n<li><em>TIM1_UP_TIM16_RelocatedRAM<\/em> dla tablicy wektor\u00f3w umieszczonej w pami\u0119ci RAM<\/li>\n<\/ul>\n\n\n\n<p>Wspomniane funkcje obs\u0142ugi przerwa\u0144 umieszczone s\u0105 jedna za drug\u0105 w pliku stm32l4xx_it.c.<\/p>\n\n\n\n<p>Z kolei wewn\u0105trz p\u0119tli niesko\u0144czonej w funkcji main.c nast\u0119puje manipulacja zawarto\u015bci\u0105 rejestru VTOR. Z pomoc\u0105 zmiennej globalnej funkcja main dokonuje podmiany tablicy wektor\u00f3w &#8211; co skutkuje cyklicznym wywo\u0142aniem kolejnych funkcji obs\u0142ugi przerwa\u0144 Timer&#8217;a 1, zdefiniowanych w pliku stm32f0xx_it.c.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full wp-image-7587\"><img decoding=\"async\" width=\"624\" height=\"694\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/06\/main.jpg\" alt=\"Aktualizacja rejestru VTOR z poziomu kodu \u017ar\u00f3d\u0142owego (main.c)\" class=\"wp-image-7587\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/06\/main.jpg 624w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/06\/main-270x300.jpg 270w\" sizes=\"(max-width: 624px) 100vw, 624px\" \/><figcaption class=\"wp-element-caption\">Ryc. 9 Aktualizacja rejestru VTOR z poziomu kodu \u017ar\u00f3d\u0142owego (main.c)<\/figcaption><\/figure>\n\n\n\n<p>Tak wi\u0119c w trakcie dzia\u0142ania programu mo\u017cemy szybko i sprawnie podmieni\u0107 wszystkie procedury obs\u0142ugi przerwa\u0144\/wyj\u0105tk\u00f3w.<\/p>\n\n\n\n<p>Widocznym efektem jest wywo\u0142ywanie kolejno wspomnianych trzech funkcji obs\u0142ugi przerwania przy przepe\u0142nieniu Timer&#8217;a &#8211; dw\u00f3ch z pami\u0119ci programu i jednej z RAMu. Mechanizm, pozwalaj\u0105cy na przemieszczanie tablicy wektor\u00f3w jest to szczeg\u00f3lnie przydatny przy wykonywaniu programu z pami\u0119ci RAM.<\/p>\n\n\n\n<p>Ale to ju\u017c temat na zupe\u0142nie inn\u0105 opowie\u015b\u0107&#8230;<\/p>\n\n\n\n<p><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2018\/11\/VTOR_NUCLEOL432.zip\" target=\"_blank\" aria-label=\" (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\">Pobierz przyk\u0142adowy projekt<\/a><\/p>\n\n\n\n<p>.223rem<\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;6295&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;5&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;5\\\/5 ( votes: 5)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;Na pocz\u0105tku by\u0142 chaos. Czyli o tablicy wektor\u00f3w przerwa\u0144 w ARM Cortex M3 i M4&quot;,&quot;width&quot;:&quot;139.5&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 139.5px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            5\/5 ( votes: 5)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>Pocz\u0105tki zwykle bywaj\u0105 trudne. Nie inaczej jest ze zrozumieniem zasad wykonywania programu na mikrokontrolerze przez osob\u0119 bez wykszta\u0142cenia i do\u015bwiadczenia &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/na-poczatku-byl-chaos-czyli-o-tablicy-wektorow-przerwan-w-arm-cortex-m3-i-m4\/\">Continued<\/a><\/p>\n","protected":false},"author":161,"featured_media":7752,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"image","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1314],"tags":[564,565,566,563,567,789,788,787,568],"class_list":["post-6295","post","type-post","status-publish","format-image","has-post-thumbnail","hentry","category-development-na-twardo","tag-arm","tag-cortex-m3","tag-cortex-m4","tag-embedded","tag-exception","tag-interrupt","tag-przerwanie","tag-vtor","tag-wyjatek","post_format-post-format-image"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/07\/blogersii-arm-cortex-2.jpg","category_names":["Development na twardo"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/6295"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/users\/161"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/comments?post=6295"}],"version-history":[{"count":2,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/6295\/revisions"}],"predecessor-version":[{"id":23412,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/6295\/revisions\/23412"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media\/7752"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media?parent=6295"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/categories?post=6295"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/tags?post=6295"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}