Auf den AVRs hatte ich einen Interrupt alle 2 Millisekunden. Der hat
zunächst mal ein paar Zähler auf den neuesten Stand gebracht, dann die
Interrupts wieder freigegeben (da kann man ja nur keinen oder alle
sperren) und erst dann eine längere Berechnung durchgeführt.
War der Prozessor zu viel mit anderen Interrupts beschäftigt, wurde
dieser Interrupt durch sich selbst unterbrochen. Um das zu erkennen,
wurde zu Beginn der langen Berechnung ein Flag gesetzt. Sah der
Interrupt-Handler dieses Flag gesetzt, hat er die lange Berechnung
ausgelassen. Das hat gut funktioniert, ich habe das hier mal mit einem
Beispiel auf dem ARM LPC1114 nachgestellt:
1 | void SysTick_Handler(void) {
|
2 | static uint8_t busy = 0, dot_written = 0;
|
3 | static uint32_t count = 0;
|
4 | static uint8_t minutes = 0, seconds = 0;
|
5 |
|
6 | count++;
|
7 | if (busy) {
|
8 | if ( ! dot_written) {
|
9 | serial_writechar('.');
|
10 | dot_written = 1;
|
11 | }
|
12 | return;
|
13 | }
|
14 |
|
15 | busy = 1;
|
16 | dot_written = 0;
|
17 |
|
18 | sei(); // = __enable_irq()
|
19 |
|
20 | if ( ! (count % 500)) { // A full second.
|
21 | // Calculate clock.
|
22 | seconds++;
|
23 | if ( ! (seconds % 60)) {
|
24 | seconds = 0;
|
25 | minutes++;
|
26 | }
|
27 | sersendf_P(PSTR("%su:"), minutes);
|
28 | if (seconds < 10)
|
29 | serial_writechar('0');
|
30 | sersendf_P(PSTR("%su\n"), seconds);
|
31 |
|
32 | // Be busy sometimes.
|
33 | if ( ! (seconds % 5))
|
34 | delay_ms(2000);
|
35 | }
|
36 | busy = 0;
|
37 | }
|
PSTR() u.Ä. einfach ignorieren, der Code ist auch für die AVRs. "busy"
ist das Flag und delay_ms(2000) ist ein Ersatz für die lange Berechnung.
Ohne den Delay gibt der Code eine prima funktionierende Uhr. Mit dem
Delay jedoch hält die Uhr alle 5 Sekunden kurz an (das ist noch OK),
danach macht sie aber nicht den eigentlich gewollten Sprung in der
Zählung. Das serial_writechar('.') wird auch nie ausgeführt.
Das bringt mich zu der Erkenntnis, dass das mit dem Re-entry des
Interrupts nicht klappt. Während der Pause gehen die Interrupts
offensichtlich verloren, der Zähler "count" stimmt nicht mehr mit der
Zeit überein.
In der ARM-Doku ist das Re-Entry von Interrupts irgendwie nicht
vorgesehen, da wird kein Wort dazu verloren. Weder dass es geht, noch,
dass es nicht geht. Ausser hier:
http://infocenter.arm.com/help/topic/com.arm.doc.dui0471g/DUI0471G_developing_for_arm_processors.pdf
in Kapitel 6.12., doch das passt so gar nicht zur sonstigen
Cortex-M0-Doku.
Irgendwas fehlt an der Geschichte noch. Hat jemand eine Idee, was das
sein könnte? Oder wie man ein ähnliches Verhalten ohne Re-Entry gebacken
bekommt? Die Berechnung muss in einen Interrupt, damit die noch
unwichtigeren Sachen im Nicht-Interrupt-Bereich unterbrochen werden
können. Ein zweiter Interrupt müsste irgendwie synchronisiert werden.
Besten Dank schon mal.