Frage

Ich bin mit einem mikrocontroller mit C51-core.Ich habe eine ziemlich aufwendige und große Unterprogramm aufgerufen werden muss alle 500 MS.Ein RTOS ist nicht verwendet wird.

Die Art und Weise, ich mache es jetzt ist, dass ich eine vorhandene Timer-interrupt von 10 ms.Setze ich ein flag nach jedem 50-interrupts, überprüft, treu in der Hauptprogramm-Schleife.Wenn das Flag wahr ist, ist das Unterprogramm aufgerufen wird.Das Problem ist, dass durch die Zeit, die das Programm-Schleife kommt die round Wartung der Flagge, es ist schon mehr als 500ms,manchmal sogar >515 ms im Fall von bestimmten code-Pfade.Die Zeit ist nicht genau vorhersehbar.

Offensichtlich, das Unterprogramm nicht aufgerufen werden, aus dem inneren der timer-interrupt aufgrund dieser großen Zeit, die es braucht, um Sie auszuführen.Das Unterprogramm braucht 50ms bis 89ms je nach verschiedenen Bedingungen.

Gibt es eine Möglichkeit, um sicherzustellen, dass die Unterroutine aufgerufen wird, in genau 500ms jedes mal?

War es hilfreich?

Lösung

Wäre das tun, was Sie brauchen?

#define FUDGE_MARGIN 2    //In 10ms increments

volatile unsigned int ticks = 0;

void timer_10ms_interrupt( void )  { ticks++; }

void mainloop( void )
{
    unsigned int next_time = ticks+50;

    while( 1 )
    {
        do_mainloopy_stuff();

        if( ticks >= next_time-FUDGE_MARGIN )
        {
            while( ticks < next_time );
            do_500ms_thingy();
            next_time += 50;
        }
    }
}

. NB: Wenn Sie hinter bekommen mit der Wartung Ihrer every-500ms Aufgabe dann wäre dies sie anstellen, was nicht sein kann, was Sie wollen

Andere Tipps

Ich glaube, Sie haben einige widersprüchliche / nicht durchdachte Anforderungen hier. Sie sagen, dass Sie nicht diesen Code aus dem Timer ISR nennen kann, weil es zu lange dauert, zu laufen (was bedeutet, dass sie eine niedrigere Priorität als etwas anderes ist, die verzögert werden würde), aber dann werden Sie durch die Tatsache getroffen, dass etwas sonst was mit niedrigerer Priorität sein sollte verzögert es, wenn Sie es aus dem Vordergrund Weg laufen ( ‚Programmschleife‘).

Wenn diese Arbeit muss in genau 500ms passieren, dann ist es aus der Timer-Routine ausgeführt werden, und befassen sich mit dem Fall-out von dem. Dies ist effektiv, was ein Präventiv RTOS würde ohnehin tun.

Wenn Sie es wollen, von der ‚Programmschleife‘ laufen, dann werden Sie sonst sicher als nichts machen müssen, das läuft von der Schleife immer mehr nimmt als die maximale Verzögerung Sie tolerieren können - oft das bedeutet, dass Ihre andere langfris brechen laufende Arbeiten in Zustandsmaschinen, die ein wenig Arbeit pro durch~~POS=TRUNC durch die Schleife tun.

Das glaube ich nicht, dass es eine Möglichkeit, es zu gewährleisten, aber diese Lösung kann eine akzeptable Alternative.

Darf ich vorschlagen nicht ein Flag gesetzt, sondern der Wert geändert wurde?

Hier ist, wie es funktionieren könnte.

1 / Start Wert auf Null.

2 / alle 10 ms unterbrechen, erhöhen Sie diesen Wert von 10 in der ISR (Interrupt-Service-Routine).

3 / In der Hauptschleife, wenn der Wert> = 500, subtrahieren 500 von dem Wert und tun Sie Ihre 500ms Aktivitäten.

Du musst vorsichtig sein, für Rennbedingungen zwischen dem Zeitgeber und Hauptprogramm zu modifizieren, um den Wert zu sehen.

Dies hat den Vorteil, dass die Funktion der 500ms Grenzen so nah wie möglich läuft unabhängig von Latenz oder Dauer.

Wenn aus irgendeinem Grunde Ihre Funktion in einer Iteration 20ms spät beginnt, wird der Wert bereits sein 520 so Ihre Funktion wird es dann auf 20 gesetzt, was bedeutet, es nur 480 ms vor der nächsten Iteration warten.

Das scheint mir der beste Weg, um zu erreichen, was Sie wollen.

ich die 8051 seit vielen Jahren nicht berührt haben (unter der Annahme, dass das, was C51 zielt, die eine sichere Wette gegeben scheint Ihre Beschreibung), aber es kann eine Anweisung haben, die 50 ohne eine Unterbrechung wobei subtrahieren. Allerdings scheine ich die Architektur war ziemlich einfach zu merken, so dass Sie deaktivieren müssen oder Verzögerung unterbricht, während es funktioniert die Last / ändern / Speicher-Betrieb.

volatile int xtime = 0;
void isr_10ms(void)  {
    xtime += 10;
}
void loop(void) {
    while (1) {
        /* Do all your regular main stuff here. */
        if (xtime >= 500) {
            xtime -= 500;
            /* Do your 500ms activity here */
        }
    }
}

Sie können auch zwei Fahnen verwenden - eine "Pre-Aktion" Flagge und einen "Trigger" Flag (mit Mike F ist als Ausgangspunkt):

#define PREACTION_HOLD_TICKS (2)
#define TOTAL_WAIT_TICKS (10)

volatile unsigned char pre_action_flag;
volatile unsigned char trigger_flag;

static isr_ticks;
interrupt void timer0_isr (void) {
   isr_ticks--;
   if (!isr_ticks) {
      isr_ticks=TOTAL_WAIT_TICKS;
      trigger_flag=1;
   } else {
      if (isr_ticks==PREACTION_HOLD_TICKS)
          preaction_flag=1;
   }
}

// ...

int main(...) {


isr_ticks = TOTAL_WAIT_TICKS;
preaction_flag = 0;
tigger_flag = 0;
// ...

   while (1) {
      if (preaction_flag) {
          preaction_flag=0;
          while(!trigger_flag)
             ;
          trigger_flag=0;
          service_routine();
      } else {
          main_processing_routines();
      }
   }
 }

Eine gute Möglichkeit ist es, ein RTOS oder schreib eine einfache RTOS zu verwenden.

Ein RTOS Ihre Bedürfnisse nur Folgendes tun müssen gerecht zu werden

  • Zeitplan periodische Aufgaben
  • Zeitplan Round-Robin-Aufgaben
  • Vorform Kontextwechsel

Ihre Anforderungen sind die folgenden:

  • eine periodische Aufgabe ausführen alle 500ms
  • in der zusätzlichen Zeit zwischen Round-Robin-Aufgaben (dabei nicht zeitkritische Operationen)
  • execute

Ein RTOS wie dies garantiert eine 99,9% Chance, dass Ihr Code auf Zeit ausgeführt wird. Ich kann nicht sagen, 100%, weil unabhängig von Operationen Sie tut in Ihrem ISR mit dem RTOS stören kann. Dies ist ein Problem mit 8-Bit-Mikrocontrollern, die nur eine Anweisung zu einem Zeitpunkt ausgeführt werden können.

ein RTOS Schreiben ist schwierig, aber machbar. Hier ist ein Beispiel für kleine (900 Zeilen) RTOS gezielt bei ATMEL 8-Bit-AVR-Plattform.

Hier finden Sie die Bericht und Code- erstellt für die Klasse CSC 460:. Echtzeit-Betriebssysteme (an der University of Victoria)

Eine einfache Lösung ist es, ein Timer-Interrupt zu haben, die bei 500ms feuert ...
Wenn Sie eine gewisse Flexibilität bei der Hardware-Design haben, können Sie die Ausgabe eines Timers zu einer zweiten Stufe Zähler kaskadieren Sie eine lange Zeitbasis zu erhalten. Ich vergesse, aber ich vage erinnern zu können, Timer auf der x51 kaskadieren.

Ah, eine weitere Alternative für die Prüfung - die x51-Architektur ermöglichen zwei Ebenen der Interrupt-Prioritäten. Wenn Sie einige Hardware-Flexibilität haben, können Sie einen der externen Interrupt-Pins bewirken bei 500ms Intervallen durch den Timer ISR angehoben werden, und dann lassen Sie die untere Ebene Interrupt-Verarbeitung Ihrer every-500ms Code auftreten.

auf Ihre speziellen x51 Je könnten Sie in der Lage sein, auch eine niedrigere Priorität zu erzeugen Interrupt vollständig intern auf Ihr Gerät.

Teil 11.2 in diesem Dokument Siehe ich im Web gefunden: http: / /www.esacademy.com/automation/docs/c51primer/c11.htm

Warum haben Sie eine zeitkritische Routine, die so lange dauert, zu laufen?

ich mit einigen der anderen einig, dass es kann hier ein architektonisches Problem sein.

Wenn der Zweck, die genaue 500ms (oder was auch immer) Intervalle Signaländerungen haben, in bestimmten Zeitintervallen auftreten, können Sie mit einem schnellen ISR besser dran, die die neuen Signale Ausgänge auf der Grundlage einer vorherigen Berechnung, und dann einen Satz Flag, das die neue Berechnung verursachen würde außerhalb des ISR auszuführen.

Können Sie besser beschreiben, was das Langlaufroutine tut, und das, was die Notwendigkeit für das spezifische Intervall ist für die?


Addition auf der Grundlage der Kommentare:

Wenn Sie versichern können, dass die Zeit in der Service-Routine von einer vorhersehbaren Dauer ist, können Sie mit fehlenden den Timer-Interrupt-Buchungen weg ...

Ihr Beispiel nehmen, wenn Ihr Timer-Interrupt für 10 ms Perioden eingestellt ist, und Sie wissen, dass Ihre Service-Routine 89ms nehmen, gehen Sie einfach weiter und 41 Timer-Interrupts zählen, dann 89 ms Aktivität tun und acht Timer-Interrupts verpassen (42. bis 49.).

Dann, wenn Ihre ISR-Exits (und löscht die anstehende Unterbrechung), die „erste“ Unterbrechung der nächsten Runde von 500ms wird eine ms später auftreten zu.

Da du bist „Ressource ausgereizt“ schlägt vor, dass Sie Ihre anderen Timer haben und Quellen auch im Gebrauch unterbrechen - was bedeutet, dass auf der Hauptschleife unter Berufung genau nicht zur Arbeit geht zeitlich gesteuert werden soll, weil die anderen Interrupt Quellen könnten im falschen Moment feuern.

Wenn ich interpretting Ihre Frage richtig, Sie haben:

  • eine main-loop
  • einige hohe Priorität Vorgang, der ausgeführt werden muss, alle 500 MS für eine Dauer von bis zu 89ms
  • einen 10ms timer, führt auch eine kleine Anzahl von Operationen.

Es gibt drei Möglichkeiten, wie ich es sehe.Die erste ist die Verwendung einer zweiten timer, der eine niedrigere Priorität für Ihre 500ms Operationen.Sie können weiterhin verarbeiten Ihre 10ms interrupt, und sobald der Vorgang abgeschlossen ist weiterhin Wartung Ihrer 500ms timer-interrupt.

Zweite option - doe, die Sie benötigen, um Ihre 10ms interrupt alle 10ms?Ist es irgendetwas anderes mache, als Zeit zu halten?Wenn nicht, und falls Ihre hardware es wird Ihnen erlauben, zu bestimmen die Anzahl von 10ms ticks, die vergangen sind, während die Verarbeitung Ihrer 500ms op ' s (dh.durch die nicht-Verwendung des unterbricht sich selbst), dann können Sie beginnen, Ihre 500ms op ' s innerhalb der 10ms interrupt und verarbeiten 10ms ticks, die Sie verpasst haben, wenn Sie fertig sind.

Dritte option:Folgen von Justin Tanner Antwort, es klingt wie Sie produzieren könnte Ihre eigenen präemptives multitasking die kernel-füllen Sie Ihre Anforderungen, ohne zu viel Mühe.Es klingt wie alles, was Sie brauchen, ist zwei Aufgaben - eine für die Haupt-super-loop und eine für Ihre 500ms Aufgabe.

Der code zum Umschalten zwischen zwei Kontexten (dh.zwei Kopien aller Ihrer Register, über unterschiedliche stack-Pointer) ist sehr einfach und besteht in der Regel aus einer Reihe von register schiebt (so speichern Sie die aktuellen Kontext), eine Serie der Registrierung erscheint (zum wiederherstellen Ihrer neuen Kontext) und die Rückkehr aus der interrupt-Anweisung.Sobald Ihre 500ms op ' s sind komplett, Sie zur Wiederherstellung der ursprünglichen Kontext.

(Ich denke, das streng dies ist eine Mischung aus preemptives und kooperatives multitasking, aber das ist nicht wichtig jetzt)


edit:Es ist ein einfaches vierte option.Reichlich Pfeffer Ihre Haupt-super-Schleife prüft, ob die 500ms vergangen ist, sowohl vor und nach jeder langen Operationen.Nicht genau das, 500ms, aber Sie können in der Lage sein zu reduzieren die Latenz auf ein erträgliches Maß.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top