mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Timer arbeitet mit zu hoher Toleranz


Autor: Harald Horn (harald_horn)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

die Anwendung habe ich zwar schon mal in einem anderen Thread 
beschrieben, aber mittlerweile habe ich in Detailproblem mit der Lösung.

Aufgabe:

Ein Ventil, eine Zündvorrichtung und ein Laser sollen einer präzisen 
zeitlichen Sequenz nach angesteuert werden. Das Ventil wird bis zu 300ms 
geöffnet. Nachdem es wieder geschlossen wurde, soll eine Zündvorrichtung 
ausgelöst werden. Nach erfolgter Zündung soll ein Messlaser ausgelöst 
werden.
Diese Sequenz soll über einen Taster ausgelöst werden und einmal 
durchlaufen werden.
Im Moment ist diese Funktion zu Testzwecken leicht abgeändert: Durch 
längeres Halten der Taste wird eine zyklische Auslösung der Sequenz 
bewirkt.

Das Ventil wird für einen Zeitraum zwischen 1ms und 300ms geöffnet; die 
geforderte Genauigkeit liegt bei +/-1ms.

Die Zündung wird durch eine Signalflanke ausgelöst. Sie soll zwischen 
5us und 10ms nach Ventilschluss erfolgen. Geforderte Genauigkeit: Nach 
Möglichkeit 1us, kann aber auch etwas ungenauer sein.

Für die Laserauslösung gelten die gleichen Vorgaben wie für die Zündung, 
außer, daß sie Zeitlich auf die Flanke der Zündauslösung bezogen werden 
anstatt auf den Ventilschluss.

Die Zeit für das Ventil wird über den Timer 0 erzeugt, was so weit gut 
funktioniert.

Die Zeit für Zündung und Laser soll über die Compareinterrupts von Timer 
1 erzeugt werden.

Wie das gerade realisiert wird, ist aus dem unten folgenden C-Code 
ersichtlich.

Das Problem ist nun folgendes: Betätigt man den Taster für die 
Sequenzauslösung länger, zeigt sich, daß die Zeit, die vom Ventilschluss 
bis zur Zündung vergeht, um ca. 30us von Auslösung zu Auslösung 
schwankt.
Hält man die Taste, so erkennt man, daß nur beim ersten Sequenzdurchlauf 
eine gro0ße, zeitliche Streuung auftritt und bei allen weiteren 
durchläufen die Schwankungsbreite innerhalb von 4us bleibt. Leider komme 
ich einfach nicht dahinter, woran das liegt und wäre für Hilfe sehr 
dankbar.

Anbei auch noch ein Oszillogramm des Problems. Kanal 4 zeigt die 
fallende Flanke, die das Ventil zusteuert und den Bezugspunkt für den 
Zündimpuls auf Kanal 1 den Zündimpuls. Bei dem Bild handelt es sich um 
die Aufzeichnung mehrer Sequenzläufe (Einstellung "Infinite Persistence" 
am Oszi). Der frühere Impuls auf Kanal 1 entsteht beim ersten 
Sequenzdurchlauf, die späteren Impulse entstehen beim Halten der 
auslösenden Taste. Wie man sieht, bewegen die Zeiten sich dann in einem 
wesentlich schmaleren Bereich.

Gruß,
Harald

Hier der C-Code:
void query()
//Diese Funktion fragt das Touchscreen sowie die angeschlossenen Taster zyklisch ab. 

{
  int index = 0;
  static char enc_last = 0x01;
  static char bargraph = VENTIL;
  char buffer_info[] = {0,0};
  int buffer_size = 0;
  char strng[4];
  char i = 0;
  static int upper_limit;
  static int lower_limit;
  static char toggle_seq=0;
  static char toggle_valve=0;
  char buffer_array[63];
  long out;


//********************** Abfrage Drehgeber ****************************
    i ^= 3;

  i -= enc_last;

  if ( !(PINB & (1<<PINB3)) )
  {
//buffer_array initialisieren
    for (index = 0; index < 63; index ++)
    {
      buffer_array[index] = 0;
    }

//************************* Daten vom Touchscreen holen ******************************

//Buffergröße etc. abfragen
    get_buffer_info(buffer_info);

//Bufferdaten holen
    get_buffer_data(buffer_array, buffer_info[0]);

//Dekodierung der empfangenen Daten    
    for (index = 7; index < buffer_info[0] + 8; index ++)
    {
      
      if (buffer_array[index] == 0x1B)
      {
          index++;
          switch (buffer_array[index])
          {
          case 'A':  //Touch-Schalter/Taster betätigt
            index++;
            index++;
            switch (buffer_array[index])
            {
              case VENTIL:
                bargraph = VENTIL;
              break;

              case ZUENDUNG:
                bargraph = ZUENDUNG;
              break;

              case LASER:
                bargraph = LASER;
              break;
            }  
          break;
          }
      }      
    }
  }

    if( i & 1 )
    {        // bit 0 = value (1)
      enc_last += i;      // store new as next last
//Abhängig vom im Touchscreen ausgewählten Bargraphen werden die Eingabewerte
//unterschiedlichen Variablen zugeordnet    
    switch(bargraph)
    {
      case VENTIL:
          bar_val_ventil += (i & 2) - 1;
//Der Drehgeber liefert pro Raste 4 Impulse. Daher ist der einglesene Wert immer um Faktor 4 zu groß,
//entsprechend müssen die Grenzen ebenfalls um Faktor 4 größer gewählt werden. 
        if (bar_val_ventil > 1200)
          bar_val_ventil = 0;

        if (bar_val_ventil < 0)
           bar_val_ventil = 1200;
//Ausgabe in msec
        out = (bar_val_ventil)/6;
        update_bargraph(VENTIL, out);
        out = bar_val_ventil/4;
        sprintf(strng, "%3d", out);
        print_string(strng, 'R', 226,9);
      break;
      
      case ZUENDUNG:
          
        if ((bar_val_zuendung < 400) || ((bar_val_zuendung == 400) && ((i & 2) - 1)<0)) 
          bar_val_zuendung += (i & 2) - 1;    // bit 1 = direction (+/-)
        else if ((bar_val_zuendung < 4000) || ((bar_val_zuendung == 4000) && ((i & 2) - 1)<0))  // && ((bar_val_zuendung/4 -  10) >= 100))
          bar_val_zuendung += 10*((i & 2) - 1);    // bit 1 = direction (+/-)
        //else if ((bar_val_zuendung <= 4000) && ((bar_val_zuendung/4 -  10) < 100))
        //  bar_val_zuendung += (i & 2) - 1;    // bit 1 = direction (+/-)
        else //if (bar_val_zuendung <=40000)
          bar_val_zuendung += 100*((i & 2) - 1);    // bit 1 = direction (+/-)

        if (bar_val_zuendung >= 200400)
          bar_val_zuendung = 0;

        if (bar_val_zuendung <= -4)
           bar_val_zuendung = 200000;

        out = bar_val_zuendung/1000;
        //out = bar_val_zuendung/6;
        update_bargraph(ZUENDUNG, out);
        out = bar_val_zuendung/4;
        sprintf(strng, "%5ld", out);
        print_string(strng, 'R', 226,49);
        sprintf(strng, "%6ld", bar_val_zuendung);
        print_string(strng, 'R', 226,69);
      break;

      case LASER:
          bar_val_laser += (i & 2) - 1;    // bit 1 = direction (+/-)

        if (bar_val_laser > 800)
          bar_val_laser = 0;

        if (bar_val_laser < 0)
           bar_val_laser = 800;
        out = bar_val_laser/4;
        update_bargraph(LASER, out);
        sprintf(strng, "%3d", out);
        print_string(strng, 'R', 226,29);
      break;
    }
     }

//Abfrage der Angeschlossenen Taster; es gibt folgene Taster:
// * Einzelzündung auslösen * Ventil öffnen/schließen * Laser auslösen * Einzelsequenz abfahren * Sequenz zyklisch fahren
    
//Sequenzstart
    if (!(PINA & (1<<PINA0)))  //Taster für Sequenzstart abfragen
    {
        _delay_ms(30);
        _delay_ms(30);
        _delay_ms(30);
        _delay_ms(30);
      if (toggle_seq == 0)
      {
        if (toggle_valve == 1)
        {
          delete_area(0,64,240,80);
          print_string("Sequenzstart bei geöffnetem", 'R', 0,64);
          print_string("Fahrventil nicht möglich.", 'R', 0,72);
        }
        else
        {
          toggle_seq = 1;
          print_sequence_running(BLINKEN);
          while (!(PINA & (1<<PINA0)));
        }
      }
      else
      {
        toggle_seq = 0;
        delete_area(0,64,240,125);
        print_ready();
        while (!(PINA & (1<<PINA0)));
      }
    }

//************************ Hier fangen die Probleme an... ******************************

//Einzelsequenz
    if (!(PINA & (1<<PINA1)))  //Taster für Einzelsequenz abfragen
    {
        //Prellzeit abwarten
        _delay_ms(30);
        _delay_ms(30);
        _delay_ms(30);
        _delay_ms(30);

//Keine Ausführung bei offenenm Fahrventil
        if (toggle_valve) 
        {
          delete_area(0,64,240,80);
          print_string("Einzelsequenz bei geöffnetem", 'R', 0,64);
          print_string("Fahrventil nicht möglich.", 'R', 0,72);
          _delay_ms(30);
          _delay_ms(30);
        }

//Keine Ausführung bei bereits laufender Sequenz
        else if (toggle_seq)
        {
          delete_area(0,64,240,80);
          print_string("Einzelsequenz bei laufender", 'R', 0,64);
          print_string("Sequenz nicht möglich.", 'R', 0,72);
        }
//Einzelsequenz abfahren        
        else
        {
          print_sequence_running(NORMAL);
          print_ready();

//Timer 0 zurücksetzen
          TCNT0 = 0;

//OCR1A mit Eingabewert vom Touch laden (= Zeit, die von Ventilschluss bis Zündung vergehen soll)          
//Der Timer wird zwar erst später benötigt, aber das Register kann bereits hier (=außerhalb der ISRn) 
//geladen werden
          //OCR1A = (bar_val_zuendung*10)/12;
//Das Register wird zu Testzwecken mit einem Festwert geladen          
          OCR1A = (4*10)/12;

//Compare Interrupt Timer 0 aktivieren
          TIMSK |= (1<<OCIE0);
        }
    }
//Zündung
    if (!(PINA & (1<<PINA2)))
    {
        _delay_ms(30);
        _delay_ms(30);
        _delay_ms(30);
        _delay_ms(30);
        if (toggle_seq)
        {
          delete_area(0,64,240,80);
          print_string("Einzelzündung bei laufender", 'R', 0,64);
          print_string("Sequenz nicht möglich.", 'R', 0,72);
        }
        else
        {
          beep(2);  
          if (toggle_valve)
          {
            delete_area(0,64,240,80);            
            print_string("Zündung erfolgt", 'R', 0,64);
            //single_shot();
            while (!(PINA & (1<<PINA2)));
            delete_area(0,64,240,80);
          }
          else
          {
            print_done();
            //single_shot();
            while (!(PINA & (1<<PINA2)));
            delete_area(0,64,240,80);
            print_ready();
          }
        }
    }
    
//Laser 
    if (!(PINA & (1<<PINA3)))
    {
      _delay_ms(30);
      _delay_ms(30);
      _delay_ms(30);
      _delay_ms(30);
      if (toggle_seq)
      {
        delete_area(0,64,240,80);
        print_string("Laser auslösen bei laufender", 'R', 0,64);
        print_string("Sequenz nicht möglich.", 'R', 0,72);
      }
      else
      {
        beep(2);  
        if (toggle_valve)
        {
          delete_area(0,64,240,80);
          print_string("Laser ausgelöst", 'R', 0,64);
          while (!(PINA & (1<<PINA3)));
          _delay_ms(100);
          delete_area(0,64,240,80);
        }
        else
        {
          print_activate();
          while (!(PINA & (1<<PINA3)));
          delete_area(0,64,240,80);
          print_ready();
        }
      }
  }

//Fahrventil
    if (!(PINA & (1<<PINA4)))
    {
        _delay_ms(30);
        _delay_ms(30);
        _delay_ms(30);
        _delay_ms(30);
      if (toggle_valve == 0)
      {
        if (toggle_seq == 1)
        {
          delete_area(0,64,240,80);
          print_string("Fahrventil öffnen bei laufen-", 'R', 0,64);
          print_string("der Sequenz nicht möglich.", 'R', 0,72);
        }
        else
        {
          toggle_valve = 1;
          print_valve_open(BLINKEN);
          while (!(PINA & (1<<PINA4)));
        }
      }
      else
      {
        toggle_valve = 0;
        delete_area(0,64,240,125);
        print_ready();
        while (!(PINA & (1<<PINA4)));

      }
    }
}

SIGNAL (SIG_OUTPUT_COMPARE0)
{

//Diese Funktion zählt die Überläufe von Timer 0 hoch; pro Überlauf vergehen 250usec incl. der Abarbeitung
//dieser Funktion, solange "counter" hochgezählt wird.
//Dieser etwas umständliche Weg ist nötig, da Timer 0 mit seinen 8 Bit die geforderte Zeitspanne zwischen
// 0 und 300ms nicht abdecken kann und Timer 1 mit seiner höheren Auflösung für andere Zwecke benötigt wird.

//"counter" zählt die Funktionsaufrufe
  static uint16_t counter = 0;

  if (counter == 0)
  {

//Fahrventil Open Command
    PORTD |= (1<<PD0);
  }

  counter++;

// Wenn "counter den Vorgabewert erreicht hat, wird die Schleife verlassen.
  //if (counter == (bar_val_ventil-2))

//"counter" wird zu Testzwecken mit einem Festwert verglichen
  if (counter == (6-2))
  //if (counter == 6)
  {

//"counter" zurücksetzen
    counter = 0;

//Fahrventil Close Command
    PORTD &= ~(1<<PD0);
    PORTD |= (1<<PD1);
  
  

//Compare-Interrupt Timer 0 deaktivieren

    TIMSK &= ~ (1<<OCIE0);

//Interrupt-Flags zurücksetzen; im Moment ist nicht klar, warum dies nötig ist. Wurde durch Versuch festgestellt.
    TIFR = 0xFF;

//Timer 1 zurücksetzen
    TCNT1 = 0;    //Timer zurücksetzen.

//Compare-Interrupt A/Timer 1 aktivieren.
    TIMSK |= (1<<OCIE1A);

//Timer 1 starten.    
    TCCR1B |= (1<<CS12)|(1<<WGM12);

//Mit "gezuendet" wird die Information, daß die folgende ISR fertig ist, übergeben.
//Aus Präzisionsgründen soll zwischen dieser und der Folgenden ISR nicht in andere PRogrammteile gewechselt werden.
    gezuendet = 1; 

//Interrupts aktivieren
    sei();

//Auf zweite ISR warten.
    while(gezuendet) {}    //Warten, bist der andere Timer-Interrupt fertig ist.

  }
}

void __attribute__ ((naked)) 
SIG_OUTPUT_COMPARE1A (void)
{
//Diese Funktion setzt den Ausgang, der die Zündung auslöst.

//Flag zur Bestätigung der Ausführung zurücksetzen
   gezuendet = 0;


//Impuls an PortD/Pin2 ausgeben
   asm volatile("sbi 0x12,0x2\n\t"  //PD2
          "nop\n\t"
            "nop\n\t"
          "nop\n\t"
          "nop\n\t"
            "cbi 0x12,0x2\n\t"
        //"cbi 0x39,0x4\n\t"
        "PUSH R24\n\t"        
        "LDS R24,0x0059\n\t"
        "ANDI R24,0xEF\n\t"
        "STS 0x0059,R24\n\t"
        "LDS R24,0x004E\n\t"
        "ANDI R24,0xFD\n\t"
        "STS 0x004E,R24\n\t"
        "POP R24\n\t"
            "reti\n\t"
          );

//Timer 1 anhalten.
        TCCR1B &= ~(1<<CS12);
}


void init()
{
  spi_init();
  DDRA = 0x00;        //Port A als Eingang
  PORTA = 0xFF;        //Interne Pull-Ups für Port A aktivieren
  
  TCCR1A |= (1<<COM1A0)|(1<<COM1B0); //Normal Mode mit TOP=ICR1
  TCCR1A &= ~((1<<COM1A1)|(1<<COM1B1)); //Compare Match toggled die Ausgänge
  DDRD = 0xFF;      //Port D als Ausgang; Compare-Ausgänge liegen auf PD4 und PD5
  MCUCR |= (1<<ISC01)|(1<<ISC00);//INT0(PD1) bei steigender Flanke
  OCR0 = 250;          //Timer 0 alle 250 Taktzyklen löschen

  //Das setzen der Taktrate in TCCR0 startet Timer 0
  TCCR0 |= (1<<CS01)|(1<<WGM01); //CLK/8 (1us/Taktzyklus), CTC

}  

void setup_display()
{

  cursor_off();
  clear_screen();

  touch_on(EIN);
  touch_inv(EIN);
  touch_beep(EIN);
  touch_style(17);

  set_touch_font(FGENEVA10PROP);

  open_radio_group(1);
    define_touch_switch("Ventil", 1, 20, 40, 15, 1, 4);
    define_touch_switch("Laser", 1, 40, 40, 15, 3, 5);
    define_touch_switch("Zündung", 1, 60, 40, 15, 2, 6);
  close_radio_group();

  define_bargraph(VENTIL, RECHTS, 45, 20, 203, 1, 0, 200, BALKEN, 1);
  define_bargraph(LASER, RECHTS, 45, 41, 230, 21, 0, 200, STRICH, 1);
  define_bargraph(ZUENDUNG, RECHTS, 45, 61, 230, 42, 0, 200, STRICH, 1);

  update_bargraph(1, 0);
  update_bargraph(2, 0);
  update_bargraph(3, 0);

  print_string("0", 'R', 226,9);
  print_string("0", 'R', 226,29);
  print_string("0", 'R', 226,49);
  print_ready();
}

int main()
{
  init();
  setup_display();
  set_protocol_params();
    sei();
  while(1)
  {
    query();
  }
}

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Harald Horn (harald_horn)

Kleiner Tipp. Lange Quelltexte als Anhang.

Um im Mikrosekundenbereich genaues Timing zu erreichen sollte man die 
Output Compare Funktion des Timers nutzen.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Interrupt SIG_OUTPUT_COMPARE1A sieht ja gruselig aus.

1)
SREG wird nicht gesichert und wiederhergestellt.

2)
Die Zeile "TCCR1B &= ~(1<<CS12);" wird nie erreicht.
(ist aber wohl im Inline-Asm-Teil auch nochmal vorhanden, habe die 
Konstanten dort nicht kontrolliert)

3)
gezuendet = 0;
Der Compiler wird hierfür das Register r1 verwenden. In einem Interrupt 
kann aber nicht sicher davon ausgegangen werden, dass es 0 enthält. Muss 
also vorher gesichert, auf 0 gesetzt, und danach wiederhergestellt 
werden.

Ich kann gar nicht sehen, welchen Vorteil das "naked" hier überhaupt 
haben soll, denn das was du damit "einsparst", ist genau das, was hier 
fehlt und zwingend nötig ist.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aha, ich sehe gerade, dass dieser Interrupt in einer Warteschleife 
innerhalb eines anderen Interrupts ausgeführt wird, daher soll das 
"naked" wohl so funktionieren (das fehlende Sichern des SREG ist aber 
trotzdem kritisch). Ich finde das ist eine ziemlich "verquere" 
Konstruktion. Warum pollst du das Compare-Flag im anderen Interrupt dann 
nicht einfach? Das erspart die Notwendigkeit des verschachtelten 
Interrupts.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich sehe keinen roten Faden in dem Gewurstel, wo wird denn nun die 
Sequenz getimed?

Ich sehe da haufenweise _delay_ms und print, damit kann man natürlich 
keinen Blumentopf gewinnen.
Die Displayzugriffe haben höchstwarscheinlich keine feste 
Ausführungszeit und _delay_ms auch nicht.


Genaue Timings müssen mit dem Timer gemacht werden, T1 hat ja 
Compareinterrupts mit denen kann man bequem Sequenzen erzeugen, bis auf 
1/XTAL genau, d.h. 50ns bei 20MHz.
Der Timer muß ständig durchlaufen, damit irgendwelche unbekannten 
Ausführungszeiten nichts verzögern.

Irgendwelche variablen Codefunktionen dürfen nicht in der Sequenz 
vorkommen bzw. müssen garantiert beendet sein bis zum nächsten 
Sequenzpunkt.

Das Prinizp ist etwa folgendes:
- Triggerevent
- setze Compareinterrupt auf 1. Sequenzpunkt:
  OCR1A = T1 + ZEIT_1;
- 1. Aktion
- Sleep (Sequenzpunkt beendet Sleep)
- setze Compareinterrupt auf 2. Sequenzpunkt:
  OCR1A += ZEIT_2;
- 2. Aktion
- Sleep
- setze Compareinterrupt auf 3. Sequenzpunkt:
  OCR1A += ZEIT_3;
- 3. Aktion
- Sleep
...
usw.



Peter

Autor: Harald Horn (harald_horn)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

> Um im Mikrosekundenbereich genaues Timing zu erreichen sollte man die
> Output Compare Funktion des Timers nutzen.

das stimmt, aber die Benutzung anderer Ports ist hier nich 
Problemursache.
Ich habe mir den Compare-Ausgang parallel zu PortD angeschaut, und die 
Flanken verschieben sich synchron zueinander um die gleiche 
Zeitdifferenz.

Gruß,
Harald

Autor: Andreas Vogt (tico)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sorry, aber der Entwurf ist für die Tonne. Fang nochmal von vorne an.

Zuerst malst Du Dir mal einen Zeitstrahl auf. Bei Null ist der 
Tastendruck. Von da ausgehend trägst Du alle Aktionen, die erfolgen 
sollen, an den entsprechenden Zeitstellen ein.
Dann suchst Du Dir den größten gemeinsamen ganzzahligen Teiler aller 
Differenzen zwischen zwei aufeinanderfolgenden Aktionen. Das ist dann 
die Zeitbasis für Deinen Timer. Du brauchst nur diesen einen Timer. 
Immer, wenn dieser Timer einen Interrupt auslöst, schaust Du - z.B. in 
einer Tabelle - nach, ob ein Ereignis ausgelöst werden muss, und wenn 
ja, welches. Das tust Du dann, bis alle Ereignisse erledigt sind. Dann 
schaltest Du den Timer wieder ab (oder die Interrupts aus), bis wieder 
die Taste gedrückt wird.

Beim schreiben des Programms beachtest Du folgende Regeln:
- sei() in der Interrupt-Routine ist verboten
- while() in der Interrupt-Routine ist verboten, es sei denn, der Inhalt 
der Schleife ist kurz und definitiv immer gleich kurz (gemeint ist 
natürlich die Ausführungszeit, nicht die Anzahl der Zeilen im Code).
- _delay_ms() ist überall verboten. Du brauchst diese Funktion nicht. 
Das Warten findet immer in der Schleife in der Funktion main() statt. 
Wenn es etwas zu tun gibt, wird ein Interrupt ausgelöst.

Gruss
Andreas

Autor: Harald Horn (harald_horn)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Peter,

ich hab mit jetzt mal die Vorschläge aus dem Forum eingehend zu Hirne 
geführt.

> Ich sehe da haufenweise _delay_ms und print, damit kann man natürlich
> keinen Blumentopf gewinnen.

Nun ja, die werden benötigt, um die Taster zu entprellen. Während der 
Sequenz sollten sie eigentlich nicht ausgeführ werden.

> Die Displayzugriffe haben höchstwarscheinlich keine feste
> Ausführungszeit und _delay_ms auch nicht.

Die Kommunikation mit dem Display ist ja auch erstmal beendet, bevor der 
erste Interrupt ausgeführt wird.

> Das Prinizp ist etwa folgendes:
> - Triggerevent
> - setze Compareinterrupt auf 1. Sequenzpunkt:
>   OCR1A = T1 + ZEIT_1;
> - 1. Aktion
> - Sleep (Sequenzpunkt beendet Sleep)
> - setze Compareinterrupt auf 2. Sequenzpunkt:
>   OCR1A += ZEIT_2;
> - 2. Aktion
> - Sleep
> - setze Compareinterrupt auf 3. Sequenzpunkt:
>   OCR1A += ZEIT_3;
> - 3. Aktion
> - Sleep
> ...
> usw.

Damit gibt es im speziellen Fall mehrere Pobleme:
-Ich benötige drei Schaltkanäle. Also kann ich nicht für alle 
Sequenzphasen den gleichen Compare-Ausgang benutzen. Oder meintest Du, 
daß nur der Compare Interrupt abgefragt und eine ISR ausgeführ werden 
soll?
In letzterem Fall ergibt sich das Problem, daß in eine us bestenfalls 8 
Maschinenbefehle passen. Ich muss jetzt aber irgendwie abfragen, wie oft 
die ISR schon gelaufen ist und nach diesem Kriterium entsprechend 
verzweigen, damit der passende Ausgang gesetzt wird. Das wird kaum in 8 
Befehlen zu machen sein, oder?

- Wie bekomme ich eine definierte Zeit zwischen Tastendruck und erstem 
Sequenzpunkt? Wenn der Timer durchläuft, kann er zum Startzeitpunkt 
einen beliebigen Wert haben.

- Es gibt da noch ein Skalierungsproblem: Die längste Zeit, die ich 
generieren muss, sind 300ms mit 1ms Präzision. Die kürzeste ist 50ms mit 
am besten 1us Präzision. Wenn ich den "großen" Timer1 so einstelle, daß 
ich eine Zykluszeit von 1us habe, dann genügt ein Timerdurchlauf nicht, 
um die 300ms zu erreichen. Daher rührt auch der Ansatz mit zwei Timern.

Im Moment ist der Stand jetzt der, daß ich eine Auflösung von 2us 
erreiche, aber eben -wie schon gesagt- jetzt keine Zeit mehr habe, um in 
der ISR zu entscheiden, welcher Ausgang geschaltet werden soll.

Gruß,
Harald

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.