www.mikrocontroller.net

Forum: Compiler & IDEs Kooperatives Multitasking


Autor: Malte __ (malte) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ich habe mir einen kooperativen Scheduler geschrieben (siehe Anhang). Im 
Simulator (Avr-Studio 3.5) sah mit einem sehr einfachem Test auch alles 
gut aus. Jetzt habe ich in der Praxis aber das Problem, dass Daten 
durcheinander kommen.
Der Test soll in einem Task eine LED blinken lassen und in einem 
weiteren einen Text per RS232 ausgeben.
Dies funktioniert auch, nur das zwei Zeichen an der RS232 Ausgabe 
wiederholt falsch sind: Anstelle von "Testtest\n\r" erhalte ich 
"T#.ttest\n\r" an der seriellen Schnittstelle.

Zusätzlich zu dem Quellcode noch:
Die Funktion sched ist in der Headerdatei als
void sched(void) _attribute_ ((naked));
deklariert.

Der RS232 task sieht wie folgt aus:

void rs232_task(void) {
u08 sendbuf[14] = "M;VVVVV;AAAA\n\0";
UBRRL = UARTNUMBER;    //set speed
UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0); //set 8Bit mode
UCSRB = 1<<RXEN | 1<<TXEN;   //TX enabled, RX enabled
//only in op_mode==0 we are willing to recive commands from the interface
//in all other modes, we send out the mode, voltage and current every 0,25sec

for (;;) {
  rs232_print("Testtest\n\r\0");
}

for (;;) {
  if (op_mode == 0) {
    //TODO: add eeprom read/write

  } else {
    //Sending format: M;VVVVV;AAAA\n
    sendbuf[0] = op_mode+48;
    sendbuf[1] = ';';
    //TODO: add voltage into sendbuf
    sendbuf[7] = ';';
    //TODO: add current into sendbuf
    rs232_print(sendbuf);
    waitms_sched(250);
  }
}
}

void rs232_print(u08 *text) {  //Sends a string over rs232
/*Please note that this function uses the sched()
  If you use this function from more than one task, it might happen
  that the output of the different tasks get mixed.
  However I use this function only within the rs232_task().
*/
u08 k = 0;
while (*(text+k) != 0) {  //for every character of *text
  while ((UCSRA & (1<<UDRE)) == 0) {  //wait until the USART can send data
    sched();
  }
  UDR = *(text+k);
  k++;
  if (k == 0) {    //security out if the string was not null terminated
    break;
  }
}
}

Ich wüsste gerne ob ich in dem Scheduler irgenwas übersehen habe, was 
ich hätte sichern müssen. Ich habe meines wissens alle Register 
gesichert, die laut avr-libc
http://www.nongnu.org/avr-libc/user-manual/FAQ.htm...
als "Call-saved registers" bezeichnet werden.

Also habe ich hierbei was übersehen?

Autor: Dirk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sichert der Eintrittscode von void sched(void) nicht schon selber einige 
selbst verwendete Register auf den Stack?

Autor: Malte __ (malte) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Sichert der Eintrittscode von void sched(void) nicht schon selber einige
> selbst verwendete Register auf den Stack?
Nein, dafür sorgt _attribute_ ((naked)) in der Headerdatei. Das 
Assemblerlisting der Datei zeigt auch keinen Code im "prologue" und 
"epilogue" Abschnitt.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du sicherst nen Haufen Register, aber das wichtigste nicht, das SREG !


Wenn der Scheduler den Watchdog triggert, dann kannst Du den auch gleich 
weglassen, sowas ist Unsinn.
Der Watchdog darf nur in einer einzigen Task getriggert werden, die das 
korrekte Funktionieren des gesamten Programms überwacht !


Peter

Autor: Malte __ (malte) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Allen ein frohes neues Jahr.

> Du sicherst nen Haufen Register, aber das wichtigste nicht, das SREG !
Ok, ich wusste nicht, dass sich Funktionen darauf verlassen das dies 
nach einem Unterfunktionsaufruf weiterhin im gleichem Zustand ist.
Ich habe mein Program um
register unsigned char sreg asm("r0");
sreg = SREG;
asm volatile ("push r0");
//der bisherige code
asm volatile ("pop r0");
SREG = sreg;
erweitert.
Das brachte aber nicht den gewünschten Erfolg. Also habe ich das 
Programm nochmal im AVR-Studio simuliert. Heraus kam, dass durch die 
Zeile
memcpy_P(&subprog,&schedlist[sched_task_l].execute,sizeof(subprog));
der Text überschrieben wurde.
Das scheint wohl daran zu liegen, dass in Normalfall der Compiler etwas 
Platz auf dem Stack für lokale Variablen reserviert, was durch mein 
_attribute_ ((naked)) aber nicht geschah.
Mal sehn wie sich das Problem halbwegs elegant lösen lässt.

> Wenn der Scheduler den Watchdog triggert, dann kannst Du den auch gleich
> weglassen, sowas ist Unsinn.
> Der Watchdog darf nur in einer einzigen Task getriggert werden, die das
> korrekte Funktionieren des gesamten Programms überwacht !

Ich dachte mir, dass auf diese Weise sichergestellt ist, dass kein Task 
hängt und sched() nicht länger aufruft. Einen zusätzlichen kontroll 
Task, der bei einem erkannten Fehler in eine Endlosschleife ohne sched() 
geht und somit einen Reset provoziert kann es so ja immer noch geben.

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In der "naked" Funktion dürfen mangels Stack-Frame nur Variablen benutzt 
werden, die garantiert in Registern landen (erzeugten Code 
kontrollieren). Komplexe Funktionen ggf. als Unterprogramm 
implementieren, und auch dort kontrollieren, dass der Compiler dieses 
nicht klammheimlich inlined (macht GCC sehr gerne, vor allem wenn als 
"static" deklariert).

Autor: Malte __ (malte) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
So der problematische Code ist jetzt eine normale Unterfunktion.
Bei dem Restlichen hoffe ich, dass jetzt garantiert ist dass er in 
Registern landet. Das war mir vorher nicht so bewusst.

Mein Testprogram funktioniert jetzt wie erwartet. Den neunen Quellcode 
habe ich angehängt, falls jemand Interesse hat.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Malte __ wrote:

> Ich dachte mir, dass auf diese Weise sichergestellt ist, dass kein Task
> hängt und sched() nicht länger aufruft. Einen zusätzlichen kontroll
> Task, der bei einem erkannten Fehler in eine Endlosschleife ohne sched()
> geht und somit einen Reset provoziert kann es so ja immer noch geben.

Wenn der Kontroll-Task aber nun nicht mehr aufgerufen wird, weil sich 
irgendwas verklemmt hat ?


Ist ungefähr so, wie ein Schließer als Notschalter, bei dem vergessen 
wurde, den Stecker reinzustecken.
Daher müssen Notschalter immer Unterbrecher sein.

Daher müssen Kontrol-Tasks immer resetten, wenn sie nicht mehr 
aufgerufen werden.


Peter

Autor: Malte __ (malte) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger wrote:

> Wenn der Kontroll-Task aber nun nicht mehr aufgerufen wird, weil sich
> irgendwas verklemmt hat ?
>
> Ist ungefähr so, wie ein Schließer als Notschalter, bei dem vergessen
> wurde, den Stecker reinzustecken.
> Daher müssen Notschalter immer Unterbrecher sein.
>
> Daher müssen Kontrol-Tasks immer resetten, wenn sie nicht mehr
> aufgerufen werden.

Ja da ist was drann. Dann werde ich den Watchdog Reset in einen eigenen 
Thread verlagern.

Autor: Malte __ (malte) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ein gemeinen Fehler habe ich noch in meinem Schduler gefunden:
An den beiden Stellen an denen der SP beschrieben wird, müssen vorher 
eventuell andere laufende Interrupts mit cli() abgeschaltet werden.

Am besten in der Form:
sreg2 = SREG;
cli();
SP = schedlist_p[sched_task_l];
SREG = sreg2;

Ohne dies verhielt sich mein Program leider manchmal recht merkwürdig. 
Mal lief es mehere Minuten, nach einer kleinen Änderung wurden dann 
plötzlich "Geisterzeichen" auf dem LCD Display sichtbar oder das 
Programm sprang plötzlich zur Adresse 0x00.

Eine einfache Stack Überwachung (Initialisieren des RAMs mit einem 
Muster) gibt es jetzt auch, da ich zunächst dachte das obige Verhalten 
hinge mit einem Stack-Überlauf zusammen.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, bei AVRs mit Stackpointer >8Bit muß jede Änderung atomar gemacht 
werden.

Der AVR ist nunmal ein 8-Bitter (nicht negativ gemeint).


Peter

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.