Hallo ich möchte einmal kurz verstehen, wie man die Interrupts so setzt, dass die eigene Funktion aufgerufen wird. z.B. Bei einem STM32 muss ich ja das richtige Bit setzen, um den Interrupt zu aktivieren. Wenn ein Interrupt kommt, wird in der Interrupttabelle nachgeschaut auf welche Adresse dieser Vektor zeigt und dann wird dort hingesprungen. Stimmt das so? Aber wenn ich so die Interruptvektortabelle anschaue(STM32F411RE) dann gibt es dort doch deutlich mehr Interrupte als in der Einleitung angegeben(52 maskable interrupt channels) oder ist das die maximale Anzahl an Interrupts zugleich?
Ich kenne nur PICs und da ist es nicht möglich seine eigenen Interruptroutinen zu definieren (auch wenn da noch scheinbar jede Menge Platz in der Tabelle ist, aber die Hardware ist für die anderen, unbenutzten Ints nicht vorhanden). Aber du kannst dir ganz einfach helfen, indem du einen Int, der gerade frei ist für deine Zwecke benutzt. Du schreibst einfach wie üblich die ISR (z.B. für ADC) und dann kannst du irgendwo im Programm das Interrupt Flag setzten und die Interrupt Service Routine wird aufgerufen. Solltes natürlich ADC nicht anschalten.
Beim STM32 werden teilweise mehrere Interruptquellen auf einen Vektor gemapped. Man muß die dann per Software wieder auseinanderdröseln, wenn man mehrer Interrupts benutzt die zu einem Vektor gehören.
Martin M. schrieb: > Hallo ich möchte einmal kurz verstehen, wie man die Interrupts so setzt, > dass die eigene Funktion aufgerufen wird. Die genaue Vorgehensweise hängt von der verwendeten Toolchain ab. Bei den ARMs kocht da jede ein bißchen ihr eigenes Süppchen. Am Ende geht es darum, die Addresse deiner ISR an die richtige Stelle der Vektortabelle zu bekommen. Meist (aber nicht immer) reicht es dazu, wenn deine Funktion den richtigen "magischen" Funktionsnamen hat. Bei anderen Toolchains mußt du bei der Funktionsdefinition ein Pragma angeben a'la
1 | void meine_ISR(void) __interrupt(10) |
2 | {
|
3 | ...
|
4 | }
|
wobei die 10 hier nur ein Beispiel ist (für den 10. Eintrag in der Vektortabelle). Und wenn du den Startup-Code selber schreibst wie z.B. dieser Mann hier: http://eleceng.dit.ie/frank/arm/ dann kannst du die Vektortabelle von Hand füllen. Das sieht dann z.B. so aus:
1 | const void * Vectors[] __attribute__((section(".vectors"))) = |
2 | {
|
3 | (void *)0x20001000, /* Top of stack (4k) */ |
4 | init, /* Reset Handler */ |
5 | Default_Handler, /* NMI */ |
6 | Default_Handler, /* Hard Fault */ |
7 | Default_Handler, /* MemManage */ |
8 | Default_Handler, /* Reserved */ |
9 | Default_Handler, /* Reserved */ |
10 | Default_Handler, /* Reserved */ |
11 | Default_Handler, /* Reserved */ |
12 | Default_Handler, /* Reserved */ |
13 | Default_Handler, /* Reserved */ |
14 | Default_Handler, /* SVCall */ |
15 | Default_Handler, /* Reserved */ |
16 | Default_Handler, /* Reserved */ |
17 | Default_Handler, /* PendSV */ |
18 | Default_Handler, /* SysTick */ |
19 | /* External interrupt handlers follow */
|
20 | Default_Handler, /* 0: WWDG */ |
21 | Default_Handler, /* 1: Reserved */ |
22 | Default_Handler, /* 2: RTC */ |
23 | Default_Handler, /* 3: FLASH */ |
24 | ...
|
25 | };
|
26 | |
27 | ...
|
28 | |
29 | void Default_Handler() |
30 | {
|
31 | while(1); |
32 | }
|
Hier würdest du dann einfach den Funktionsnamen der ISR an der passenden Stelle in die Tabelle einsetzen. > wenn ich so die Interruptvektortabelle anschaue(STM32F411RE) dann > gibt es dort doch deutlich mehr Interrupte als in der Einleitung > angegeben(52 maskable interrupt channels) oder ist das die maximale > Anzahl an Interrupts zugleich? Die Vektortabelle ist für alle µC einer Familie (hier: STM32F4) gleich groß. Da aber nicht alle Mitglieder der Familie über die gleiche Peripherie-Ausstattung verfügen, ist der eine oder andere Vektor unbenutzt. Der wird dann auch nie angesprungen. Darüber hinaus ist die Tabelle keine reine Interrupt-Tabelle, sondern eine kombinierte Exception/Interrupt-Tabelle. Die ersten 16 Einträge sind für Exceptions reserviert. Danach können bis zu 240 weitere Einträge für Interrupts kommen. Die meisten realen Implementierungen haben aber eine deutlich kürzere Interrupt-Tabelle.
Martin M. schrieb: > Hallo ich möchte einmal kurz verstehen, wie man die Interrupts so setzt, > dass die eigene Funktion aufgerufen wird. z.B. Bei einem STM32 muss ich > ja das richtige Bit setzen, um den Interrupt zu aktivieren. Laß dich nicht verwirren. Axel hat es zwar aus Sicht von C beschrieben, aber das ist mehr Verschleierung als Erklärung. Also: Die Cortexe haben einen Interrupt-Controller (NVIC) in die CPU eingebaut und dieser NVIC handhabt alle Interrupts sowie alles, was man braucht, um einen Interrupt auch softwaemäßig auslösen zu können. Aber das alles scheint hier NICHT dein Problem zu sein. Was du hier verstehen mußt, ist folgendes: Ab Adresse 0 hat bei den Cortexen eine Vektortabelle zu stehen. Es sind alles 32 Bit Adressen und die Tabelle kann je nach Chip unterschiedlich lang sein. Die allerersten Einträge sind hingegen von ARM festgelegt und sie beinhalten neben den Vektoren für interne Exceptions auch den Startup nach folgendem Schema: Nach dem Reset lädt die CPU den Inhalt von Vektor 0 in den Stackpointer und den Inhalt von Vektor 1 in den Programmcounter. Für alle anderen Vektoren ist eine eiserne Regel geschaffen worden: Dort, in der Quelle, wo sie deklariert werden, müssen die damit bezeichneten Default-Interrupthandler als WEAK gekennzeichnet sein. Das ist nötig, damit der Linker beim Vorhandensein eines echten Handlers (der sich ja in der Regel in einer ganz anderen Quelle befindet) die Adresse eben dieses echten Handlers in die Vektortabelle einträgt - und nicht etwa die Adresse des Defaulthandlers. Ich finde, daß man mit der Möglichkeit, einen Startupcode auch in C formulieren zu können, es zwar gut gemeint hat, aber dabei das genaue Gegenteil erreicht hat. Es hat dazu geführt, daß die Leute genau DESHALB nicht mehr verstehen, wie das Ganze eigentlich funktioniert. Also: Wenn alles richtig hingeschrieben ist, dann enthält der Startupcode die Vektortabelle und den/die Defaulthandler, welche als "WEAK" gekennzeichnet sind. Du hingegen schreibst dir für deine Peripherie den Handler, den du tatsächlich brauchst. Aber: du mußt ihn EXAKT GENAUSO benennen wie der entsprechende Defaulthandler heißt. Sonst kriegt der Linker das nicht auf die Reihe und dein Handler wird garnicht in die Vektortabelle eingetragen. Etwas anderes ist es, den betreffenden Interrupt auch tatsächlich freizuschalten, da muß man den NVIC bemühen. Beispiel: NVIC_ISERx = (1<<IrNummer); Näheres MUSS man im RefManual nachlesen. W.S.
W.S. schrieb: > Laß dich nicht verwirren. Axel hat es zwar aus Sicht von C beschrieben, > aber das ist mehr Verschleierung als Erklärung. Ähhm. Verwirrung stiftest hier eher du. > Ab Adresse 0 hat bei den Cortexen eine Vektortabelle zu stehen. Die Tabelle ist i.d.R. relozierbar, Adresse 0 ist also keineswegs so in Stein gemeißelt wie du behauptest. Außerdem ist die Adresse der Vektortabelle auch wieder nur ein Detail, das für das Verständnis der Funktionsweise ohne Belang ist. > Für alle anderen Vektoren ist eine eiserne Regel geschaffen worden: > Dort, in der Quelle, wo sie deklariert werden, müssen die damit > bezeichneten Default-Interrupthandler als WEAK gekennzeichnet sein. Das > ist nötig, damit der Linker beim Vorhandensein eines echten Handlers > (der sich ja in der Regel in einer ganz anderen Quelle befindet) die > Adresse eben dieses echten Handlers in die Vektortabelle einträgt - und > nicht etwa die Adresse des Defaulthandlers. Und das ist ein weiteres Implementierungsdetail. Es setzt voraus daß der Linker der Toolchain überhaupt weak symbols kennt. Und selbst wenn - man muß es nicht so machen. Siehe das von mir gegebene Beispiel mit der Vektortabelle als C-Array. Und in Assembler würde man das schon gar nicht so machen. Da würde man schlicht und einfach schreiben:
1 | .section "vectors" |
2 | .long top_of_stack |
3 | .long reset |
4 | .long ... |
5 | ... |
Und die Labels der Funktionen direkt in die Tabelle schreiben. > Ich finde, daß man mit der Möglichkeit, einen Startupcode auch in C > formulieren zu können, es zwar gut gemeint hat, aber dabei das genaue > Gegenteil erreicht hat. Es hat dazu geführt, daß die Leute genau > DESHALB nicht mehr verstehen, wie das Ganze eigentlich funktioniert. Überhaupt nicht. Die Verwendung von weak symbols ist eine weitere Hürde beim Verständnis der Zusammenhänge. Und es ist gut wenn man den Leuten die Möglichkeit gibt, Erfolgserlebnisse zu erzielen auch ohne alle vorhandenen Hürden gleich beim ersten Anlauf überspringen zu müssen. Die Funktionsweise des Linkers, insbesondere die Verwendung von weak symbols und generischem Startup-Code können sie danach ja immer noch lernen. > Also: Wenn alles richtig hingeschrieben ist, dann enthält der > Startupcode die Vektortabelle und den/die Defaulthandler, welche als > "WEAK" gekennzeichnet sind. Nur für deine höchst private Meinung davon, was "richtig" ist.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.