Gerade Anfänger haben oft das Problem, das erstmal alles wunderbar
läuft, aber plötzlich muß noch irgendwo gewartet werden.
Und ruchzuck ist die Performance im Eimer, aber der MC soll doch noch
so viele andere Sachen auch erledigen.
Wie macht man das also, Warten ohne Rechenzeitverschwendung ?
Eine mögliche Lösung ist der hier vorgestellte Scheduler.
Das Programm wurde mit WINAVR auf einem ATMega8 compiliert und belegt
etwa 900 Byte.
In dem Zip-File ist die Beschreibung und alle Source-Files.
Viel Spaß damit.
Peter
Nachtrag:
So ein Scheduler hat natürlich auch seine Einschränkungen.
Im Beispiel wird er alle 65536 Zyklen ausgeführt, d.h. die Main-Loop
sollte in der Regel in dieser Zeit mindestens einmal durchlaufen worden
sein. Dauert sie manchmal länger ist das meist auch kein Beinbruch.
Exakte Zeiten, wie z.B. Uhrzeit+Datum, Frequenzmessung sollte man
deshalb nicht im Scheduler sondern direkt im Timerinterrupt machen.
Wichtig ist also voher eine grobe Abschätzung der maximalen und
minimalen Zeitintervalle zu machen.
Im Beispiel ist die minimale Zeit: 1 / (11,0592MHz 256 256) =
5,9ms.
Das ist relativ schnell (das Beispiel hat ja fast nichts zu tun),
meistens sollten aber Zeiten von 100ms...500ms ausreichend sein.
Die maximale Zeit ergibt sich daraus, daß der Delayzähler 16-Bittig
ist, zu 65536 * 5,9ms = 6,4min.
Im main.h kann der Delayzähler als 8-, 16- oder 32-Bittig festgelegt
werden. Das hat dann Einfluß auf die Codegröße sowie die Größe der
Liste im RAM.
Für ein ordnungsgemäßes Funktionieren des Schedulers ist zu beachten,
daß man auch versteckte Wartezeiten vermeiden sollte.
Z.B. ist zu empfehlen, für die UART Sende- und Empfangspuffer
anzulegen, die dann im UART-Interrupt gefüllt bzw. geleert werden.
Das hängt aber immer auch von der konkreten Anwendung ab. Z.B. je 10
Bytes bei 115200Baud im Pollingmode zu senden, dürfte kaum einen
kritischen Zeitverbrauch bewirken.
Ein Scheduler ist also auch kein Zaubermittel, aber für komplexe
Zeitsteuerungen (z.B. Lichteffekte) fast unschlagbar einfach.
Peter
Hallo Peter,
da ich auf der Suche nach einer Guten Entprell variante in C Suche bin
ich auf diese Seite gestossen!
Leider bin ich noch anfänger um komme mit dem Code überhaupt nicht
zurecht er ist halt einfach zu komplex!
Im Prinzip möchte ich nur einen Port als Eingschalten bzw. Port B und
dann mit den Einzelnen Pins bei Betätigung einen Funktion aufrufen oder
variablen um 1 erhöhen, leider geliegt es mir nicht den Code so aus
einander zu nehmen das ich ihn benutzen kann! Ich wäre dir echt dankbar
wenn du gerade für anfänger dieses Thema nochmal bearbeiten könntest da
das entprellen ja fast das wichtigste ist was man am anfang benötigt!
Vielen Dank Tina
@Tina
die Schedulerfunktion laß einfach links liegen.
if( get_key_press( 1<<LED0 ) )
testet ein Bit (gedrückte Taste) und löscht es.
Zur Funktion der Tastenerkennung und Entprellung steht hier was:
http://www.mikrocontroller.net/forum/read-4-20549.html#new
Peter
Hallo,
vielen Dank für ds wirklich schöne Codestück - schon die
Entprellroutine ist toll, die Timer sind ebenfalls sehr nützlich.
Dennoch ein Verbesserungsvorschlag: der Code initialisiert den PortC
nicht, schaltet also auch die Pullups nicht ein.
Out-of-the-zip hat das bei mir sehr schöne Random-Muster zur Folge
gehabt - gegen völlig wild schwingende Eingänge hilft eben auch 4-fach
Abtastung noch nicht.
Aber kaum macht man es richtig, da funktioniert es auch.
hase
Moin Peter,
nachdem ich den Scheduler (also die Timer) auch schon erfolgreich
eingesetzt habe, wollte ich auch mal Danke sagen. Die Idee ist nicht
neu, aber funktionierend und effektiv fertig vorzufinden ist natürlich
schön :-)
Vielen Dank Peter.
900ss
Hallo Peter,
ich versuche gerade deinen Scheduler auf einem Atmega16 zum laufen zu
kriegen (16Mhz). Aber es tut sich irgendwie nichts. Als Beispiel sollen
drei LEDs eingeschaltet werden. Wenn ich test() ausführe passiert dies
auch. Mit timmeradd passiert aber nichts.
1
#include<avr/io.h>
2
#include<util/delay.h>
3
#include<avr/interrupt.h>
4
#include<avr/pgmspace.h>
5
#include"MAIN.h"
6
7
intmain(void)
8
{
9
//Für Scheduler
10
TCCR0=1<<CS02;//divide by 256 * 256; Timereinstellungen für Scheduler
11
TIMSK=1<<TOIE0;//enable timer interrupt
12
13
timerinit();//Initialisierung Timertick für Scheduler
14
15
sei();
16
17
timeradd(test,SECONDS(1));
18
19
for(;;)// main loop
20
{
21
if(f_timer_tick)
22
{
23
f_timer_tick=0;
24
timertick();
25
}
26
}
27
}
28
29
voidtest(void)
30
{
31
DDRC=0b00000111;
32
PORTC=0b00000000;
33
}
Die SCHEDULE.c habe ich nicht verändert und die MAIN.c sieht so aus:
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
//#include <signal.h>
4
5
#define XTAL 16e6
6
7
#define TIMERTICK (XTAL / 256 / 256) // timer ticks per second
8
9
#define SECONDS(x) ((uint)(TIMERTICK * x + 0.5))
10
11
#define uchar unsigned char
12
#define uint unsigned int
13
#define bit uchar
14
#define idata
15
#define code
16
17
typedefvoid(code*funcp)(void);
18
19
//#define t_res uchar // define time resolution 8 bit
20
#define t_res uint // define time resolution 16 bit
21
22
23
externucharkey_state;// debounced and inverted key state:
24
// bit = 1: key pressed
25
externucharkey_press;// key press detect or repeat
26
27
externucharf_timer_tick;// timer flag
28
29
30
externucharget_key_press(ucharkey_mask);
31
32
voidtimertick(void);
33
34
bittimeradd(funcpfunc,t_resdelay);
35
36
bittimerremove(funcpfunc);
37
38
voidtimerinit(void);
39
40
externvoidtest(void);
Woran könnte das liegen? Schonmal danke für deine Hilfe!
Hmmm.... mal sehen.... polier die Glaskugel ;-)
Da du den Code nicht vollständig gepostet hast, kann man nur raten.
Die ISR fehlt zum Beispiel.
Aber hier:
extern uchar f_timer_tick; // timer flag
Hier fehlt das volatile. Die Variable sollte natürlich auch als volatile
definiert sein. Das könnte der Fehler sein.
Hast du geprüft, ob deine ISR überhaupt anläuft?
Wird der Zweig in
if( f_timer_tick ) {...}
aufgerufen? Kannst du doch einfach prüfen (Leds einschalten).
Doch ich habe meinen ganzen Code gepostet. Meine main.c und meine
main.h. der Rest ist der scheduler von Peter Dannegger. Da habe ich ja
nichts verändert.
Bene Jan schrieb:> Doch ich habe meinen ganzen Code gepostet.
Stimmt, hab mal nachgesehen in dem Code von Peter. Vorher
fehlte mir dir ISR. Die ist dann im Modul TOV0.C
1) Setze mal volatile für die oben angegebene Variable.
2) Prüfe, ob die ISR anläuft.
3) Prüfe ob if( f_timer_tick ) {...} durchlaufen wird.
Bene Jan schrieb:> void test(void)> {> DDRC = 0b00000111;> PORTC = 0b00000000;> }
ääähm... wierum genau hast du denn deine leds angeschlossen..?
könnte dein problem vielleicht einfach nur sein, das du portC auf 0x00
stehen lässt...?
dunno.. schrieb:> könnte dein problem vielleicht einfach nur seinBene Jan schrieb:> Als Beispiel sollen> drei LEDs eingeschaltet werden. Wenn ich test() ausführe passiert dies> auch
@dunno: alles klar?
@Bene Jan: Wenn es funktioniert dann schreib es.
Hallo,
ich verwende schon lange den Taskscheduler von PEDA und habe hier eine
Beispiel mit kompletten Code veröffentlicht.
Beitrag "Re: 3fach Timer (einschaltverzögerung)"
Damit solltest Du zum Erfolg kommen !
Es gibt auch von mir einige Anpassungen, aber damit komme ich immer
sicher ans Ziel bei meiner Projekte.
1
utimer_tdelta;
kann 16-Bit oder 32-Bit je nach Problem haben und mit der Nutzung i.a.
von GPIOR0 - GPIOR2; siehe |main.h|:
Also mit oder ohne volatile ändert sich leider nichts.
Habe mal versucht in der if( f_timer_tick ) die LEDs einzuschalten.
Funktioniert nicht! Also wird f_timer_tick nicht 1. Was wiederrum heißt,
dass die ISR in TOV0.c nicht ausgeführt wird. Ich weiß aber noch nicht
warum?!?
Bene Jan schrieb:> Also mit oder ohne volatile ändert sich leider nichts.
Du mußt das volatile in zwei Modulen einsetzen, in dem ISR-Modul und in
main.h, überall wo eine Deklaration oder Definition von f_timer_tick
steht.
Um zu prüfen, ob die ISR durchlaufen wird, brauchst du doch nur dort
(also in der ISR die LEDs einschalten.
Ansonsten die Timerinitialisierung prüfen.
SIGNAL(...) für die Interrupttoutine ist alt. Evtl. liegt es daran, was
ich aber nicht glaube. Siehe avrlibc-Doku.
Ich hab jetzt nochmal volatile benutzt und auf einmal geht es!
Ich bin mir 100%-sicher das ich es eben genauso gemacht habe und da hat
es nicht funktioniert...
Bene Jan schrieb:> Ich hab jetzt nochmal volatile benutzt und auf einmal geht es!
Das ist doch schön :-)
> Ich bin mir 100%-sicher das ich es eben genauso gemacht habe und da hat> es nicht funktioniert...
Daran hab ich meine Zweifel und die darfst du auch haben ;-)
Hallo zusammen,
ich benutze den Scheduler in verschiedenen Projekten und dieser
funktioniert auch super. Jetzt würde ich gerne auch die Entprellfunktion
nutzen, diese funktioniert aber leider noch nicht bei mir:
Eingelesen werden sollen PE4 und PE5. Dort sind zwei Kurzhubtaster gegen
GND angeschlossen. Für dieses Beispiel werte ich aber nur PE5 aus!
In meinem Beispiel soll ein kleiner Text über die UART ausgegeben
werden. Ausgaben über die UART funktionieren einwandfrei, daran kann es
also nicht liegen.
Wenn ich den Controller starte wird 1x der Text ausgegeben, ohne das ich
den Taster drücke. Das kann ja auch schon nicht normal sein?!
Wo liegt mein Fehler?
Vielen Dank für eure Hilfe.
Hier mein Code:
getkey.c
1
#include"../main.h"
2
3
ucharget_key_press(ucharkey_mask)
4
{
5
cli();
6
key_mask&=key_press;// read key(s)
7
key_press^=key_mask;// clear key(s)
8
sei();
9
returnkey_mask;
10
}
tov0.c
1
#include"../main.h"
2
3
ucharf_timer_tick;
4
5
ucharkey_state;// debounced and inverted key state:
6
// bit = 1: key pressed
7
ucharkey_press;// key press detect or repeat
8
9
SIGNAL(SIG_OVERFLOW0)
10
{
11
staticcharct0,ct1;
12
chari;
13
14
i=KEY_INPUT&(1<<KEY4^1<<KEY5);
15
i^=~key_state;// key changed ?
16
17
ct0=~(ct0&i);// reset or count ct0
18
ct1=ct0^(ct1&i);// reset or count ct1
19
i&=ct0&ct1;// count until roll over ?
20
key_state^=i;// then toggle debounced state
21
key_press|=key_state&i;// 0->1: key press detect
22
23
f_timer_tick=1;
24
}
Veränderungen in der main.h:
1
#define KEY_INPUT PINE
2
#define KEY4 4
3
#define KEY5 5
main.c
1
TCCR0B=1<<CS02;//divide by 256 * 256; Timereinstellungen für Scheduler
Hast Du externe Pullups oder schaltest Du sie ein?
Bene Jan schrieb:> Hier mein Code:
Das ist kein Code, sondern Bröckchen, wie man sie einem Hund hinwirft.
Was soll damit jemand anfangen?
Er müßte ein neues Projekt machen und die Änderungen mühsam eintragen.
Und dabei können Fehler passieren.
Hänge das komplette compilierfähige Programm (als Zip) an. Es darf keine
Warnungen und Fehlermeldungen geben.
Willst Du Änderungen markieren, kommentiere die Originalzeile aus und
schreibe die neue darunter.
Oder nimm #ifdef, #else, #endif zum Unterscheiden.
Peter
Hallo Peter,
habe den Scheduler auch für den XMEGA umgeschrieben.
Leider habe ich in deinem Code keine Hinweise zu einer bestimmten Lizenz
gefunden. Unter welcher Lizenz steht der Code? Soll der Autor angegeben
werden?
Gruß Flo
Hallo zusammen,
hattet ihr das auch mal, dass in timeradd 'funcp func' außer für -O0
komplett wegoptimiert worden ist, und damit das Ganze bei aktivierten
Optimierungen kaputt gegangen ist (während es ohne Optimierungen so
funktioniert hat wie erwartet)?
Nachdem ich alles probiert habe, was ich kannte, um das Problem zu
beseitigen, und es mir nicht gelang, hab ich mich letztlich von der Idee
inspirieren lassen und das Ganze komplett neu geschrieben - allerdings
mit Pointerarithmetik. Jetzt läufts auch optimiert und braucht sogar ein
bisschen weniger Speicher im Flash.
Danke für die Vorlage! :)
Tom
Servus zusammen,
Ich versucht die original Dateien von Peter zum laufen zu bringen.
Ich habe den Code nur an folgenden Punkten angepasst.
In TOV0.c
SIGNAL (SIG_OVERFLOW0)
wurde geändert in:
SIGNAL (TIMER0_OVF_vect)
und signal.h habe ich rausgeschmissen.
Benutze dazu Atmel Studio 6 (Version 6.1.2730- SP2).
Leider bekomme ich nun eine Warnung die ich nicht ganz verstehe.
Warning 1 suggest parentheses around arithmetic in operand of '^'
[-Wparentheses]
Kann mir einer sagen was da falsch läuft?
Viele Grüße
Tim
Die Warnung sagt doch exakt, was dort moniert wird. Der Compiler spricht
eine Empfehlung aus. Versuche die Warnung zu übersetzen, dann wirst du
es auch wissen.
Ähm ja,
auf die Idee es mal zu übersetzen hätte ich auch kommen können. Sorry!
Aber vielen Dank für die schnelle Antwort.
Jetzt hangle ich mich mal an dem Code entlang um diesen genau zu
verstehen. Ich hoffe das nicht noch mehr so dumme Fragen von mir kommen
:-(
Viele Grüße
Tim
Hallo zusammen,
ich stelle mich mal wieder zu Blöd an. Ich bekomme den Code nicht ans
laufen.
Ich habe zwei Taster an PC2 und PC3 und zwei LEDs an PC4 und PC5.
Ich bekomme den Code nicht so abgeändert das dieser läuft.
Ich habe auch noch nicht kapiert welchen Taster er in dem Code benutzt,
vielleicht kann mir das einer erklären. Ich würde den Code so gerne
verstehen...
Ich habe mir einen Mega8 mit 16Mhz.
Viele Grüße
Tim
Hallo zusammen,
mein Code läuft nun. Der Code von Peter (UART mit FIFO) läuft auch.
Peter schreibt aber so Knackig das ich Schwierigkeiten habe alles zu
versehen.
Hat jemand von euch den Scheduler und UART Code verheiratet und kann mir
mal ein Codebeispiel schicken bzw. hier ablegen.
Das würde mir vielleicht auch helfen, beide Codes ein wenig mehr zu
verstehen.
Vielen Dank und Grüße
Tim
Tim,
sag mal was soll man da verheiraten ?
Das sind zwei C Module ohne gemeinsame Schnittstellen.
Sind vielleicht die C Grundlagen, bzw. das C Projektmanagement das
Problem ?
Da ich mir C mehr oder weniger selber beigebracht habe, fehlt es hier
und da auch an den Grundlagen.
Ich möchte gerne mehrere Prozesse gleichzeitig laufen lassen.
Sprich ich möchte ein Rollo von der Taste aus fahren lassen bzw. diese
Rollo über eine RS232 Schnittstelle Steuern können.
Deshalb muss ich in den Scheduler auch die UART einbinden.
Es ist ja Theoretisch möglich das während eines Schaltbefehl (Empfang am
Rollo) einer die Taste für Rollo hoch oder runter drückt.
Ich versuche bestehenden Code zu verstehen um in meinem Code weiter zu
kommen.
Viele Grüße
Tim
Wie ich oben schon schrieb habe ich den Code nicht ganz verstanden.
Kann mir einer sagen wie ich die Zeitschleife abbreche?
Also durch Drücken der zweiten Taste. LED ausmachen ist kein Problem
aber wie "lösche" ich die Zeit? Die Zeit läuft im Hintergrund natürlich
weiter.
Danke uns schönes WE
Tim
Tim H. schrieb:> Deshalb muss ich in den Scheduler auch die UART einbinden.
In den Scheduler eher nicht, sondern in die Mainloop:
1
for(;;){
2
if(taste_gedrueckt)
3
mache_was();
4
if(byte_empfangen)
5
mache_auch_was();
6
}
Sind mehrere Byte für ein Kommando nötig, ist das kein Beinbruch.
Die Behandlungsroutine sammelt dann eben die Bytes in einen Puffer, bis
sie das Paketende erkennt und parst es dann.
Peter Dannegger schrieb:> Tim H. schrieb:>> Kann mir einer sagen wie ich die Zeitschleife abbreche?>> bit timerremove( funcp func );
Danke Peter, hätte ich selber sehen können. Das ist ein super Sache,
vielen dank fürs einstellen des Codes. Hoffentlich verstehe ich ihn auch
irgendwann zu mehr als 50%...
Peter Dannegger schrieb:> Tim H. schrieb:> Sind mehrere Byte für ein Kommando nötig, ist das kein Beinbruch.> Die Behandlungsroutine sammelt dann eben die Bytes in einen Puffer, bis> sie das Paketende erkennt und parst es dann.
Das mit empfangen von 1 Byte habe ich hinbekommen. Also mit Auswertung
in einer IF-Abfrage. Aber wie mache ich das mit mehreren Bytes, ich habe
das in deinem Beispiel nicht verstanden wie du den Puffer verwendest. Da
ich ja zum Beispiel den String "Test1" auswerten will. Wenn aber "Test2"
kommt soll er natürlich nix tun, bis der "Test1" sieht.
Entschuldigt bitte die blöden Fragen, aber ich möchte den Code
wenigstens ein bisschen verstehen, da ich ihn in meinem Haus noch ein
paar mal einsetzen will.
Viele Grüße
Tim