Datum:
Hallo Programmierer, ich habe ein kleines Problemchen und möchte hierzu mal ein paar Lösungsansätze sammeln. Konkret geht es um folgendes: Ich habe ein Transpondersystem per RS485 mit meinem Mega128 verbunden. Die Kommunikation steht und funktioniert soweit. Das Ganze ist ein Master/Slave System --> Master Mega128. Nun habe ich die Abläufe bis jetzt in meinen Programmen immer mittels hochzählen einer Variablen erledigt jedoch ist mir das nun etwas zu unübersichtlich. Ich habe es immer wie folgt gemacht, nennen wir die Variable mal var. 1. var = 1 2. suche nach einem Transponder --> var = 2 3. Transponder gefunden --> var = 3, nicht gefunden var = 1 4. erstes Byte auslesen senden var = 4 5. ACK mit Byte empfangen, Byte speichern var = 5 6 zweites Byte auslesen senden var = 6 7. ACK mit Byte empfangen,Byte speichern var = 7 ... 18.achtes Byte auslesen senden var = 16 19. ACK mit Byte empfangen,Byte speichern var = 17 20. Werte vergleichen var = 17 21 wenn Wert bekannt var = 18 ansonsten var 30 22. grüne LED am Leser einschalten var = 19 usw. Doch man sieht schon es wird schon unübersichtlich. Die ganze Abfragen der Variable habe ich immer mit einem switch case gemacht und das ganze in eine Funktion gepackt. Wie steuert Ihr solche Abläufe? Gebt mir bitte ein Paar Ansätz wie Ihr das macht. Danke schon mal für die Anregungen. Thomas
Datum:
Das was Du andeutest nennst sich Statusmaschine oder state machine. Googel mal danach. Ach ja: Keine Zahlenangaben, also besser #define DEF_STATE 0 #define MAIN_STATE 1 #define NOCHN_STATE 2 ...
Datum:
Besserwisserle wrote: > Ach ja: Keine Zahlenangaben, also besser > > #define DEF_STATE 0 > #define MAIN_STATE 1 > #define NOCHN_STATE 2 > ... ...oder, wenn es wirklich C sein soll (was im OP leider nirgends Erwähnung findet) ne enum nehmen...
Datum:
@ Besserwisserle, OK damit vergebe ich eben indirekt auch Zahlen die eben per define als ein Statusnamen vergeben sind. Wenn ich nun aber 100 Statuse habe ist es auch unübersichtlich. Es ist ja fast das gleiche ob mit einer variablen oder mit einem definiertem Namen zu arbeiten. @Johannes, ja ich programmiere in C. Thomas
Datum:
> Doch man sieht schon es wird schon unübersichtlich. > Die ganze Abfragen der Variable habe ich immer mit einem Man kann sich das ganze aber auch vor dem Programmieren auf einem grossen Blatt Papier aufmalen. Jeder Zustand ist ein Kreis. Pfeile symbolisieren Zustandsübergänge. Neben den Pfeil schreibst du die Bedingung die gelten muss, damit dieser Übergang genommen werden kann. Und du schreibst neben den Pfeil die Aktion die ausgeführt werden muss, wenn dieser Übergang genommen werden muss. Und schon hast du ein schönes Schaubild, welches deine Statemaschine beschreibt.
Datum:
@ Karl-Heinz, Danke für die Deine Beschreibung/Visualisierung. Wie es scheind gibt es nur diesen einen Weg einer Statemashine. Werde mal den Vorschlag mir define zu arbeiten anstatt mir Zahlen versuchen. Dies scheind dann doch übersichtlicher zu sein. Ich wollte nur mal hören wie Ihr das so macht und einige Anregungen sammeln. DANKE an ALLE !!!!!!!!!!!!!!!!!!!!!!!! Thomas
Datum:
@all, nun hätte ich noch eine Unklarheit zu beseitigen. Wie empfiehlt es sich mit der Statemashine zu zählen. Bsp 1. 0x0001 --> 0x0002 --> 0x0004 --> 0x0008 --> 0x0010 --> usw. Bsp 2. 0x0001 --> 0x0002 --> 0x0003 --> 0x0004 --> 0x0005 --> usw. Ich denke Bsp 1 hat einige Vorteile da man die einzelnen Bits eine Bedeutung haben und man sie auch auskommentieren kann mit einer & Verknüpfung. Wie ist Eure Meinung? Thomas
Datum:
Ich arbeite auch bevorzugt mit Statusmaschinen und benutze Zahlen, die ich eins incrementiert hochzaehle. Der Vorteil ist, dass man eine Sprungtabelle einsetzen kann, was sich ab einer Handvoll Zustaende lohnt.
Datum:
Eher Beispiel zwei. ABer da ja per #define den Schritten namen zugewiesen werden, spielt das keine Rolle. Ich verwende immer Zahlen, keine Bitbedeutungen. Ich programmiere states immer so: Hier hab ich eine regelmäßige Aktion, Transitionen, Zeitüberwachungen und Eingangsaktionen... Funktioniert sehr gut ;-)
#define u8SmSchritt0 0 #define u8SmSchritt4 1 #define u8SmSchritt5 2 //... uint8_t _u8SmAblauf = u8SmSchritt0; uint8_t _u8SmAblaufLast = 255; //.. switch _u8SmAblauf { //... case c_u8SmSchritt4: //-- ENTRY ------------------------------------ if ( _u8SmAblaufLast <> _u8SmAblauf ) { _u8SmAblaufLast = _u8SmAblauf; _u16SmAblaufTime = TCNT0; // zB 1kHz Timertakt // einmalige actionen hier, zB ausgang setzen PORTD |= 0x01; } //-- ACTION------------------------------------ // regelmäßige actionen im schritt hier //-- TRANSITION ------------------------------- //-- irgendeine weiterschaltbed. --- if ( Variable == 0x4543 ) _u8SmAblauf = c_u8SmSchritt5 //-- andere weiterschaltbed. ------- if ( PINC & (1<<PINC3) ) _u8SmAblauf = c_u8SmSchritt1 //-- Zeit rum 16,332sek ------------ if ( (TCNT0-u16SmAblaufTime ) > 16332 ) _u8SmAblauf = c_u8SmSchritt7 //... case c_u8SmSchritt5: } |
Jeder Schritt sieht bei mir identisch aus. Zumindest vom Aufbau her. Die ACTION bleibt meistens leer, da in den meisten Schritten nur ein Ausgang gesetzt werden muss und auf einen Eingang gewartet wird. zB.
Datum:
Wenn es schneller gehen soll, benutze ich als Speicher für den Zustand
einen Pointer auf eine Funktion.
void (*sf)(void);
Die möglichen Zustände werden erst vordefiniert:
extern void sf_start(void);
extern void sf_zustand2(void);
Dieser Pointer wird beim Start mit der adresse der Funktion
initalisiert, die als erstes laufen soll:
sf = sf_start ;
In jeder der Funktionen wird bei der Entscheidung, dass dieser Zustend
verlassen werden soll die Zieladresse des nachfolgenden Zustandes in den
Pointer geschrieben:
if (...) sf = sf_zustand2 ;
In der Hauptschleife wird die richtige Funktion für den aktuellen
Zustand aufgerufen:
for(;;)
{
(*sf)();
}
Die einzelnen Zustandsfunktionen werden als einzelne Unterprogramme
geschrieben:
void sf_start( void )
{
...
if (...) sf = sf_zustand2 ;
}
Das spart die vielen Abfragen in switch-case, kostet aber einen
indirekten Funktionsaufruf mit entry und return.
Datum:
@Matthias Lipinsky wrote: Hallo Mathias, ich habe mich nun immer wieder mit meinem Problem beschäftigt und habe nun Deinen Vorschlag nochmals genauer angeschaut. Nur einige Sachen sind mir nicht so klar und bevor ich mein Programm komplett zerlege möchte ich alles zumindest richtig verstanden wissen. Du schreibst in Deinem Beispiel
#define u8SmSchritt0 0 #define u8SmSchritt4 1 #define u8SmSchritt5 2 //... |
weiter unten machst Du dann div. Zuweisungen z.B. _u8SmAblauf = c_u8SmSchritt5
> switch _u8SmAblauf { //... case c_u8SmSchritt4: //... if ( Variable == 0x4543 ) _u8SmAblauf = c_u8SmSchritt5 //-- andere weiterschaltbed. ------- if ( PINC & (1<<PINC3) ) _u8SmAblauf = c_u8SmSchritt1 //-- Zeit rum 16,332sek ------------ if ( (TCNT0-u16SmAblaufTime ) > 16332 ) _u8SmAblauf = c_u8SmSchritt7 //... case c_u8SmSchritt5: |
Also definiert hast Du u8SmSchritt4 und in der SwitchCase Anweisung verwendest Du c_u8SmSchritt4. Ist dieses "c_" ein Schreibfehler oder bedeutet es etwas was ich noch nicht verstehe? Sorry fast Anfänger Thomas
Datum:
Thomas S. wrote: > Also definiert hast Du u8SmSchritt4 und in der SwitchCase Anweisung > verwendest Du c_u8SmSchritt4. > Ist dieses "c_" ein Schreibfehler oder bedeutet es etwas was ich noch > nicht verstehe? Falls Matthias momentan nicht online ist und du auch mit einer Antwort von mir Vorlieb nimmst: Das sieht in der Tat nach einem Tippfehler aus, der entstanden ist, als er ein Beispiel hier für Forum zusammenkopiert hat. Geht man von einem Tippfehler aus, macht alles Sinn. Unterstellt man, dass mit dem c_ (welche wahrscheinlich eine const-ante kennzeichnen soll) ein anderer Mechanismus ins Spiel kommt, ergibt sich so gut wie kein Sinn mehr. -> wahrscheinlichste Lösung des Rätsels: Tippfehler beim Zusammenstellen des Postings. Kommt immer wieder mal vor, egal wie sorgfältig man versucht solche Dinge zu vermeiden.
Datum:
@Karl Heinz, danke für die schnelle Antwort deinerseits. Ich hatte mir das schon gedacht war mir nur nicht sicher. Nun hätte ich noch eine Frage bzw. ich brauche einen Denkanstoß. Ich habe alles so programmiert das alles zeitabhängig abläuft per Timer im 1ms Takt. Ich habe insgesamt 16 zeitgeteuerte Abläufe in der ISR wir ein Integer hochgezählt im 1ms Takt. Nun muss ich in der ISR bestimmen welcher Timer abgelaufen ist und dies irgendwie speichern damit es in main also in dieser Schrittkette bearbeitet werden kann und das ist momentan mein Problem. ich habe gedacht in 2 byte Werte bitweise speichern also jedes Bit steht für einen Timer. In der SwitchCase Schrittkette prüfen welcher bearbeitet werden muss, ich habe mir den Code ungefähr so vorgestellt.
switch _u8SmAblauf { //... case c_u8SmSchritt4: //-- ENTRY ------------------------------------ if ( _u8SmAblaufLast <> _u8SmAblauf ) { _u8SmAblaufLast = _u8SmAblauf; _u16SmAblaufTime = TCNT0; // zB 1kHz Timertakt // einmalige actionen hier, zB ausgang setzen PORTD |= 0x01; } //-- ACTION------------------------------------ // regelmäßige actionen im schritt hier //-- TRANSITION ------------------------------- //-- irgendeine weiterschaltbed. --- if ( Variable >= 0x80 ) { _u8SmAblauf = u8SmSchritt2; Variable -= 0x80; break; } if ( Variable >= 0x40 ) { _u8SmAblauf = u8SmSchritt2; Variable -= 0x40; break; } if ( Variable >= 0x20 ) { _u8SmAblauf = u8SmSchritt2; Variable -= 0x20; break; } if ( Variable >= 0x10 ) { _u8SmAblauf = u8SmSchritt2; Variable -= 0x10; break; } if ( Variable >= 0x08 ) { _u8SmAblauf = u8SmSchritt2; Variable -= 0x08; break; } //... case c_u8SmSchritt5: //... } |
So würde ich immer nach dem höchsten Bit suchen und eins nach dem anderen abarbeiten. Würde das funktionieren ?!? :-) Thomas
Datum:
Hallo nochmal, ich habe vergessen zu erwähnen das meine Timer zwischen 20ms und 10 Sekunden liegen. Es kann natürlich vorkommen das einmal gleichzeitig mehrer Timer aktiv sind und bearbeitet werden müssen. Jedoch zeitlich sollte dies jedoch kein Problem darstellen ich möchte eben durch die Umstellung erreichen das Abläufe sicherer ablaufen. Da ich zur Zeit nur Integer und Longs verwende und diese bitweise &= und |= verknüpfen dauert das immer und dabei können auch Interrupt aktiviert werden daher wird ca.30 Mal pro Tag ein Timer nicht wieder aktiviert. Das habe ich nun zwar im Griff jedoch möchte ich die Programmierung/Abläufe vereinfachen und störungsfreier machen. Gruß Thomas
Datum:
Thomas S. wrote: > Ich habe insgesamt 16 zeitgeteuerte Abläufe in der ISR wir ein Integer > hochgezählt im 1ms Takt. > Nun muss ich in der ISR bestimmen welcher Timer abgelaufen ist und dies > irgendwie speichern damit es in main also in dieser Schrittkette > bearbeitet werden kann und das ist momentan mein Problem. Das würde ich ehrlich gesagt nicht in der Satetmaschine machen, sondern in die ISR verlagern. Ein bischen was darf in einer ISR schon passieren. Und 16 Software-Timer überprüfen, ob sie auf 0 runtergezählt haben, ist noch nicht die Welt. Wenn der nächste ISR sowieso erst 1ms später kommt, bleibt noch massig Zeit. Bist du knapp an Speicher? Wenn nein: Würde da nicht lange mit Speicher knausern.
struct Timer { uint16_t Value; // zählt von einer vorgegebenen Zeit runter // auf 0. Zeit wird in ms angegeben }; struct Timer AllTimers[ 16 ]; #define NR_TIMERS ( sizeof( AllTimers ) / ( sizeof( *AllTimers ) ) ) ISR( .... ) { MilliSekunden++; for( i = 0; i < NR_TIMERS; ++i ) { if( AllTimers[i].Value > 0 ) AllTimers[i].Value--; } } |
und in der Statemachine setzt du dann einfach den Zeitbedarf beim richtigen Timer in dessen Value ein. Die ISR tickt diesen Wert mit Millisekunden-Auflösung runter. Ist die Zeit abgelaufen, dann wird der Value Wert des Timers zu 0 und bleibt auch 0. Deine Statemachine muss also nur (per Zustand) warten, dass Value 0 geworden ist - dann ist seine Zeit abgelaufen.
Datum:
Thomas S. wrote: > Das habe ich nun zwar im Griff jedoch möchte ich die > Programmierung/Abläufe vereinfachen und störungsfreier machen. Hmm. Darf ich dir eine Idee unterbreiten? Das Multitasking des kleinen Mannes. Jeder Task ist eine Statemachine und wird Round Robin massig durchgeschaltet. Mit ein klein wenig Selbstdisziplin (Warteschleifen sind verboten) ist sowas ganz einfach realisierbar und sehr zuverlässig. Ich stell mal was zusammen und poste wieder, wenn ich was habe
Datum:
Angehängte Dateien:Ja kooperatives Multitasking mit einfachem dispatcher -> siehe Anhang!
Datum:
Berti hats erfasst. Erstaunlich, wie sich unsere Tasks gleichen :-) Hier mit der zugehörigen Infrastruktur. Das eignet sich dann auch wunderbar dafür, dass jeder Task in eine eigene *.c Datei ausgelagert wird, sodass man in einem File immer nur einen Themenkreis (eine Aufgabe) behandeln muss ohne sich um die anderen kümmern zu müssen. Wie gesagt: Die einzige Einschränkung ist: Warteschleifen sind verboten. Statt dessen gibt es in der Statemaschine einen Zustand, der die Ablaufbedingung (Timer runtergezählt, Eingangspin hat erwarteten Wert, ...) überprüft und gegebenenfalls in einen neuen Zustand wechselt. Ist die Bedingung nicht erfüllt, bleibts beim Wartezustand und die Statemachine gibt die Kontrolle wieder an den Scheduler ab.
#include <avr/io.h> #include <avr/interrupt.h> // // Timer Verwaltung // struct Timer { uint16_t Value; // zählt von einer vorgegebenen Zeit runter // auf 0. Zeit wird in ms angegeben }; struct Timer AllTimers[ 16 ]; #define NR_TIMERS ( sizeof( AllTimers ) / ( sizeof( *AllTimers ) ) ) void StartTimer( uint8_t Nr, uint16_t Time ) { if( Nr < NR_TIMERS ) { cli(); AllTimers[Nr].Value = Time; sei(); } } uint8_t IsTimerStopped( uint8_t Nr ) { uint16_t curVal; cli(); curVal = AllTimers[Nr].Value; sei(); return curVal == 0; } ISR( .... ) // wird alle Millisekunden aufgerufen { uint8_t i; for( i = 0; i < NR_TIMERS; ++i ) { if( AllTimers[i].Value > 0 ) AllTimers[i].Value--; } } // // Taskverwaltung. Jeder Task ist eine Statemachine für sich selbst // typedef uint8_t (*ExecuteFnct)( uint8_t ); struct Task { uint8_t currentState; ExecuteFnct execute; }; struct Task AllTasks[8]; #define MAX_TASKS ( sizeof( AllTasks ) / sizeof( *AllTasks ) ) void RegisterTask( uint8_t Nr, ExecuteFnct fnct, uint8_t StartState ) { if( Nr < MAX_TASKS ) { AllTasks[Nr].currentState = StartState; AllTasks[Nr].execute = fnct; } } void RunScheduler() { static uint8_t NextTask = 0; if( AllTasks[NextTask].execute ) AllTasks[NextTask].currentState = (*AllTasks[NextTask].execute)( AllTasks[NextTask].currentState ); NextTask++; if( NextTask == MAX_TASKS ) NextTask = 0; } // // und jetzt die Tasks .... // // States die alle Tasks haben #define INIT 0 // // ***************************************************************** // Task: Alle 2 Sekunden eine Aktion ausführen // States nur für den 2 Sekunden Task #define WAIT_2S 1 #define WORK_2S 2 uint8_t Blink_2S( uint8_t state ) { switch( state ) { case INIT: StartTimer( 0, 2000 ); // Timer starten, 2 Sekunden return WAIT_2S; // beim nächsten Aufruf soll die Statemachine im Zustand WAIT_2S sein break; case WAIT_2S: if( !IsTimerStopped( 0 ) ) // Timer abgelaufen? return WAIT_2S; // nein: Task bleibt im Zustans WAIT_2S return WORK_2S; // ja: nächster Zustand ist: 2 Sek sind vorbei, jetzt wird gearbeitet! break; case WORK_2S: PORTA = PORTA ^ 0x01; // na ja, soviel gibts nicht zu tun StartTimer( 0, 2000 ); return WAIT_2S; break; } return state; } // // ***************************************************************** // Task: Alle 9 Sekunden eine Aktion ausführen // States nur für den 9 Sekunden Task #define WAIT_9S 1 #define WORK_9S 2 #define TASK_9_TIMER 1 #define WAIT_TIME_9 9000 uint8_t Blink_9S( uint8_t state ) { switch( state ) { case INIT: StartTimer( TASK_9_TIMER, WAIT_TIME_9 ); // Timer starten, 9 Sekunden return WAIT_9S; // beim nächsten Aufruf soll die Statemachine im Zustand WAIT_9S sein break; case WAIT_9S: if( !IsTimerStopped( 1 ) ) // Timer abgelaufen? return WAIT_9S; // nein: Task bleibt im Zustans WAIT_9S return WORK_9S; // ja: nächster Zustand ist: 9 Sek sind vorbei, jetzt wird gearbeitet! break; case WORK_9S: PORTA = PORTA ^ 0x02; // na ja, soviel gibts nicht zu tun StartTimer( TASK_9_TIMER, WAIT_TIME_9 ); return WAIT_9S; break; } return state; } int main() { // // Timer initialisieren und ISR anhängen // // // Tasks registrieren // RegisterTask( 0, Blink_2S, INIT ); RegisterTask( 1, Blink_9S, INIT ); // // hui - und los gehts // ab jetzt arbeiten alle Tasks (fast) gleichzeitig // while( 1 ) { RunScheduler(); } } |
PS: Ich hab jetzt nicht auf maximale Performance geachtet. Ich wollte dir das Prinzip zeigen ohne da jetzt in kryptischen Code abzugleiten. Edit: Wenn du globale Variablen verwendest, kannst du aus allen Tasks auf sie zugreifen und so zb. Tasks miteinander kommunizieren lassen. Diese Zugriffe brauchen dann auch nicht speziell abgesichert werden, durch die Taskstruktur ist gewährleistet, dass es keine Probleme mit atomarem Zugriff geben kann.
Datum:
Das gibts auch als fertigen Code: Beitrag "Wartezeiten effektiv (Scheduler)" Mit dem besonderen Trick, daß die Ereignisse sortiert werden. Dadurch müssen nicht bei jedem Timertick sämtliche Timerslots verglichen werden. Periodische Ereignisse erreicht man ganz einfach, indem sie sich selber wieder reinstellen. Peter
Datum:
kennt ihr dieses framework: http://www.state-machine.com/products/index.htm hervorragendes statemachine-framework. - hierarchische sm - event-delivery/queues - time-events - rtos optional - tracer/logger kostenlos für private anwendung, sehr guter kostenloser support im forum
Datum:
Karl heinz Buchegger wrote: > >
> //... > void StartTimer( uint8_t Nr, uint16_t Time ) > { > if( Nr < NR_TIMERS ) { > cli(); > AllTimers[Nr].Value = Time; > sei(); > } > } > > uint8_t IsTimerStopped( uint8_t Nr ) > { > uint16_t curVal; > > cli(); > curVal = AllTimers[Nr].Value; > sei(); > > return curVal == 0; > } > > //... > |
Ich habe nun mal Deinen Code durchgearbeitet damit ich alles verstehe wie es so funktioniert da ich nicht so bewandert bin mit Strukturen musse ich mir noch einiges erarbeiten. Nun habe ich gesehen das Du beim setzen/stopen eines Timers immer die Interript's global abschaltest und danach wieder einschaltest. Ich habe das bei mir im Code nicht gemacht eventuell würde das schon mein Problem lösen. Meine generelle Frage ist sollte man dies immer so handhaben oder ist es nur Zufall das es hier so ist. Wenn der Interrupt wieder eingeschaltet wird werden dann alle Interrupts die in der Zwischenzeit aufgelaufen sind bearbeitet? Ich verwende einen MEGA128 also eigentlich kein Speicherproblem zur Zeit ist noch 40%Flash frei da mir die Displaysteuerung und die dazugehörigen Strings sehr viel Flash gefressen haben. Gruß und Danke schon mal Thomas
Datum:
Thomas S. wrote: > Meine generelle Frage ist sollte man dies immer so handhaben oder ist es > nur Zufall das es hier so ist. value ist eine 16 Bit Variable, muss also mittels 2 8-Bit Zugriffe gelesen/gesetzt werden. Ich will verhindern, dass mir ein Interrupt dazwischen knallt, wenn das Low-Byte schon geholt ist und das High-Byte noch nicht. Stichwort: atomarer Zugriff. > Wenn der Interrupt wieder eingeschaltet wird werden dann alle Interrupts > die in der Zwischenzeit aufgelaufen sind bearbeitet? ja
Datum:
Karl-Heinz, Danke für Deine schnelle Antwort. Ich verwende ja zur Zeit ein Integer bei dem jedes Bit für einen Timer steht und mittels
if(TimerStatus & 0x08) { //... CODE } |
schaue ich ob der Timer abgelaufen ist (BIT = 1) Nun habe ich im AVR-GCC-Tutorial etwas über Bitfelder gelesen. Könnte ich nun ein Bitfeld wie folgt anlegen
struct { unsigned Timer1:1; // 1 Bit für Timer1 unsigned Timer2:1; // 1 Bit für Timer2 unsigned Timer3:1; // 1 Bit für Timer3 unsigned Timer4:1; // 1 Bit für Timer4 unsigned Timer5:1; // 1 Bit für Timer5 unsigned Timer6:1; // 1 Bit für Timer6 unsigned Timer7:1; // 1 Bit für Timer7 unsigned Timer8:1; // 1 Bit für Timer8 //... unsigned Timer16:1; // 1 Bit für Timer16 } TimerStatus; |
Auslesen könnte ich es dann mit
if(TimerStatus.Timer1) { //... CODE } |
Würde das eine Verbesserung bringen im Bezug auf Schnelligkeit gegenüber dem ersten Beispiel? Gruß Thomas
Datum:
Hallo!! Erfahrungsgemäss weiss Herr Buchegger ja was er tut. Ich verstehe es nur mal wieder nicht :-). Karl heinz Buchegger wrote: > Thomas S. wrote: > >
> struct Timer > { > uint16_t Value; // zählt von einer vorgegebenen Zeit runter > // auf 0. Zeit wird in ms angegeben > }; > > struct Timer AllTimers[ 16 ]; > #define NR_TIMERS ( sizeof( AllTimers ) / ( sizeof( *AllTimers ) ) ) > |
Hätte man hier nicht einfach
#define NR_TIMERS 16 struct Timer AllTimers[ NR_TIMERS ]; |
schreiben können? Sicher wird beides zur Compilezeit evaluiert und es sollte doch auch der gleiche Code entstehen? Aber warum macht man das dann so? Viele Grüße, Klaus
Datum:
@ Karl Heinz, ich habe Deinen Code nun endlich durchgearbeitet hatte in den letzten Monaten teilweise keine Zeit/Lust aber das hat sich in den letzten Wochen geändert. Ich habe die funktionsweise Deines Codes verstanden und will es nun auch so probieren. Um nun alle Abläufe gleich zu machen will ich auch die UART Kommunikation dazu umstellen. Zur Zeit läuft die UART Verbindung recht problemlos das ganze ist per ISR gesteuert ohne WAIT etc. Ich habe mir nun überlegt auch Strukturen für die UART zu erstellen diese sollten der besseren Übersichtlichkeit halber in eine UART Struktur aufgeteilt werden und danach im RunScheduler() nacheinander abgearbeitet werden? Hier mal so wie ich es mir vorstelle.
void RunScheduler() { static uint8_t NextTask = 0,NextUARTTask = 0; if( AllTasks[NextTask].execute ) AllTasks[NextTask].currentState = (*AllTasks[NextTask].execute)( AllTasks[NextTask].currentState ); NextTask++; if( NextTask == MAX_TASKS ) NextTask = 0; if( AllUARTTasks[NextUARTTask].UARTexecute ) AllUARTTasks[NextUARTTask].currentUARTState = (*AllUARTTasks[NextUARTTask].UARTexecute)( AllUARTTasks[NextUARTTask].currentUARTState ); NextUARTTask++; if( NextUARTTask == MAX_UARTTASKS ) NextUARTTask = 0; } |
Ist dies empfehlenswert oder wäre es besser nur eine Struktur zu erstellen und alles und diese zu packen? Danke schon mal für die Antworten. Thomas
Datum:
Klaus W. schrieb: > Hätte man hier nicht einfach >
> #define NR_TIMERS 16 > struct Timer AllTimers[ NR_TIMERS ]; > |
> schreiben können? > > Sicher wird beides zur Compilezeit evaluiert und es sollte doch auch der > gleiche Code entstehen? Ja, hätte man auch machen können. > Aber warum macht man das dann so? Gewohnheit :-) Im Ernst: Die sizeof Variante hat den Vorteil, auch dann korrekt zu funktionieren, wenn man so etwas macht ....
int Values[] = { 1, 3, 7, 9 }; #define NR_VALUES ( sizeof( Values ) / sizeof( *Values ) ) |
...., wenn also der Compiler anhand der Initialisierungen die Arraygröße festlegt. Spielt in diesem konkreten Fall keine Rolle. Allerdings habe ich die Erfahrung gemacht, dass es gut ist, wenn man Dinge möglichst immer auf die gleiche Art und Weise löst.
Datum:
Thomas S. schrieb: > Ist dies empfehlenswert oder wäre es besser nur eine Struktur zu > erstellen und alles und diese zu packen? Wieso willst du für die UART eine eigene Taskstruktur aufbauen? UART ist ja nicht besonders zeitkritisch, so dass es m.E. nicht unbedingt notwendig ist, jeweils abwechselnd einen 'normalen' Task und einen 'UART' Task abzuarbeiten. Falls doch, könnte man immer noch in der Haupttaskliste abwechselnd einen 'normalen' Task und einen 'UART' Task einstellen. Kurz und gut: Ich seh eigentlich keinen Grund, dass der Scheduler wissen muss, das es spezielle Tasks, nämlich die UART Tasks, gibt.
Datum:
Karl heinz Buchegger schrieb: > Im Ernst: Die sizeof Variante hat den Vorteil, auch dann korrekt zu > funktionieren, wenn man so etwas macht .... > > ..... > Spielt in diesem konkreten Fall keine Rolle. Allerdings habe ich die > Erfahrung gemacht, dass es gut ist, wenn man Dinge möglichst immer auf > die gleiche Art und Weise löst. Ahja... wieder was gelernt :-).
