Forum: Compiler & IDEs Multitasking


von Klaus N. (niedzwiedz)


Angehängte Dateien:

Lesenswert?

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
volatile uint8_t flag_1ms;
19
20
uint8_t taste_lesen(void)
21
{
22
  if (KEY_PIN & (1<<KEY0))
23
    return 1;
24
  else
25
    return 0;
26
}
27
 
28
void led_blinken(uint8_t taste)
29
{
30
  static uint16_t zaehler=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
void lcd_ausgabe(void)
52
{
53
  static uint16_t zaehler=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
66
  {
67
    LED_PORT |= (1<<LED0);  // LED0 an, Programm stoppen
68
  while (1);
69
  }
70
  flag_1ms = 1;
71
}
72
73
74
int main(void)
75
{
76
  int8_t taste;
77
  
78
  // LCD initialisieren
79
  lcd_init(LCD_DISP_ON);  // initialisiere Display
80
  lcd_clrscr();
81
82
  // Taste initialisieren
83
  KEY_DDR &= (0<<KEY0);  // Taste 0 ist Eingang
84
85
  // LED initialisieren
86
  LED_DDR |= (1<<LED0);  // LED0 ist Ausgang
87
  LED_PORT |= (0<<LED0); // LED0 ausschalten
88
  
89
  // Timer2 initialisieren
90
  TCCR2 = (1<<WGM21) | (1<<CS01) | (1<<CS00);  // CTC, Prescaler 64
91
  OCR2  = 56;  // 1ms
92
  TIMSK |= (1<<OCIE2);
93
  
94
  // Interrupts global freigeben
95
  sei();
96
97
  while (1)
98
  {    
99
  if (flag_1ms)
100
  {
101
    flag_1ms=0;
102
    
103
    // abzuarbeitende Tasks
104
    taste = taste_lesen();
105
    led_blinken(taste);
106
    lcd_ausgabe();
107
    
108
    if (flag_1ms)  // Laufzeit der Tasks > 1 ms => Fehlersignalisierung
109
    {
110
    LED_PORT |= (1<<LED0);  // LED0 an, Programm stoppen
111
    while(1);
112
      }
113
    }
114
  }
115
}

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ 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.

von Klaus N. (niedzwiedz)


Lesenswert?

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);
      }

von Falk B. (falk)


Lesenswert?

@ 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

von Karli (Gast)


Lesenswert?

Sorry der Nachfrage.
Was verursacht die LCD genau dabei? Ist 1ms zu langsam? Erhöhen?

Karl

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karli (Gast)


Lesenswert?

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

von npn (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Karli (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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 ...?

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

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"

von Karli (Gast)


Lesenswert?

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

von Karli (Gast)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

@ 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.

von Karli (Gast)


Lesenswert?

Werde es testen
Danke

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
Noch kein Account? Hier anmelden.