Forum: Mikrocontroller und Digitale Elektronik Einfacher Atmega 328 Timer code ungewollte pausen


von fred (Gast)


Lesenswert?

Hallo Mikrocontroller-Freunde,

ich versuche einen Atmega328p eine LED jede zweite sekunde eine sekunde 
leuchten zu lassen. Habe ich jetzt hinbekommen mit dem Timer1,
macht der MCU nach 4 mal blinken 10s lang gar nichts...
1
#include <avr/io.h>
2
  #include <avr/interrupt.h>
3
4
  int t = 0;
5
  int main(void)
6
  {
7
    DDRB = 0b000100000;
8
    OCR1A = 0x3D08;
9
10
    TCCR1B |= (1 << 3);
11
    // Mode 4, CTC on OCR1A
12
13
    TIMSK1 |= (1 << 1);
14
    //Set interrupt on compare match
15
16
    TCCR1B |= 0x05;
17
    // set prescaler to 1024 and start the timer
18
19
20
    sei();
21
    // enable interrupts
22
23
24
    while (1)
25
    {
26
      while(t==1)
27
      {
28
        PORTB = 0xFF;
29
      }
30
      PORTB = 0x00;
31
      t=0;
32
      
33
    }
34
  }
35
36
  ISR (TIMER1_COMPA_vect)
37
  {
38
    t++;
39
  }

Könnt ihr mir weiterhelfen? Ich bin am verzweifeln :(
Danke

von Jim M. (turboj)


Lesenswert?

1
volatile int t = 0;

von fred (Gast)


Lesenswert?

Hi Jim!

leider half das auch nicht...

von foobar (Gast)


Lesenswert?

Die Interaktion zwischen Interruptroutine und Hauptprogramm robust zu 
bekommen, ist nicht trivial. Das volatile nannte Jim schon. Reicht 
leider nicht, du brauchst auch noch nen atomaren Datentyp (z.B. char 
oder uint8_t).

Und dann muß das Protokoll auch noch korrekt sein - deines ist es 
nicht. Stell dir vor, t ist 0 und kurz nach dem Test "while(t==1)" und 
noch vor dem "t=0" kommt der Interrupt, t wird 1 und von main sofort 
wieder auf 0 gesetzt ohne jemals gemerkt zu haben, dass es kurz auf 1 
war.

von foobar (Gast)


Lesenswert?

Die einfachste Variante:
1
volatile char t;
2
   ...
3
   while (1)
4
      PORTA = (t & 1) ? 0xff : 0x00;
5
   ...

von fred (Gast)


Lesenswert?

Hi foobar,

leider verstehe ich die variante nicht wirklich...
und später muss/ will ich die eine LED durch drei ersetzen.
Dann muss jede led 4s nacheinander blinken mit einem zeitabstand von 4s.

von foobar (Gast)


Lesenswert?

> leider verstehe ich die variante nicht wirklich

Die läßt den Timer einfach durchlaufen. Je nachdem, ob der gerade oder 
ungerade ist, werden die LEDs ein- oder ausgeschaltet.

Etwas flexibler wäre man z.B. mit so etwas:
1
// user timers, will count down to zero and then stop
2
volatile uint8_t t1, t2, t3; // t1 for leds, t2 and t3 are free
3
4
ISR (TIMER1_COMPA_vect)
5
{
6
    if (t1) t1--;
7
    if (t2) t2--;
8
    if (t3) t3--;
9
}
10
11
int main() {
12
    // setup
13
    ...
14
    for (;;) {
15
        if (t1 == 0) { // led timer expired?
16
            // turn leds off and on
17
            ...
18
            t1 = 4; // restart led timer
19
        }
20
        // do something else
21
        ...
22
    }
23
}

von fred (Gast)


Lesenswert?

Jetzt habe ich was anderes ausprobiert,
und ich versteh die welt nicht mehr.
1
#include <avr/io.h>
2
3
int main(void)
4
{
5
  // connect led to pin PC0
6
  DDRB |= (1 << 5);
7
  
8
  TCCR1B |= (1 << WGM12)|(1 << CS11)|(1 << CS10);
9
  TCNT1 = 0;
10
  OCR1A = 25000;
11
  
12
  int counter = 0;
13
14
  while(1)
15
  {
16
    counter = 0;
17
    if (TIFR1 & (1 << OCF1A))
18
    {
19
    counter = 1;
20
    for (int i=0; i < 30000; i++);  
21
    }
22
    PORTB ^= (1 << 5);
23
    TIFR1 |= (1 << OCF1A);    
24
  }
25
}

füge ich bei
[code]
if (TIFR1 & (1 << OCF1A))
    {
    counter = 1;
    for (int i=0; i < 30000; i++);
    }
[\code]

dies ein:
[code]
if (TIFR1 & (1 << OCF1A))
    {
    PORTB ^= (1 << 5);
    }
[\code]

Warum bekomme ich wenn ich die for-schleife im if-vergleich drin habe 
ein schnelleres blinken? und wenn nur der toggelbefehl drin steht die 
gewünschte zeitspanne?

von foobar (Gast)


Lesenswert?

Zielloses rumballern auf Registern ist selten sinnvoll (deine "while 
(1)" Loop wird mehrere 100000 mal pro Sekunde durchlaufen...). Deine 
zwei Varianten machen halt was ganz anderes. Btw, die for-busy-loop wird 
eh wegoptimiert.

von Peter D. (peda)


Lesenswert?

foobar schrieb:
> Stell dir vor, t ist 0 und kurz nach dem Test "while(t==1)" und
> noch vor dem "t=0" kommt der Interrupt, t wird 1 und von main sofort
> wieder auf 0 gesetzt ohne jemals gemerkt zu haben, dass es kurz auf 1
> war.

So ist es. Das Programm ist logisch falsch.
Ein Interrupt kommt asynchron zum Main und das muß man berücksichtigen.

von fred (Gast)


Lesenswert?

Ok, wie kann ich den zweiten code richtigstellen,
das ich anstatt einem getoggelten PortB einen Variable aufrufe und 
inkrementiere und diese variable dann für drei leds zum leuchten 
benutzen kann?

von Peter D. (peda)


Lesenswert?

fred schrieb:
> Ok, wie kann ich den zweiten code richtigstellen,

Welchen 2. Code denn?
Hint: Man darf (sollte) verlinken oder zitieren.

Delay mit Interrupt zu vermanschen ist ganz großer Mumpitz. Du mußt Dich 
für eins entscheiden.

fred schrieb:
> dann für drei leds zum leuchten
> benutzen kann?

Beschreibe mal genau, was die 3 LEDs machen sollen.

von Stefan F. (Gast)


Lesenswert?

fred schrieb:
>>PORTA = (t & 1) ? 0xff : 0x00;
> leider verstehe ich die variante nicht wirklich...

Das bedeutet in Worten:

Wenn in t das Bit 1 gesetzt ist, dann setze PORTA auf 0xff (alle 
Ausgänge auf High).
Wenn in t das Bit 1 nicht gesetzt ist, dann setze PORTA auf 0x00 (alle 
Ausgänge auf High).

Ziel = (Ausdruck) ? (Wert wenn wahr) : (Wert wenn falsch)

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.