Forum: Mikrocontroller und Digitale Elektronik Interrupts mit Atmel Studio für SAM3X8E (Arduino Due)


von Andreas W. (andy_w)


Lesenswert?

Hallo,
das AVR-GCC Tutorial sagt mir da leider nicht genug. Soweit ich das 
verstanden habe, heißt die eigentliche Interruptroutine 
ISR(vectorname){}.

In meinem Fall sollen Interrupts von PIOC Bit 8 und 9 behandelt werden. 
Leider finde ich aber nicht heraus, wie der dazugehörige Vektorname 
heißt. Es ist weder PIOC_Handler noch PIOC_IRQn, die ich in sam3x8e.h 
finde, damit bekomme ich nur Fehlermeldungen, daß z.B. der PIOC_Handler 
schon definiert ist. Das ist tatsächlich auch der Fall, ich fand eine 
entsprechende Funktion, die schon vorgegeben war. Die fragt 
offensichtlich alle 32 möglichen Bits ab und macht dann abhängig davon 
was, was ich nicht mehr verstehe (in pio_handler.c ab Zeile 86, da mein 
Atmel Studio auf einem PC ohne Internetanschluß ist und ich nur fürs 
Intrernet einen alten Linux-PC benutze, ist es extrem umständlich, Code 
herüber zu kopieren).

Gibt es irgendwo ein kleines Beispielprojekt, das eine Interruptroutine 
benutzt, so daß man mal sehen kann, wie das gemacht wird? Es muß nicht 
unbedingt ein PIO-Interrupt sein, als Beispiel tut es z.B. auch ein 
Timer- oder UART-Interrupt oder sonst einer. Wichtig ist nur noch eine 
Angabe, wo man alle Vektornamen für die Interruptroutinen finden kann.

Gruß
Andy

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Hallo Andy,

Andreas W. schrieb:
> das AVR-GCC Tutorial sagt mir da leider nicht genug. Soweit ich das
> verstanden habe, heißt die eigentliche Interruptroutine
> ISR(vectorname){}.
Das gilt aber nur fuer AVR, nicht fuer die ARM-Controller von Atmel. Das 
hast du ja aber schon festgestellt. :)

Andreas W. schrieb:
> Leider finde ich aber nicht heraus, wie der dazugehörige Vektorname
> heißt.
Einen Vektornamen brauchst du nicht, da jeder IRQ seinen eigenen Handler 
hat, und es keine "universal" Funktion wie bei den AVR ist.

Andreas W. schrieb:
> ibt es irgendwo ein kleines Beispielprojekt, das eine Interruptroutine
> benutzt, so daß man mal sehen kann, wie das gemacht wird?
Das ist eigentlich total einfach (wie immer: nur dann, wenn man weiss 
wie :) )
1
#include "sam.h"
2
3
// Interrupt fuer Timer 0
4
void TC0_Handler(void)
5
{
6
  
7
}
8
9
int main(void)
10
{
11
    /* Initialize the SAM system */
12
    SystemInit();
13
14
    // Globaler Interrupt einschalten
15
    NVIC_EnableIRQ(TC0_IRQn);
16
    
17
    // Interrupt prioritaet setzen, niedrige nummer == hohe prio
18
    NVIC_SetPriority(TC0_IRQn, 6);
19
    
20
    // Globaler Interrupt abschalten
21
    //NVIC_DisableIRQ(TC0_IRQn);
22
23
    while (1) 
24
    {
25
        //TODO:: Please write your application code 
26
    }
27
}
Natuerlich muesste der Timer vorher noch konfiguriert werden :)

Andreas W. schrieb:
> Wichtig ist nur noch eine
> Angabe, wo man alle Vektornamen für die Interruptroutinen finden kann.
Der Name des IRQ-Handlers ergibt sich einfach aus dem Modul das du 
benutzen moechtest.
UART0 --> UART0_Handler --> UART0_IRQn
USART3 --> USART3_Handler --> USART3_IRQn
PIOC --> PIOC_Handler --> PIOC_IRQn
TC5 --> TC5_Handler --> TC5_IRQn
usw.

Hinweise
========

Watchdog
========
Du musst den Watchdog abschalten, da dieser standardmaessig 
eingeschaltet ist. ansonsten startet sich der Controller ca. alle 18 
sek. neu.

Heap/Stack
==========
In Atmel Studio findest du im Solution Explorer unter cmsis -> 
linkerScripts die Linker Scripte. Darin ist die max. groesse fuer Heap 
und Stack definiert! Die musst du vielleicht an deine Beduerfnisse 
anpassen.

UART/USART
==========
Im gegensatz zu den AVR hast du hier keine getrennten Interrupts zum 
senden und empfangen! Das heisst du musst in der ISR abfragen, was jetzt 
den IRQ ausgeloest hat.
1
void USART1_Handler(void)
2
{
3
  uint32_t irq_status = USART1->US_CSR;
4
5
  if( irq_status & empfangs_interrupt_bit_maske)
6
  {
7
8
  }
9
10
  if( irq_status & sende_interrupt_bit_maske)
11
  {
12
13
  }
14
}

Peripherie
==========
Atmel stellt dir Pointer auf Strukturen zur verfuegung. Da das bei den 
Timern am anfang vielleicht etwas verwirrend sein kann, hier eine 
aufschluesselung fuer die Timer:
1
TC0[0].TC_CHANNEL->TC_SR; // Timer 0 Status Register --> Timer 0 Kanal 0
2
TC0[1].TC_CHANNEL->TC_SR; // Timer 1 Status Register --> Timer 0 Kanal 1
3
TC0[2].TC_CHANNEL->TC_SR; // Timer 2 Status Register --> Timer 0 Kanal 2
4
5
TC1[0].TC_CHANNEL->TC_SR; // Timer 3 Status Register --> Timer 1 Kanal 0
6
TC1[1].TC_CHANNEL->TC_SR; // Timer 4 Status Register --> Timer 1 Kanal 1
7
TC1[2].TC_CHANNEL->TC_SR; // Timer 5 Status Register --> Timer 1 Kanal 2
8
9
TC2[0].TC_CHANNEL->TC_SR; // Timer 6 Status Register --> Timer 2 Kanal 0
10
TC2[1].TC_CHANNEL->TC_SR; // Timer 7 Status Register --> Timer 2 Kanal 1
11
TC2[2].TC_CHANNEL->TC_SR; // Timer 8 Status Register --> Timer 2 Kanal 2

Andere Peripherie ist nicht als "Array" angeordent, wie die Timer, aber 
der aufruf fuer die Strukturen ist analog.
1
PIOA->PIO_PSR;
2
PIOB->PIO_PSR;
3
PIOC->PIO_PSR;
4
PIOD->PIO_PSR;
5
6
TRNG->...
7
UART0->..
8
USART3->..
usw.

Du musst die Peripherie einzeln einschalten. das Kapitel im Datenlbatt 
dazu ist "29. Power Managment Controller (PMC)", Seite 535.
Die ID fuer ein Peripheriemodul findest du in Kapitel "11. Peripherals", 
Seite 47.

PIO - Pins setzen
=================
Um einen Pin auf 1 zu setzen musst du das entsprechende Bit im "Set 
Output Data Register" auf 1 setzen.
1
// Setze Pin 0 von PIOA auf 1
2
PIOA->PIO_SODR = 0x00000001;

Um den Pin wieder auf 0 zu setzen musst du das entsprechende Bit im 
"Clear Output Data Register" auf 1 setzen.
1
// Setze Pin 0 von PIOA auf 0
2
PIOA->PIO_CODR = 0x00000001;
Auch das unterscheidet sich also von den AVR, wo man setzen und loeschen 
uber ein Register macht. Du hast hier also getrennte register um einen 
Pin auf High oder Low zu setzen, und du musst jeweils eine 1 an die 
entsprechende Stelle schreiben.


So, ich hoffe das hilft dir erstmal ein bisschen :)
Wenn du konkreten Code brauchst, am Montag kann ich dir Code geben, z.B. 
fuer die initialisierung von Timer/Usart und die IRQ-Handler.

Ich glaub ich koennte dazu mal einen entsprechenden Artikel schreiben. 
:)

Gruesse

: Bearbeitet durch User
von Andreas W. (andy_w)


Lesenswert?

Hallo,
das sieht so aus, als wird es mir weiterhelfen. Kann sein, daß es noch 
etwas dauert, bis ich das ausprobieren kann. Die Sache mit dem Watchdog 
und dem PMC Power Management Controller habe ich selber schon 
herausgefunden. Die Interruptroutine selbst mit Namen von z.B. 
TC0_Handler habe ich auch schon probiert, allerdings fehlten mir die 
funktionen zum Einschalten des Interrupts. Also war ich wohl gar nicht 
so weit weg von der Lösung. Daß man z.B. den Timer zuerst initialisieren 
muß, ist klar.

Was nicht funktioniert, ist auf diese Weise eine Interruptroutine für 
einen einzelnen Portpin, z.B. PIOC_Handler, da meckert der 
Compiler/Linker, daß diese Funktion schon vorhanden ist und hier doppelt 
ist. Allerdings brauche ich wohl erst einmal keinen Portpin-Interrupt. 
Wie man die Portpins normal bedient, habe ich schon heraus, ich habe 
auch eigene Funktionen dafür geschrieben, um z.B. auch mehrere Pins auf 
einen Rutsch (z.B. PD0-PD7 für einen 8-Bit Bus für ein 7"-LCD) direkt 
beschreiben zu können. Ohne Interrupt geht das ja. Auch das 
Konfigurieren von Ports für andere Hardware (z.B: Timer, SPI usw. 
funktioniert.

Zumindest sind die Tipps hier schon einmal hilfreich, mit google hatte 
ich nur teilweise diese Informationen finden können.

Gruß
Andy

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Andreas W. schrieb:
> Zumindest sind die Tipps hier schon einmal hilfreich,
Das freut mich :)

Andreas W. schrieb:
> mit google hatte
> ich nur teilweise diese Informationen finden können.
Ging mir auch so, als ich mit den SAM angefangen hatte. Fuer die gibt es 
im vergleich zu STM32 praktisch gar nichts im Internet :-/

Andreas W. schrieb:
> Was nicht funktioniert, ist auf diese Weise eine Interruptroutine für
> einen einzelnen Portpin,
Das gibt es so auch nicht fuer einen einzelnen Pin. Du nimmst den 
Handler fuer den Port, und dann musst du abfragen welcher Pin den IRQ 
ausgeloest hat.
1
void PIOC_Handler(void)
2
{
3
  uint32_t irq_status = PIOC->PIO_ISR; // Input Change Interrupt
4
  
5
  // war es pin 0
6
  if(irq_status & 0x00000001 )
7
  {
8
  }
9
  
10
  // war es pin 1
11
  if(irq_status & 0x00000002)
12
  {
13
  }
14
15
  ...
16
17
  // war es pin 31
18
  if(irq_status & 0x80000000)
19
  {
20
  }
21
}

Oder verstehe ich dich falsch? :-/

Gruesse

von Andreas W. (andy_w)


Lesenswert?

Hallo,
ja genau so habe ich das versucht, aber das geht nicht. Bei der Funktion 
PIOC_Handler mosert der Compiler/Linker, daß die Funktion doppelt ist, 
während z.B. TC0_Handler problemlos compilierbar ist. PIOC_Handler ist 
irgendwo hart codiert, das hatte ich auch gefunden (find in all files), 
werde da aber nicht schlau, was man damit dann weiter machen soll. 
Irgendwo ist da auch ein Funktionspointer, aber ich bekam nichts 
funktionierendes hin.

Gruß
Andy

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Also dieser Code-Schnipsel
1
#include "sam.h"
2
3
void PIOC_Handler(void)
4
{
5
  uint32_t status = PIOC->PIO_ISR;
6
  
7
  if( status & 0x00000001)
8
  {
9
  }
10
  
11
  if( status & 0x80000000)
12
  {
13
  }
14
}
15
16
int main(void)
17
{
18
    /* Initialize the SAM system */
19
    SystemInit();
20
21
    // Interrupt einschalten
22
    NVIC_EnableIRQ(PIOC_IRQn);
23
    
24
    // Interrupt prioritaet setzen
25
    NVIC_SetPriority(PIOC_IRQn, 6);
26
27
    while (1) 
28
    {
29
        //TODO:: Please write your application code 
30
    }
31
}
Compiliert bei mir ohne probleme.
Keine Ahnung was da bei dir los ist :-/

Gruesse

von ech0 (Gast)


Lesenswert?

Hallo,
ich glaub ich kann mich hier mal anhängen, wenns OK ist.

Wärst du so freundlich mal ein Beispiel für den USART0 ( init, senden, 
lesen per interrupt ) einzustellen?

Ich muss mich grad mit dem Controller befassen, habe mit ARM aber noch 
nicht soviel gemacht. Ich hab ein Beispiel aus dem ASF übernommen aber 
es gehen keine Daten raus bzw. rein...
enable ich den TX interrupt, läuft er permanent rein, obwohl pro Sekunde 
ein Zeichen gesendet werden sollte.

von Simon (Gast)


Lesenswert?

Hallo Kay

Ist schon ne weile her, aber da ich auf das selbe Problem gestossen bin 
dachte ich ich lass die Infos mal für zukünftige Googler hier:

Der Grund warum dein Code nicht kompiliert ist weil der pio-Driver den 
Handler bereits implementiert. Du kannst dem Driver aber deinen eigenen 
Handler als Function-Pointer mitteilen:
1
void (*p_buttonPressedHandler) (uint32_t, uint32_t) = &buttonPressedHandler;
2
pio_handler_set_pin(BUTTON_UP, PIO_IT_FALL_EDGE, p_buttonPressedHandler);
3
pio_enable_pin_interrupt(BUTTON_UP);
4
NVIC_EnableIRQ(PIOD_IRQn);

Deine Implementation des Handlers sollte dann folgendermassen aussehen:
1
void buttonPressedHandler (uint32_t id_source, uint32_t mask_source)
2
{
3
  printf("Button pressed! \n");
4
}

Das schöne daran ist, dass die Interrupt-Quelle beim Aufrufen des 
Handlers direkt mitgegeben wird.

Gruss
Simon

von Sven (Gast)


Lesenswert?

Moin moin,

ich schlage mich auch gerade damit rum und versuche das noch mal hoch zu 
holen. Die Arduino IDE meckert auch bei dem PIO Handler rum (mehrfache 
Definition). Jetzt würde ich gerne den Tipp von Simon einbauen. Das sind 
für mich aber ehrlich gesagt böhmische Dörfer was in dem ersten 
Codeschnipsel von ihm steht. Wo baue ich das denn in dem genannten 
Beispiel von Kaj G. oben ein?

Was steht denn dann in den beiden Parametern id_source und mask_source 
drin?

Ich hoffe ihr könnt mir da noch helfen.

Grüße
Sven

von Philipp H. (philipp93)


Lesenswert?

Hallo,

ich muss leider das Thema aufgreifen. Ich möchte mmit Atmel Studio 7 
möglichst ohne die ASF Bibliothek (pio_handler_set_pin etc.) 3 externe 
Interrupts auf der steigenden Flanke zum laufen bekommen.

Da wenig Pins genutzt werden würde nicht mal eine Abfrage nach der 
genauen ID von Nöten sein.

Das Problem ist in dem Moment gegeben in dem ich versuche sam.h 
einzubinden. Kann mir jemand bitte sagen was ich machen muss.

Gruß Philipp

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.