mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Programm bzw. Ablaeufe steuern


Autor: Thomas S. (thomass)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Besserwisserle (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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
...

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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...

Autor: Thomas S. (thomass)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> 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.

Autor: Thomas S. (thomass)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ 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

Autor: Thomas S. (thomass)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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

Autor: 6632 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Matthias Lipinsky (lippy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Hans-jürgen Herbert (hjherbert) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Thomas S. (thomass)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Thomas S. (thomass)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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

Autor: Thomas S. (thomass)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Berti (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ja kooperatives Multitasking mit einfachem dispatcher -> siehe Anhang!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: jupp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Thomas S. (thomass)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Thomas S. (thomass)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Klaus W. (Firma: privat) (texmex)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Thomas S. (thomass)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Klaus W. (Firma: privat) (texmex)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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 :-).

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.