W jaki sposób implementowane są procedury obsługi przerwań w CMSIS Cortex M0?


9

Mam zestaw LPC1114. W ciągu ostatnich kilku dni analizowałem implementację Cortex M0 w CMSIS, aby dowiedzieć się, jak to się dzieje. Do tej pory rozumiałem, w jaki sposób mapowane są wszystkie rejestry i jak mogę uzyskać do nich dostęp. Ale wciąż nie wiem, w jaki sposób są w nim realizowane przerwania. Wszystko, co wiem o przerwaniach w CMSIS, to pewne nazwy procedur obsługi przerwań wymienione w pliku startowym. I mogę pisać własne programy obsługi, pisząc po prostu funkcję C o tych samych nazwach, o których mowa w pliku startowym. To, co mnie dezorientuje, to to, że w instrukcji obsługi powiedziano, że wszystkie GPIO mogą być używane jako zewnętrzne źródła przerwań. Ale w pliku startowym wymienione są tylko 4 przerwania PIO. Więc powiedz mi:

  1. Jak mogę wdrożyć zewnętrzne procedury obsługi przerwań dla innych GPIO?
  2. Gdzie jest mapowana tablica przerwań w CMSIS?
  3. Jakie są główne różnice między NVIC a implementacją przerwań w AVR / PIC? (z wyjątkiem NVIC można zmapować w dowolnym miejscu we flashu)

Odpowiedzi:


14

Poniższe informacje stanowią uzupełnienie doskonałej odpowiedzi Igora.

Z perspektywy programowania C procedury obsługi przerwań są zdefiniowane w pliku cr_startup_xxx.c (np. Plik cr_startup_lpc13.c dla LPC1343). Wszystkie możliwe procedury obsługi przerwań są tam zdefiniowane jako SŁABY alias. Jeśli nie zdefiniujesz własnego XXX_Handler () dla źródła przerwań, zostanie użyta domyślna funkcja obsługi przerwań zdefiniowana w tym pliku. Linker określi, która funkcja ma zostać uwzględniona w końcowym pliku binarnym wraz z tabelą wektorów przerwań z cr_startup_xxx.c

Przykład przerwań GPIO z portów pokazano w plikach demo w gpio.c. Dla każdego portu GPIO jest jedno wejście przerwania na port GPIO. Każdy pojedynczy bit w porcie można włączyć / wyłączyć, aby wygenerować przerwanie na tym porcie. Jeśli potrzebujesz na przykład przerwań na portach PIO1_4 i PIO1_5, wówczas możesz włączyć poszczególne bity przerwań PIO1_4 i PIO1_5 w GPIO0IE. Kiedy twoja funkcja obsługi przerwań PIOINT0_Handler () zostanie uruchomiona, od Ciebie zależy, które z przerwań PIO1_4 lub PIO1_5 (lub obu) oczekują, odczytując rejestr GPIO0RIS i odpowiednio obsługując przerwanie.


10

(Należy pamiętać, że punkty 1 i 2 są szczegółami implementacyjnymi, a nie ograniczeniami architektonicznymi.)

  1. W większych układach NXP (takich jak LPC17xx) znajduje się kilka dedykowanych pinów przerwań (EINTn), które mają własną obsługę przerwań. Reszta GPIO musi korzystać z jednego wspólnego przerwania (EINT3). Następnie możesz sondować rejestr statusu przerwania, aby zobaczyć, które piny wywołały przerwanie.
  2. Nie znam się zbyt dobrze na LPC11xx, ale wygląda na to, że ma jedno przerwanie na port GPIO. Ponownie będziesz musiał sprawdzić rejestr stanu, aby dowiedzieć się, które konkretnie piny. Istnieje również do 12 pinów, które mogą działać jako źródła budzenia. Nie jestem pewien, czy potrafisz porwać je jako ogólne przerwania (tzn. Prawdopodobnie zostaną one uruchomione tylko w stanie uśpienia).
  3. Domyślna tabela obsługi jest umieszczona pod adresem 0 (który jest flashowany). Pierwszy wpis to wartość resetowania rejestru SP, drugi to wektor resetowania, a pozostałe to inne wyjątki i wektory przerwań. Kilka pierwszych (takich jak NMI i HardFault) jest naprawionych przez ARM, pozostałe są specyficzne dla układu. Jeśli chcesz zmienić wektory w czasie wykonywania, możesz ponownie przypisać do pamięci RAM (najpierw musisz skopiować tabelę). W LPC11xx ponowne mapowanie jest ustawione na początku SRAM (0x10000000), inne układy mogą być bardziej elastyczne.
  4. NVIC jest zoptymalizowany pod kątem wydajnej obsługi przerwań:
    • programowalny poziom priorytetu 0-3 dla każdego przerwania. Przerwanie o wyższym priorytecie blokuje te o niższym priorytecie (zagnieżdżanie). Realizacja niższego priorytetu jest wznawiana po zakończeniu przerwania o wyższym priorytecie.
    • automatyczne układanie w stos stanu procesora przy wejściu do przerwania; pozwala to na pisanie programów obsługi przerwań bezpośrednio w C i eliminuje potrzebę owijania asemblera.
    • łączenie łańcuchów: zamiast odskakiwania i ponownego naciskania stanu, następne oczekujące przerwanie jest obsługiwane natychmiast
    • późne nadejście: jeśli przerwanie o wyższym priorytecie nadejdzie podczas układania w stan procesora, zostanie ono wykonane natychmiast zamiast wcześniej oczekującego.

Ponieważ znasz PIC, zapoznaj się z tą aplikacją Uwaga: Migracja z mikrokontrolerów PIC do Cortex-M3

Chodzi o M3, ale większość punktów dotyczy także M0.


8

Odpowiedzi Austina i Igora są wystarczająco szczegółowe. Chcę jednak odpowiedzieć na to w inny sposób, być może uznasz to za pomocne.

LPC11xx (Cortex-M0) ma 4 poziomy dla pinów GPIO, wszystkie piny od GPIO0.0 do GPIO0.n mają ten sam numer przerwania, a wszystkie piny od GPIO3.0 do GPIO3.m mają ten sam numer przerwania.

Istnieje sześć kroków, aby zainicjować przerwanie GPIO w LPC11xx

  1. Skonfiguruj funkcję pin, modyfikując rejestry bloków połączeń pinowych.
  2. Ustaw kierunek pinów, modyfikując rejestr kierunku danych GPIO (wartość domyślna to wejście).
  3. Skonfiguruj przerwanie dla każdego pojedynczego pinu, musisz przejść do rejestru GPIOnIE maski przerwań GPIO i ustawić bit (odpowiadający pinowi) logikę 1.
  4. Skonfiguruj przerwanie dla zbocza narastającego lub opadającego albo obu, modyfikując rejestry wykrywania przerwań GPIO GPIOnIBE i GPIOnIS.
  5. Włącz źródło przerwania PIO_0 / PIO_1 / PIO_2 / PIO_3 w zagnieżdżonym sterowaniu przerwaniami wektorowymi za pomocą funkcji CMSIS.
  6. Ustaw priorytet przerwania za pomocą funkcji CMSIS.

Implementacje kodu. Potrzebujesz dwóch funkcji: jedna inicjuje 6 powyższych kroków, a druga to moduł obsługi przerwań, który musi mieć taką samą nazwę jak moduł obsługi zdefiniowany w startup_LPC11xx.spliku kodów uruchamiania . Nazwy są od PIOINT0_IRQHandlerdo PIOINT3_IRQHandler. Jeśli używasz innej nazwy, musisz zmienić nazwy w pliku startowym.

/*Init the GPIO pin for interrupt control */
void GPIO_Init(){
    LPC_IOCON-> =..              //Pin configuration register
    LPC_GPIO1->FIODIR = ...      //GPIO Data direction register
    LPC_GPIO1->FIOMASK = ..      //GPIO Data mask register - choose  the right pin
    LPC_GPIO1->GPIOnIE = ..      //Set up falling or rising edge 
    NVIC_EnableIRQ(PIO_1);       //Call API to enable interrupt in NVIC
    NVIC_SetPriority(PriorityN); //Set priority if needed
}


/*Must have the same name as listed in start-up file startup_LPC11xx.s */
void PIOINT1_IRQHandler(void){
   //Do something here
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.