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


von Harald H. (harald_horn)


Angehängte Dateien:

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:
1
void query()
2
//Diese Funktion fragt das Touchscreen sowie die angeschlossenen Taster zyklisch ab. 
3
4
{
5
  int index = 0;
6
  static char enc_last = 0x01;
7
  static char bargraph = VENTIL;
8
  char buffer_info[] = {0,0};
9
  int buffer_size = 0;
10
  char strng[4];
11
  char i = 0;
12
  static int upper_limit;
13
  static int lower_limit;
14
  static char toggle_seq=0;
15
  static char toggle_valve=0;
16
  char buffer_array[63];
17
  long out;
18
19
20
//********************** Abfrage Drehgeber ****************************
21
    i ^= 3;
22
23
  i -= enc_last;
24
25
  if ( !(PINB & (1<<PINB3)) )
26
  {
27
//buffer_array initialisieren
28
    for (index = 0; index < 63; index ++)
29
    {
30
      buffer_array[index] = 0;
31
    }
32
33
//************************* Daten vom Touchscreen holen ******************************
34
35
//Buffergröße etc. abfragen
36
    get_buffer_info(buffer_info);
37
38
//Bufferdaten holen
39
    get_buffer_data(buffer_array, buffer_info[0]);
40
41
//Dekodierung der empfangenen Daten    
42
    for (index = 7; index < buffer_info[0] + 8; index ++)
43
    {
44
      
45
      if (buffer_array[index] == 0x1B)
46
      {
47
          index++;
48
          switch (buffer_array[index])
49
          {
50
          case 'A':  //Touch-Schalter/Taster betätigt
51
            index++;
52
            index++;
53
            switch (buffer_array[index])
54
            {
55
              case VENTIL:
56
                bargraph = VENTIL;
57
              break;
58
59
              case ZUENDUNG:
60
                bargraph = ZUENDUNG;
61
              break;
62
63
              case LASER:
64
                bargraph = LASER;
65
              break;
66
            }  
67
          break;
68
          }
69
      }      
70
    }
71
  }
72
73
    if( i & 1 )
74
    {        // bit 0 = value (1)
75
      enc_last += i;      // store new as next last
76
//Abhängig vom im Touchscreen ausgewählten Bargraphen werden die Eingabewerte
77
//unterschiedlichen Variablen zugeordnet    
78
    switch(bargraph)
79
    {
80
      case VENTIL:
81
          bar_val_ventil += (i & 2) - 1;
82
//Der Drehgeber liefert pro Raste 4 Impulse. Daher ist der einglesene Wert immer um Faktor 4 zu groß,
83
//entsprechend müssen die Grenzen ebenfalls um Faktor 4 größer gewählt werden. 
84
        if (bar_val_ventil > 1200)
85
          bar_val_ventil = 0;
86
87
        if (bar_val_ventil < 0)
88
           bar_val_ventil = 1200;
89
//Ausgabe in msec
90
        out = (bar_val_ventil)/6;
91
        update_bargraph(VENTIL, out);
92
        out = bar_val_ventil/4;
93
        sprintf(strng, "%3d", out);
94
        print_string(strng, 'R', 226,9);
95
      break;
96
      
97
      case ZUENDUNG:
98
          
99
        if ((bar_val_zuendung < 400) || ((bar_val_zuendung == 400) && ((i & 2) - 1)<0)) 
100
          bar_val_zuendung += (i & 2) - 1;    // bit 1 = direction (+/-)
101
        else if ((bar_val_zuendung < 4000) || ((bar_val_zuendung == 4000) && ((i & 2) - 1)<0))  // && ((bar_val_zuendung/4 -  10) >= 100))
102
          bar_val_zuendung += 10*((i & 2) - 1);    // bit 1 = direction (+/-)
103
        //else if ((bar_val_zuendung <= 4000) && ((bar_val_zuendung/4 -  10) < 100))
104
        //  bar_val_zuendung += (i & 2) - 1;    // bit 1 = direction (+/-)
105
        else //if (bar_val_zuendung <=40000)
106
          bar_val_zuendung += 100*((i & 2) - 1);    // bit 1 = direction (+/-)
107
108
        if (bar_val_zuendung >= 200400)
109
          bar_val_zuendung = 0;
110
111
        if (bar_val_zuendung <= -4)
112
           bar_val_zuendung = 200000;
113
114
        out = bar_val_zuendung/1000;
115
        //out = bar_val_zuendung/6;
116
        update_bargraph(ZUENDUNG, out);
117
        out = bar_val_zuendung/4;
118
        sprintf(strng, "%5ld", out);
119
        print_string(strng, 'R', 226,49);
120
        sprintf(strng, "%6ld", bar_val_zuendung);
121
        print_string(strng, 'R', 226,69);
122
      break;
123
124
      case LASER:
125
          bar_val_laser += (i & 2) - 1;    // bit 1 = direction (+/-)
126
127
        if (bar_val_laser > 800)
128
          bar_val_laser = 0;
129
130
        if (bar_val_laser < 0)
131
           bar_val_laser = 800;
132
        out = bar_val_laser/4;
133
        update_bargraph(LASER, out);
134
        sprintf(strng, "%3d", out);
135
        print_string(strng, 'R', 226,29);
136
      break;
137
    }
138
     }
139
140
//Abfrage der Angeschlossenen Taster; es gibt folgene Taster:
141
// * Einzelzündung auslösen * Ventil öffnen/schließen * Laser auslösen * Einzelsequenz abfahren * Sequenz zyklisch fahren
142
    
143
//Sequenzstart
144
    if (!(PINA & (1<<PINA0)))  //Taster für Sequenzstart abfragen
145
    {
146
        _delay_ms(30);
147
        _delay_ms(30);
148
        _delay_ms(30);
149
        _delay_ms(30);
150
      if (toggle_seq == 0)
151
      {
152
        if (toggle_valve == 1)
153
        {
154
          delete_area(0,64,240,80);
155
          print_string("Sequenzstart bei geöffnetem", 'R', 0,64);
156
          print_string("Fahrventil nicht möglich.", 'R', 0,72);
157
        }
158
        else
159
        {
160
          toggle_seq = 1;
161
          print_sequence_running(BLINKEN);
162
          while (!(PINA & (1<<PINA0)));
163
        }
164
      }
165
      else
166
      {
167
        toggle_seq = 0;
168
        delete_area(0,64,240,125);
169
        print_ready();
170
        while (!(PINA & (1<<PINA0)));
171
      }
172
    }
173
174
//************************ Hier fangen die Probleme an... ******************************
175
176
//Einzelsequenz
177
    if (!(PINA & (1<<PINA1)))  //Taster für Einzelsequenz abfragen
178
    {
179
        //Prellzeit abwarten
180
        _delay_ms(30);
181
        _delay_ms(30);
182
        _delay_ms(30);
183
        _delay_ms(30);
184
185
//Keine Ausführung bei offenenm Fahrventil
186
        if (toggle_valve) 
187
        {
188
          delete_area(0,64,240,80);
189
          print_string("Einzelsequenz bei geöffnetem", 'R', 0,64);
190
          print_string("Fahrventil nicht möglich.", 'R', 0,72);
191
          _delay_ms(30);
192
          _delay_ms(30);
193
        }
194
195
//Keine Ausführung bei bereits laufender Sequenz
196
        else if (toggle_seq)
197
        {
198
          delete_area(0,64,240,80);
199
          print_string("Einzelsequenz bei laufender", 'R', 0,64);
200
          print_string("Sequenz nicht möglich.", 'R', 0,72);
201
        }
202
//Einzelsequenz abfahren        
203
        else
204
        {
205
          print_sequence_running(NORMAL);
206
          print_ready();
207
208
//Timer 0 zurücksetzen
209
          TCNT0 = 0;
210
211
//OCR1A mit Eingabewert vom Touch laden (= Zeit, die von Ventilschluss bis Zündung vergehen soll)          
212
//Der Timer wird zwar erst später benötigt, aber das Register kann bereits hier (=außerhalb der ISRn) 
213
//geladen werden
214
          //OCR1A = (bar_val_zuendung*10)/12;
215
//Das Register wird zu Testzwecken mit einem Festwert geladen          
216
          OCR1A = (4*10)/12;
217
218
//Compare Interrupt Timer 0 aktivieren
219
          TIMSK |= (1<<OCIE0);
220
        }
221
    }
222
//Zündung
223
    if (!(PINA & (1<<PINA2)))
224
    {
225
        _delay_ms(30);
226
        _delay_ms(30);
227
        _delay_ms(30);
228
        _delay_ms(30);
229
        if (toggle_seq)
230
        {
231
          delete_area(0,64,240,80);
232
          print_string("Einzelzündung bei laufender", 'R', 0,64);
233
          print_string("Sequenz nicht möglich.", 'R', 0,72);
234
        }
235
        else
236
        {
237
          beep(2);  
238
          if (toggle_valve)
239
          {
240
            delete_area(0,64,240,80);            
241
            print_string("Zündung erfolgt", 'R', 0,64);
242
            //single_shot();
243
            while (!(PINA & (1<<PINA2)));
244
            delete_area(0,64,240,80);
245
          }
246
          else
247
          {
248
            print_done();
249
            //single_shot();
250
            while (!(PINA & (1<<PINA2)));
251
            delete_area(0,64,240,80);
252
            print_ready();
253
          }
254
        }
255
    }
256
    
257
//Laser 
258
    if (!(PINA & (1<<PINA3)))
259
    {
260
      _delay_ms(30);
261
      _delay_ms(30);
262
      _delay_ms(30);
263
      _delay_ms(30);
264
      if (toggle_seq)
265
      {
266
        delete_area(0,64,240,80);
267
        print_string("Laser auslösen bei laufender", 'R', 0,64);
268
        print_string("Sequenz nicht möglich.", 'R', 0,72);
269
      }
270
      else
271
      {
272
        beep(2);  
273
        if (toggle_valve)
274
        {
275
          delete_area(0,64,240,80);
276
          print_string("Laser ausgelöst", 'R', 0,64);
277
          while (!(PINA & (1<<PINA3)));
278
          _delay_ms(100);
279
          delete_area(0,64,240,80);
280
        }
281
        else
282
        {
283
          print_activate();
284
          while (!(PINA & (1<<PINA3)));
285
          delete_area(0,64,240,80);
286
          print_ready();
287
        }
288
      }
289
  }
290
291
//Fahrventil
292
    if (!(PINA & (1<<PINA4)))
293
    {
294
        _delay_ms(30);
295
        _delay_ms(30);
296
        _delay_ms(30);
297
        _delay_ms(30);
298
      if (toggle_valve == 0)
299
      {
300
        if (toggle_seq == 1)
301
        {
302
          delete_area(0,64,240,80);
303
          print_string("Fahrventil öffnen bei laufen-", 'R', 0,64);
304
          print_string("der Sequenz nicht möglich.", 'R', 0,72);
305
        }
306
        else
307
        {
308
          toggle_valve = 1;
309
          print_valve_open(BLINKEN);
310
          while (!(PINA & (1<<PINA4)));
311
        }
312
      }
313
      else
314
      {
315
        toggle_valve = 0;
316
        delete_area(0,64,240,125);
317
        print_ready();
318
        while (!(PINA & (1<<PINA4)));
319
320
      }
321
    }
322
}
323
324
SIGNAL (SIG_OUTPUT_COMPARE0)
325
{
326
327
//Diese Funktion zählt die Überläufe von Timer 0 hoch; pro Überlauf vergehen 250usec incl. der Abarbeitung
328
//dieser Funktion, solange "counter" hochgezählt wird.
329
//Dieser etwas umständliche Weg ist nötig, da Timer 0 mit seinen 8 Bit die geforderte Zeitspanne zwischen
330
// 0 und 300ms nicht abdecken kann und Timer 1 mit seiner höheren Auflösung für andere Zwecke benötigt wird.
331
332
//"counter" zählt die Funktionsaufrufe
333
  static uint16_t counter = 0;
334
335
  if (counter == 0)
336
  {
337
338
//Fahrventil Open Command
339
    PORTD |= (1<<PD0);
340
  }
341
342
  counter++;
343
344
// Wenn "counter den Vorgabewert erreicht hat, wird die Schleife verlassen.
345
  //if (counter == (bar_val_ventil-2))
346
347
//"counter" wird zu Testzwecken mit einem Festwert verglichen
348
  if (counter == (6-2))
349
  //if (counter == 6)
350
  {
351
352
//"counter" zurücksetzen
353
    counter = 0;
354
355
//Fahrventil Close Command
356
    PORTD &= ~(1<<PD0);
357
    PORTD |= (1<<PD1);
358
  
359
  
360
361
//Compare-Interrupt Timer 0 deaktivieren
362
363
    TIMSK &= ~ (1<<OCIE0);
364
365
//Interrupt-Flags zurücksetzen; im Moment ist nicht klar, warum dies nötig ist. Wurde durch Versuch festgestellt.
366
    TIFR = 0xFF;
367
368
//Timer 1 zurücksetzen
369
    TCNT1 = 0;    //Timer zurücksetzen.
370
371
//Compare-Interrupt A/Timer 1 aktivieren.
372
    TIMSK |= (1<<OCIE1A);
373
374
//Timer 1 starten.    
375
    TCCR1B |= (1<<CS12)|(1<<WGM12);
376
377
//Mit "gezuendet" wird die Information, daß die folgende ISR fertig ist, übergeben.
378
//Aus Präzisionsgründen soll zwischen dieser und der Folgenden ISR nicht in andere PRogrammteile gewechselt werden.
379
    gezuendet = 1; 
380
381
//Interrupts aktivieren
382
    sei();
383
384
//Auf zweite ISR warten.
385
    while(gezuendet) {}    //Warten, bist der andere Timer-Interrupt fertig ist.
386
387
  }
388
}
389
390
void __attribute__ ((naked)) 
391
SIG_OUTPUT_COMPARE1A (void)
392
{
393
//Diese Funktion setzt den Ausgang, der die Zündung auslöst.
394
395
//Flag zur Bestätigung der Ausführung zurücksetzen
396
   gezuendet = 0;
397
398
399
//Impuls an PortD/Pin2 ausgeben
400
   asm volatile("sbi 0x12,0x2\n\t"  //PD2
401
          "nop\n\t"
402
            "nop\n\t"
403
          "nop\n\t"
404
          "nop\n\t"
405
            "cbi 0x12,0x2\n\t"
406
        //"cbi 0x39,0x4\n\t"
407
        "PUSH R24\n\t"        
408
        "LDS R24,0x0059\n\t"
409
        "ANDI R24,0xEF\n\t"
410
        "STS 0x0059,R24\n\t"
411
        "LDS R24,0x004E\n\t"
412
        "ANDI R24,0xFD\n\t"
413
        "STS 0x004E,R24\n\t"
414
        "POP R24\n\t"
415
            "reti\n\t"
416
          );
417
418
//Timer 1 anhalten.
419
        TCCR1B &= ~(1<<CS12);
420
}
421
422
423
void init()
424
{
425
  spi_init();
426
  DDRA = 0x00;        //Port A als Eingang
427
  PORTA = 0xFF;        //Interne Pull-Ups für Port A aktivieren
428
  
429
  TCCR1A |= (1<<COM1A0)|(1<<COM1B0); //Normal Mode mit TOP=ICR1
430
  TCCR1A &= ~((1<<COM1A1)|(1<<COM1B1)); //Compare Match toggled die Ausgänge
431
  DDRD = 0xFF;      //Port D als Ausgang; Compare-Ausgänge liegen auf PD4 und PD5
432
  MCUCR |= (1<<ISC01)|(1<<ISC00);//INT0(PD1) bei steigender Flanke
433
  OCR0 = 250;          //Timer 0 alle 250 Taktzyklen löschen
434
435
  //Das setzen der Taktrate in TCCR0 startet Timer 0
436
  TCCR0 |= (1<<CS01)|(1<<WGM01); //CLK/8 (1us/Taktzyklus), CTC
437
438
}  
439
440
void setup_display()
441
{
442
443
  cursor_off();
444
  clear_screen();
445
446
  touch_on(EIN);
447
  touch_inv(EIN);
448
  touch_beep(EIN);
449
  touch_style(17);
450
451
  set_touch_font(FGENEVA10PROP);
452
453
  open_radio_group(1);
454
    define_touch_switch("Ventil", 1, 20, 40, 15, 1, 4);
455
    define_touch_switch("Laser", 1, 40, 40, 15, 3, 5);
456
    define_touch_switch("Zündung", 1, 60, 40, 15, 2, 6);
457
  close_radio_group();
458
459
  define_bargraph(VENTIL, RECHTS, 45, 20, 203, 1, 0, 200, BALKEN, 1);
460
  define_bargraph(LASER, RECHTS, 45, 41, 230, 21, 0, 200, STRICH, 1);
461
  define_bargraph(ZUENDUNG, RECHTS, 45, 61, 230, 42, 0, 200, STRICH, 1);
462
463
  update_bargraph(1, 0);
464
  update_bargraph(2, 0);
465
  update_bargraph(3, 0);
466
467
  print_string("0", 'R', 226,9);
468
  print_string("0", 'R', 226,29);
469
  print_string("0", 'R', 226,49);
470
  print_ready();
471
}
472
473
int main()
474
{
475
  init();
476
  setup_display();
477
  set_protocol_params();
478
    sei();
479
  while(1)
480
  {
481
    query();
482
  }
483
}

von Falk B. (falk)


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.

von Stefan E. (sternst)


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.

von Stefan E. (sternst)


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.

von Peter D. (peda)


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

von Harald H. (harald_horn)


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

von Andreas V. (tico)


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

von Harald H. (harald_horn)


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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.