Interrupt Prioritäten gibt es nicht - was nun?

Hallo,

ich habe in meinem Projekt 3 Tasks.

Reihenfolge der Prioritäten: Task 1 > Task 2 > Task 3. Höhere Priorität darf niedrigere Priorität unterbrechen, aber muss dann danach wieder dorthin springen wo es ausgehört hat.

Task 1 und 2 habe ich realisiert.
Task 1 ist eine Timer ISR in der ich ein Buffer ausgebe und die in einer bestimmten Frequenz ausgelöst werden muss - darum höchste Priorität.
Task 2 füllt dieses Buffer sobald es leer ist. Ich habe diesen Task einfach in die MainLoop geklatscht, weil diese von der Timer ISR unterbrochen werden darf.
Das funktioniert soweit. Nun soll Task 3 dann laufen wenn Task 1 und 2 nicht laufen, von diesen zwar unterbrochen werden dürfen, jedoch danach wieder dort ansetzen wo es aufgehört hat.

Soweit ich das verstanden habe sind in Arduino die Interrupts ja aus in einer ISR, das heißt ich kann einen Interrupt nicht durch einen anderen unterbrechen. Gibt es eine Alternativlösung zu dieser Situation?

Cheers

Soweit ich das verstanden habe sind in Arduino die Interrupts ja aus in einer ISR, das heißt ich kann einen Interrupt nicht durch einen anderen unterbrechen.

Natürlich kann man in einer ISR die Interrupts wieder erlauben.
Dann solltest du aber jeder Task einen eigenen Stack spendieren.
Die Wiedereintrittsfähigkeit, ist eine ganze Sammlung von Sorgen und Problemen.

Gibt es eine Alternativlösung zu dieser Situation?

FreeRTOS

winterwurst:
Soweit ich das verstanden habe sind in Arduino die Interrupts ja aus in einer ISR, das heißt ich kann einen Interrupt nicht durch einen anderen unterbrechen.

Das ist nur standardmäßig so. Man kann das globale Interrupt Enable Flag auch wieder setzen. Das ist aber aus gutem Grund so gemacht. Gegenseitige Interrupts sind sehr gefährlich

Hi

In Deinem Beispiel wäre Das doch einfach ein

if (buffer_auffuellbar){
   fülle Buffer aus
}else{
   mache Task 3
}

Dadurch wird Tast 3 nur ausgeführt, wann in Deiner loop() sonst Nichts zu tun ist.
Aus der loop() wird in die ISR gesprungen, wenn der Timer zuschlägt und sogar dahin wieder zurück, wo Er davor war.

Würde ich jetzt nicht wirklich 'Task' nennen, eher State-Maschine :wink:

MfG

Eine andere Alternative wäre, die Voraussetzung

ich habe in meinem Projekt 3 Tasks.
Reihenfolge der Prioritäten: Task 1 > Task 2 > Task 3

zu überdenken.

Wie wird denn erkannt, dass überhaupt eventuell ein Task-Wechsel zu machen ist, oder dass das wg. niedriger Priorität verschoben aber gemerkt werden muss? Kümmert sich darum ein Task 0 mit noch höherer Priorität? Wenn du sowas wie ein Betriebssystem haben willst, musst du dich darum kümmern.

Ob FreeRTOS auf Atmel AVR 8bit Controllern genügend Ressourcen übrig lässt, dass das ganze Sinn macht, weiß ich nicht.

Ob FreeRTOS auf Atmel AVR 8bit Controllern genügend Ressourcen übrig lässt,

Meiner (geringen) Erfahrung nach sitzen schon eine Handvoll drin...
Je nach dem ob Preemptiv oder Kooperativ
Für eine preemptive Task, wird man schon an die 256 Byte für Stack und Verwaltung ansetzen müssen.
Ist aber von den konkreten Bedingungen abhängig.

Wäre das mein Problem, dann würde ich es natürlich so versuchen: Multitasking Macros
Für die ganz zeitkritischen Dinge kann man ja, wie schon im Eingangsposting gesagt, einen Timer verwenden.

winterwurst:
Task 1 ist eine Timer ISR in der ich ein Buffer ausgebe und die in einer bestimmten Frequenz ausgelöst werden muss

Was heißt das konkret?

Wie oft muss die Ausgabe erfolgen, was wird wie ausgegeben?

P.S. Wenn es um deinen 44kHz Audio-Player geht, dann ist ein normaler Arduino ungeeignet.
Ein Teensy oder Due würden wirklich ohne Kopfstände zuverlässig funktionieren, in Stereo.

P.P.S. Hast du dir den Fireduino mal angesehen?
Dual-Core mit Audio, Micro-SD, WiFi und Arduino Unterstützung.
Auf dem kannst du dann mit Tasks programmieren.

Wenn du auf Tasks stehst ist auch ein ESP32 schön. Dual-Core mit voller Task-Unterstützung, WiFi und Bluetooth.

Hallo und danke für die Antworten.

combie:
FreeRTOS

Habe mir FreeRTOS angeschaut und vom Prinzip her ist es genau das was ich suche. Wenn ich das jedoch in meine Anwendung einbinde, wird das zu viel für den Arduino. Ich habe es nun auf ein grundlegendes Problem zurückführen können, vermutlich Speichermangel, auch wenn mir die Ausgabe nach dem Hochladen angibt, es sei noch genug Speicher verfügbar. Im Folgenden ein kurzes Beispiel...

#include <Arduino_FreeRTOS.h>
#include <SD.h>
#include <SPI.h>

uint8_t Puffer[400];

File myFile;

void setup() {
  Serial.begin(9600);
  SPI.begin();
  SD.begin();


  myFile = SD.open("beethoven.wav");
  myFile.read(Puffer, sizeof(Puffer));
}

void loop() {
  Serial.println("funktioniert");
}

Ich benötige für meine Anwendung einen Puffer, je größer desto besser. Bei einer Puffergröße von 400 Bytes wird der Serial Befehl in der mainLoop einfach nicht ausgeführt (im Terminal erscheint nichts), bei einer Puffergröße von 300 schon. Ich kann mir zwar nicht erklären, warum das so ist, aber es ist definitiv vom genutzten Speicher abhängig. Die eigentlichen Tasks zu definieren, verringert die maximal mögliche Puffergröße noch weiter, da diese auch Speicher beanspruchen.

Die Ausgabe nach dem Hochladen sagt bei 400 size Puffer

Globale Variablen verwenden 1458 Bytes (71%) des dynamischen Speichers, 590 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.

(Ohne die FreeRTOS lib einzubinden kann ich ein Puffer von >1000 Byte auslesen.)

Whandall:
Wenn du auf Tasks stehst ist auch ein ESP32 schön. Dual-Core mit voller Task-Unterstützung, WiFi und Bluetooth.

Gut erkannt mit dem Audio-Player. Danke auch für die Anregung, werde mir wohl einen solchen zulegen. Dann sollte das mit FreeRTOS auch besser funktionieren, glaube ich.

PS:

postmaster-ino:

if (buffer_auffuellbar){

fülle Buffer aus
}else{
  mache Task 3
}

In diesem Fall könnte Task 3 jedoch nicht von Task 2 unterbrochen werden. Task 3 müsste vollständig ausgeführt werden, bevor Task 2 wieder drankommen kann. Ich habe tatsächlich so etwas probiert, indem ich innerhalb von Task 3 nach jedem einzelnen Schritt geprüft habe ob das Buffer aufgefüllt werden muss - so eine Art Pseudo Multitasking. Das hat sogar ganz gut funktioniert, aber die Arbeitsschritte von Task 3 dürfen nur sehr wenig Zeit beanspruchen. Auch hier wäre ein schnellerer Prozessor gut.

vermutlich Speichermangel, auch wenn mir die Ausgabe nach dem Hochladen angibt, es sei noch genug Speicher verfügbar

Die SD Library braucht einiges an RAM, und wieviel RAM zur Laufzeit tatsächlich belegt ist, kann der Compiler nicht wissen (weder vor noch nach dem Hochladen).

Dazu gibts aber was im playground:
z.B.

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

Es ist übrigens ziemlich leicht, rauszukriegen, was nicht geht.
Schwieriger und spannender ist, herauszufinden, wie etwas mit den gegebenen minimalen Ressourcen (2kB RAM) doch machbar ist.