Hallo zusammen,
ich habe eine Frage zum Multitasking (Artikel
http://www.mikrocontroller.net/articles/Multitasking - Variante 3 mit
Timer).
Im angehangenen Beispiel soll eine Taste abgefragt werden, eine LED
unterschiedlich schnell blinken (in Abhängigkeit von der Taste) und nach
10 Sekunden ein Text auf dem LCD ausgegeben werden. Zeitraster des
Timers ist 1 ms.
Das Programm funktioniert ohne die LCD-Ausgabe (lcd_ausgabe)
einwandfrei. Schaltet man die LCD-Ausgabe dazu, hängt sich das Programm
auf.
Vermutung: Die LCD-Routinen brauchen länger als 1 ms - Heraufsetzen des
Timers aus z.B. 10 oder 100 ms löst das Problem aber nicht.
Frage: Wie bindet man LCD-Routinen in das Multitasking ein ? Verwendet
wird ein ATMEGA8 mit 16 MHz.
Danke und viele Grüsse, Niedzwiedz
1
/* Multitasking_III.c */
2
#include<stdlib.h>
3
#include<avr/io.h>
4
#include"d:/AVRStudio4/C/libs/lcd2x16.h"
5
#define F_CPU 16000000UL
6
#include<util/delay.h>
7
#include<avr/interrupt.h>
8
9
#define KEY_DDR DDRD
10
#define KEY_PORT PORTD
11
#define KEY_PIN PIND
12
#define KEY0 0
13
14
#define LED_DDR DDRC
15
#define LED_PORT PORTC
16
#define LED0 0
17
18
volatileuint8_tflag_1ms;
19
20
uint8_ttaste_lesen(void)
21
{
22
if(KEY_PIN&(1<<KEY0))
23
return1;
24
else
25
return0;
26
}
27
28
voidled_blinken(uint8_ttaste)
29
{
30
staticuint16_tzaehler=0;
31
32
if(taste)
33
{
34
if(zaehler>=5000)
35
{
36
LED_PORT^=(1<<LED0);
37
zaehler=0;
38
}
39
}
40
else
41
{
42
if(zaehler>=2500)
43
{
44
LED_PORT^=(1<<LED0);
45
zaehler=0;
46
}
47
}
48
zaehler++;
49
}
50
51
voidlcd_ausgabe(void)
52
{
53
staticuint16_tzaehler=0;
54
55
if(zaehler>=10000)
56
{
57
zaehler=0;
58
lcd_puts("* Start *\n");
59
}
60
zaehler++;
61
}
62
63
ISR(TIMER2_COMP_vect)
64
{
65
if(flag_1ms)// Laufzeit der Tasks > 2 ms => Fehlersignalisierung
@ Klaus Niedzwiedz (niedzwiedz)
>Das Programm funktioniert ohne die LCD-Ausgabe (lcd_ausgabe)>einwandfrei. Schaltet man die LCD-Ausgabe dazu, hängt sich das Programm>auf.>Vermutung: Die LCD-Routinen brauchen länger als 1 ms -
Genau.
>Heraufsetzen des>Timers aus z.B. 10 oder 100 ms löst das Problem aber nicht.
Sicher?
Deaktiviere doch einfach mal die Sicherheitsprüfungen mit dem flag_1ms.
Ausserdem sollte man nur eine der beiden Abfragen nutzen. Und WENN man
sie nützt, sollte man z.B. eine rote LED anschalten, wenn das Timing von
1ms nicht eingehalten wurde.
>Frage: Wie bindet man LCD-Routinen in das Multitasking ein ? Verwendet>wird ein ATMEGA8 mit 16 MHz.
Der mehr als schnell genug sein sollte ;-)
Ansonsten sehe ich keinen Fehler.
Falk Brunner schrieb:> Deaktiviere doch einfach mal die Sicherheitsprüfungen mit dem flag_1ms.> Ausserdem sollte man nur eine der beiden Abfragen nutzen.
Bingo, wenn ich beide Sicherheitsprüfungen auskommentiere, geht es auch
mit 1 ms ! Das verstehe ich allerdings nicht, denn nach meinem
Verständnis unterbricht der Timer doch die LCD-Routine und der
Programmablauf gerät dann durcheinander. Muss das Timing immer grösser
sein als die Summe aller Taskzeiten ? Und wie kann ich die Taskzeiten
bestimmen ?
> Und WENN man sie nützt, sollte man z.B. eine rote LED anschalten, wenn das
Timing von > 1ms nicht eingehalten wurde.
Im Beispiel leistet das auch die LED0
if (flag_1ms) // Laufzeit der Tasks > 1 ms => Fehlersignalisierung
{
LED_PORT |= (1<<LED0); // LED0 an, Programm stoppen
while(1);
}
@ Klaus Niedzwiedz (niedzwiedz)
>Bingo, wenn ich beide Sicherheitsprüfungen auskommentiere, geht es auch>mit 1 ms ! Das verstehe ich allerdings nicht, denn nach meinem>Verständnis unterbricht der Timer doch die LCD-Routine
Sicher, denn es ist ja eine ISR.
>und der>Programmablauf gerät dann durcheinander.
Nein, aber das 1ms Timing stimmt halt nicht.
> Muss das Timing immer grösser>sein als die Summe aller Taskzeiten ?
Artikel gelesen?
> Und wie kann ich die Taskzeiten bestimmen ?
Simulieren oder messen. Messen kann man intern mit einem (anderen) Timer
oder extern mit dem Oszilloskop.
>Im Beispiel leistet das auch die LED0> if (flag_1ms) // Laufzeit der Tasks > 1 ms => Fehlersignalisierung> {> LED_PORT |= (1<<LED0); // LED0 an, Programm stoppen> while(1);> }
Huch, da hatte ich wohl Tomaten auf den Augen 8-0
Karli schrieb:> Sorry der Nachfrage.> Was verursacht die LCD genau dabei?
Nix.
> Ist 1ms zu langsam?
Ja. LCD Ausgabe ist generell relativ langsam.
Das ist allerdings auch nicht schlimm, denn ein LCD hängt man ja drann,
damit ein Mensch etwas lesen kann. Bei 1000 wechselnden Ausgaben pro
Sekunde auf ein LCD kann aber kein Mensch mehr irgendwas lesen. D.h. da
braucht es die 1 Millisekunde gar nicht.
Zu langsam, ok (?)
So wie ich das sehe, ist der Aufruf lcd_ausgabe jede ms drin. Wenn es
länger dauert und es ausreicht z.B. alle 10ms oder 100ms zu
aktualiesieren, wie kann ich es machen?
z.B. so:
eine Schleife in die lcd_ausgabe schreiben, das es nur jedes 10x (oder
100) mal aktualiseiert wenn die Schleife vorbeikommt? Dann hätte das LCD
genügend Zeit nach der übergabe der Werte die Anzeige zu machen, während
Multitasking mit 1ms weiter läuft.
Oder kennst du eine bessere Möglichkeit?
Karl
Zähle in deiner 1ms-Schleife eine Variable hoch. Und wenn die Variable
bei 100 angekommen ist, rufst du die LCD-Ausgabe auf und setzt die
Variable wieder auf Null. Damit wird das LCD nur noch 10mal pro Sekunde
aktualisiert (100ms), was normalerweise völlig genügt.
Karli schrieb:> Zu langsam, ok (?)> So wie ich das sehe, ist der Aufruf lcd_ausgabe jede ms drin.
Schon.
es kommt aber nicht bei jedem Aufruf zu einer Ausgabe, sondern nur bei
jedem 10-tausendstem Aufruf.
> 100) mal aktualiseiert wenn die Schleife vorbeikommt? Dann hätte das LCD> genügend Zeit nach der übergabe der Werte die Anzeige zu machen, während> Multitasking mit 1ms weiter läuft.
So wie das weiter oben geschrieben ist, muss man ganz einfach
akzeptieren, dass die LCD Ausgabe die restlichen Tasks ab und an mal ein
bischen verzögert.
Wenn das inakzeptabel ist, dann kommt die entsprechende regelmässig
auszuführende Aktion eben mit in die ISR hinein. Dann ist garantiert,
dass die 1ms auch dann halten, wenn die Hauptschleife kurzfristig auch
mal ein wenig länger braucht.
Wenn ich es bei jedem 10 Lauf aufrufe verzögert sich dieser Durchlauf
und verursacht einen Fehler. Man könnte dabei die Fehlerauswertung
blockieren.
Wie kann ich die Anzeige in die ISR legen? Es sollen doch nur kurze
Programme reinkommen die keinen Verzug verursachen?
Karl
Karli schrieb:> Wenn ich es bei jedem 10 Lauf aufrufe verzögert sich dieser Durchlauf> und verursacht einen Fehler. Man könnte dabei die Fehlerauswertung> blockieren.> Wie kann ich die Anzeige in die ISR legen?
Am besten gar nicht.
Irgendwie hab ich das Gefühl, dass dir nicht wirklich klar ist, dass 1
Millisekunde für einen µC wie dem AVR eine sehr, sehr lange Zeit ist.
Wenn er mit (Hausnummer) 10MHz getaktet wird, arbeitet der in dieser
Zeit immerhin knapp 8000 Befehle ab.
Die Verzögerung beim LCD ist nicht darauf zurück zu führen, dass ein AVR
so langsam wäre, sondern dass es bei der Ansteuerung eines LCD gilt,
Wartezeiten einzuhalten, damit das LCD mitkommt.
Und du kannst mir nicht erzählen, dass irgendein Mensch feststellen
kann, ob eine Ausgabe mal um eine halbe Millisekunde verzögert kommt
bzw. fertig gestellt wird. Ok, Supermann könnte das vielleicht, aber
sonst ...?
Karli schrieb:> Wie kann ich die Anzeige in die ISR legen? Es sollen doch nur kurze> Programme reinkommen die keinen Verzug verursachen?
Ganz einfach, immer nur ein Zeichen ausgeben.
Dann entfällt auch das 40µs warten, da ja immer 1ms vergangen ist.
Hier ein Beispiel:
Beitrag "Formatierte Zahlenausgabe in C"
Hallo Karl Heinz
Die Sache mit den 10M ist mir absolut klar. Arbeite selber mit
Multitasking und bin bestrebt alles in dieser Zeit zu machen. Habe es
bisher so gemacht, für Anzeigen und Rechenoperationen die überwachung
nicht zu benutzen. Damit verlängert sich zwar jeder 20 oder anderer
Durchlauf. Hatte die Anzeige immer so auf 100ms oder 200ms anzusteuern.
Leider gefällt es mir bei einigen Sachen nicht so. Die Zeit ist zwar
nicht zu sehen, doch die Zeit innerhalb von Warteschleifen (mit Zähler)
verändert sich dann. Das kann zu Problemen führen. (ungenau-relativ)
Daher suche ich nach einer Lösung. Werde mir den Artikel anschauen. Gibt
es keine Lösung ausserhalb der ISR?
Karl
Hallo Peter
habe mir den Text und die Datein angeschaut. Muss ganz ehrlich sagen,
sieht schön aus, verstanden habe ich es nicht. Ein user wollte schon
2009 von seinen Erfolgen berichten. Scheint noch nicht geklappt zu
haben. Hast du vielleicht mehr Info zur Anwendung oder ein Beispiel?
Karl
@ Karli (Gast)
>Daher suche ich nach einer Lösung. Werde mir den Artikel anschauen. Gibt>es keine Lösung ausserhalb der ISR?
Ist es denn SOOOO schwer? Die 1ms sind kein göttliches Gesetz. Ändere
den Timer auf 2, 5 oder 10ms ab und alles ist gut. Denn auch wenn man
nur jden X-ten Aufruf eine Aktion in den Tasks/Funktionen ausführt, darf
diese Aktion keinesfalls länger als der Timerzyklus dauern, denn sonst
stimmt das 10ms Timing nicht mehr. Bei sehr einfachen Programmen ist das
zwar unkritisch, bei etwas anspruchsvolleren allerdings nicht mehr. Du
gibst hier im Beispiel 16 Zeichen aus, das dauer bei ~40us/Zeichen
~640us, real vielleicht 800us. Das ist schon recht knapp an der 1ms
dran.