Controlerul de întrerupere (ITC - InTerrupt Controller) din cadrul suportă întreruperile imbricate și le împarte în funcție de prioritate. Acest lucru înseamnă că, dacă am realizat acum un handler de întrerupere unele nu foarte importante (de exemplu un ceas), și dintr-o dată sa întâmplat întrerupe cu o prioritate mai mare, atunci întreruperea cronometrului va fi întreruptă și MC graba imediat să se ocupe mai mult. Și dacă prioritățile sunt aceleași (sau noua prioritate este mai mică decât cea actuală) - noua întrerupere va fi procesată numai după terminarea celei anterioare.
Și ce se întâmplă dacă două întreruperi apar simultan? Sau au apărut câteva întreruperi în timpul procesării mai importante? Și apoi primul va merge să se ocupe de unul cu prioritate mai mare. Și dacă prioritățile sunt la fel? Cititorul meticulos va întreba. Apoi va fi executată prima întrerupere, care este mai aproape de începutul tabelului de întrerupere.
In total sunt 29 întrerup vectori plus doi nenumerotate: RESET - resetare a sistemului și TRAP - întrerupere software-ul (echipa TRAP numit un ansamblu). Strict vorbind, există două tipuri de priorități - software (pe care le putem configura pentru fiecare întrerupere) și hardware - aceasta este doar ordinea vectorilor din tabela de întrerupere. Tabelul arată că cea mai mare prioritate hardware pentru memoria FLASH întrerupe, iar întreruperea de la I2C este în general lipsită de atenție, fiind la sfârșitul tabelului. Cu toate acestea, nimeni nu interzice să o facă cel mai important, stabilind o prioritate înaltă a programului - este mai important decât cea hardware.
Întreruperea programului TRAP scutește în general toate prioritățile programului. Indiferent de prioritatea întreruperii curente, executarea comenzii TRAP aici ne duce de asemenea la handler.
Pentru alte întreruperi, prioritatea poate fi stabilită prin intermediul registrelor ITC_SPR1 - ITC_SPR8.
Fiecărui vector i se alocă doi biți, ceea ce oferă 4 valori posibile. Dar, de fapt, prioritățile programului sunt doar 3:
Valoarea 10, care nu este în listă, corespunde priorității programului principal și nu o puteți instala. Da, și este inutil, dacă vă amintiți cum controlerul rupe coada de întrerupere: o întrerupere cu aceeași prioritate ca programul principal, nu va întrerupe niciodată.
După ce întreruperile necesare sunt activate și configurate, trebuie să activați în mod global întreruperea manipulării. În STM8, comanda de asamblare RIM (A pentru a dezactiva întreruperea - SIM) servește acestui scop. Și lucrează într-un mod foarte interesant. În loc să setați orice semn de activare / dezactivare a întreruperii (cum ar fi, de exemplu, în AVR), ele schimbă prioritatea codului care se execută în prezent. Comanda RIM stabilește o prioritate scăzută (10, aceeași care nu poate fi setată pentru întreruperi) și acum orice întrerupere poate întrerupe execuția programului. Și comanda SIM stabilește codul curent la cea mai înaltă prioritate și nici o întrerupere nu poate împiedica executarea codului.
Fiecare întrerupere are propriul pavilion, ceea ce indică faptul că evenimentul sa întâmplat și este timpul să fugim la manipulator. În STM8, aceste steaguri, de regulă, nu se resetează și trebuie să fie abandonate manual. În caz contrar, după ce ieșim de la handler, vom intra din nou în ea. Și ce? Steagul este ridicat, deci trebuie să rezolvați întreruperea.
Este adevărat că resetarea steagurilor în diferite cazuri este organizată în moduri diferite. Uneori trebuie să resetați singur pavilionul, câteodată - pentru a citi un registru specific (de exemplu, în întreruperea de la ADC - citiți rezultatul măsurătorii). Mai mult, atunci când descriu diferitele periferice, voi da șabloanele handler-ului și va specifica exact modul în care sunt reinițializate steagurile de întrerupere.
Atunci când întreruperea are loc în modul WFI, MK intră în dispozitivul de manipulare fără întârzieri inutile. În practică, timpul de intrare în întrerupere este redus cu 5 cicluri. aici:
Pentru a intra în WFI, trebuie să efectuați comanda de asamblare WFI (Captain Evidence ajutat activ la scrierea articolului)
Acum nucleul este oprit până la următoarea întrerupere. Doar nu uitați să setați această întrerupere și să o permiteți, altfel nu vă puteți trezi :)
Ei bine, acum avem o idee despre cum funcționează întreruperile în STM8 pe care le avem. Treceți fără probleme la EXT-EXT ernal I întrerupe, adică, întreruperea externă.
Dacă te uiți la tabela de întrerupere, poți vedea că 11 vectori sunt alocați EXTI.
Trei dintre aceștia au în numele o scrisoare care în mod suspicios amintește de numele portului (EXTIE / F, EXTIB.). Și restul de 8 cifre, nu mai puțin suspecte de numărul PIN. RM0031 risipi toate îndoielile și suspiciunile noastre au confirmat - din cauza faptului că vectorii de întrerupere nu sunt suficient de puternice pentru toate pinii, sistemul este organizat într-o manieră extrem de sofisticat.
Să începem de la început. Pentru fiecare pin, puteți activa sau dezactiva întreruperea individuală. Acest lucru se face, dacă vă amintiți partea anterioară, prin registrul Px_CR2, unde x este litera portului (înainte de aceasta trebuie să configurați pinul de intrare). Astfel, puteți activa întreruperile numai pentru acele necesare, iar restul nu va interfera.
Mai mult, pentru fiecare jumătate a portului (de exemplu, cele mai mici de 4 picioare și 4 de mare), putem alege unde să meargă semnal - pentru a întrerupe ace (EXTI0, EXTI1.), Sau porturi (EXTIB, EXTID.). În primul caz, obținem o întrerupere individuală pentru fiecare pin. Dar întreruperile din alte porturi vor veni aici. În al doilea caz, vom avea un singur handler pentru semnalul de la toate cele patru pini (dacă, desigur, permitem întreruperi de la toate), dar individual pentru fiecare port. Toate acestea sunt configurate prin registrele EXTI_CONF1 și EXTI_CONF2:
Fiecare bit corespunde cu jumătatea superioară sau cea inferioară a oricărui port. De exemplu, PDLIS (Port D Low Interrupt select) este responsabil pentru pinii 0..3 ai portului D.
Setarea unui bit la unul înseamnă că această jumătate a portului va da o întrerupere tuturor pinilor (de exemplu EXTID). Un zero în mod corespunzător înseamnă că fiecare dintre cele patru pini va da întreruperea lor de la grupul EXTI0..EXTI7.
În plus, pentru fiecare vector (și anume un vector, nu un singur pin), puteți configura frontul de-a lungul căruia va declanșa întreruperea. Acest lucru se face prin intermediul registrelor EXTI_CR1 - EXTI_CR4.
Fiecare întrerupere este atribuită câte doi biți fiecare. În consecință, sunt disponibile 4 moduri de întrerupere:
00 - declanșează trecerea de la 1 la 0 (marginea posterioară) și la un nivel scăzut. Adică, mânerul va fi apelat în mod continuu până când știftul este scăzut.
01 - La trecerea de la 0 la 1 (marginea de vârf)
10 - Din nou pe marginea posterioară, dar deja fără un nivel scăzut
11 - Pe ambele fronturi. Cu alte cuvinte, funcționează pentru orice schimbare de nivel.
Numele biților se formează simplu. P1IS - Pin 1 Sensibilitate la întrerupere, PBIS - Sensibilitate la întreruperea portului B și așa mai departe.
Steagurile întreruperilor (despre care am scris mai sus, că trebuie să fie abandonate) sunt în registrele EXTI_SR1 și EXTI_SR2.
Un bit pe vector. Și este necesar să le aruncați scriind o unitate (mai degrabă decât zgârietură, cum ar fi putut aștepta căpitanul).
Aici este un sistem atât de complicat. La început se pare că nu este foarte convenabil la locul de muncă. Dar, de fapt, acest inconvenient se manifestă numai dacă este necesar să prindă întreruperea dintr-o grămadă de picioare (și acest lucru nu este necesar de multe ori). În problemele obișnuite, nu există inconveniente speciale.
În ceea ce privește STM8S. atunci acolo cu întreruperi nu este atât de confuz (dar mai puține posibilități). De fapt, există doar întreruperi din porturi. Ca PCINT în AVR. Un semnal converge de la fiecare pin al acestui port. Puteți permite întreruperi individuale pentru fiecare pin, dar frontul este configurat numai pentru fiecare vector - adică pentru tot portul.
Să scriem un alt exemplu inutil pentru a înțelege cum funcționează în practică. Pentru o bază hardware, luați un exemplu din ultima parte (unde a fost un indicator) și atașați un alt buton la acesta. Fiecare buton va fi întrerupt.
În primul manipulator de întrerupere, vom fi într-o buclă (da, eu sunt ciudat) pentru a comuta cifrele de pe indicator. Și în procesorul celui de-al doilea (pe care o vom pune o prioritate mai mare) vom lumina intermitent LED-ul.
Dacă țineți apăsat primul buton, MK se blochează în dispozitivul de întrerupere și începe să sorteze numerele. Dar când apăsați al doilea buton, prima întrerupere va fi întreruptă mai important și LED-ul va începe să clipească. Dacă eliberați al doilea buton, indicatorul va continua să circule prin numerele de la cel pe care sa oprit - primul dispozitiv de întrerupere va continua să funcționeze. Pur și simplu, clar.
Mai întâi trebuie să configurați ace. Lăsați primul buton să stea pe D6, iar cel de-al doilea pe D7 (acești pini sunt conectați cel mai convenabil la butoanele de pe Pinboard).
Nu atingem registrul DDR - este deja resetat când MK pornește.
In EXTI_CONF1 registru (care distribuie semnalele la întreruperea bolduri / porturi) este de asemenea zero la început, atunci fiecare știft ne va da o întrerupere separat. Și pleacă.
Totuși, trebuie să configurați frontul, care va declanșa întreruperea. Butoanele apăsate aproape de pământ înseamnă că trebuie să prindem marginea posterioară, care corespunde valorii 2 (10):
În cele din urmă, este necesar să micșorați prioritatea întreruperii de la primul buton (D6), astfel încât o întrerupere de la cea de-a doua să întrerupă tratamentul primului:
Uită-te la tabelul de întrerupere - vectorul EXTI6 pe care-l avem la numărul 14 și corespunde grupului VECT14SPR. Valoarea 0, voi aminti, înseamnă "prioritate medie".
Ajustarea indicatorului și LED-ul a migrat din exemplele anterioare, așa că nu o voi aduce aici - vedeți codul sursă.
Rămâne doar să permită întreruperi la nivel global. Aceasta se face prin comanda de asamblare RIM
Pe aceasta, cu main () terminat și mergeți la operatorii de întrerupere. În IAR, manipulatorul este declarat în acest mod complicat:
Că de fiecare dată când nu urcați într-o foaie de date în spatele unui număr de vector necesar, este posibil să luați un defileu de la iostm8l151k6.h. La sfârșitul fișierului sunt scrise toate vectorii. Iată o bucată pentru un exemplu:
Oh, și de ce vectorul EXTI6 corespunde numărului 0x10 (16) și nu 14, ca în plăcuța din fișa tehnică? Și este ușor să țineți cont de primele două întreruperi - RESET și TRAP. Și în tabel se duc fără numere.
Și numele handler-ului este doar numele funcției și nu există cerințe speciale pentru aceasta.
Se pare că poate fi desemnat ca manipulatorul de întrerupere EXTI6:
Ventilatoare de scurtătate pot, dacă doresc, să o rotească într-o definiție:
Apoi, întreruperea va fi denumită:
Ce, vedeți, arată mai bine.
Operatorii de întrerupere din exemplul nostru vor fi aproape la fel (cu excepția faptului că primul comută numerele, iar al doilea luminează intermitent dioda). Prin urmare, voi arăta numai mâna primului buton:
Cred că totul ar trebui să fie clar. În cel de-al doilea handler, clipește LED-ul pe D5, până când apare un nivel ridicat pe pinul D7.
Aceasta pare să fie totul despre întreruperile externe.
În cele din urmă, vă voi spune despre o caracteristică interesantă numită Activation Level, care are de a face cu întreruperi.
Destul de des se pare că întregul program (bine, cu excepția configurației inițiale) este împrăștiat în jurul operatorilor de întrerupere. Și în flaunturile principale o patetică buclă infinită. Avem doar un astfel de caz. Și dacă dispozitivul nostru este alimentat de baterii și are nevoie de economisire a energiei, atunci între întreruperi va fi în modul de repaus și lăsați-l numai pentru a executa manipulatorii.
Tipii de la STM au decis să ne facă mai ușor să construim astfel de algoritmi și să zapilili biți AL. Acesta este situat în CFG_GCR (Configurare - Registru general de configurare)
Dacă vom scrie yedinichku, și du-te în modul de repaus, apoi, după MK se trezește întrerupe și să execute handler, el nu va primi în principal () și zbura imediat înapoi la culcare. Despre modurile de dormit, încă nu știm nimic, dar vreau să încerc această caracteristică.
Să înlocuim ciclul de stub la sfârșit cu această construcție:
HALT - este cel mai adânc modul de economisire a energiei, care se oprește ceasul, iar trezirea este posibilă numai prin întrerupere externă, întreruptă de o pereche de interfețe sau RTC.
După lansare, comportamentul programului nu sa schimbat în nici un fel, dar nu există o întârziere de buclă la sfârșitul principalului (), iar kernelul nu pierde energia pentru nimic. Imediat după terminarea procesorului, MK trece în modul de repaus.
Oh, și de ce vectorul EXTI6 corespunde numărului 0x10 (16) și nu 14, ca în plăcuța din fișa tehnică? Și este ușor să țineți cont de primele două întreruperi - RESET și TRAP. Și în tabel se duc fără numere. când pentru prima dată acest lucru a fugit - foarte mult matyukalsya ... Care este semnificația secretă a acestui lucru?
Un alt lucru care ucide este că în definițiile IAR, numeroși vectori sunt numiți astfel încât FIG va găsi întreruperea dorită. Pentru ADC, de exemplu. Este mai ușor să o declarați singur sau pur și simplu să scrieți un număr magic.
Apoi, "care sa ridicat prima data - asta si adidașii". și întreruperea de la I2C este, în general, lipsită de atenție de Aha, în tabelul dvs. nu există deloc :)
La început se pare că nu este foarte convenabil la locul de muncă. La început mi sa părut că a fost foarte pătată. Și atunci, totuși.
P.S. Mai avem nevoie să atribuim că nu ar încerca să repeta acest lucru pe seria STM8S :)
Tableta fixă (a continuat pe o altă pagină a fost, nu l-am capturat imediat), am scris despre STM8S, am luat chiar și adidații :)
Și sistemul pare ciudat, da. Se pare că au luat baza de la STM8S și au încercat să se extindă, dar au făcut-o destul de bizar. Ei bine, cel puțin am intrat de la orice picior