Forum: Mikrocontroller und Digitale Elektronik Atmega 16 Timer1 in Simulation ok aber in in Real


von Frank (Gast)


Lesenswert?

Guten Morgen zusammen,

Ich habe ein Problem mit meinem Timer1 auf meinem Atmega 16

Und zwar nutze ich den Output Compare Interrupt vom Timer 1

Diesen stelle ich mit OCR inhalt(0x0753) auf ca 10ms.

In der ISR toggle ich nun einen Pin an Port B.
In dieser Simulation toggelt er auch mit ca 10ms.

Aber in der realität variert das toggel mal von 10 auf 30 oder 20 ms.

woran könnte soetwas liegen?
Das sieht so aus als wenn irgendetwas verhindert das die ISR aufgerufen 
wird.

Könnte mal jemand schauen ob er etwas findet was falsch sein könnte ?

Hier der Code
1
//////////////////////////////////////////////////
2
// Interupt Service Routinen          //  
3
//////////////////////////////////////////////////
4
5
//Alle 10ms tritt ein Timer1 Output Compare Interrupt auf.  
6
ISR (TIMER1_COMPA_vect)
7
{    
8
  TCNT1L    =  0x00;  //Timer1-Compare-Wert wieder 
9
  TCNT1H    =  0x00;  //auf 0 setzen
10
  PORTB ^= (1<<PB0);
11
}
12
13
14
// Interrupt speichert den Taster-Status in Variablen ab.
15
SIGNAL (SIG_OVERFLOW0)      // every 10ms
16
{
17
  //static unsigned char ct0, ct1, rpt;
18
   //unsigned char i;
19
20
  TCNT0 = (unsigned char)(signed short)-(XTAL / 1024 * 10e-3 + 0.5);  // preload for 10ms
21
  PORTB ^= (1<<PB1);
22
}
23
24
25
//////////////////////////////////////////////////
26
// Main-Schleife                //  
27
//////////////////////////////////////////////////  
28
int main (void)
29
{
30
  //Initialisierung
31
  TCCR1A    =  0x00;          // Timer 1 Initialisieren
32
  TCCR1B    |=  ((1<<CS11) | (1<<CS10));// Prescaler auf "64"
33
  TIMSK    |=  (1<<OCIE1A);      // Output Compare A Interrupt  
34
  OCR1AH    =  0x07;          // einschalten. Interrupt bei:
35
  OCR1AL    =  0x53;          // OCR1 Inhalt = 12Mhz / 64 * 0,0005s (500us)
36
                      // =005E(500us)
37
                      // =00BC(1ms)
38
                      // =0753(10ms)
39
40
  // Im SIMULATOR etwas realistischer bei F_CPU = 1MHz
41
  //OCR1AH    =  0x01;
42
  //OCR1AL    =  0x08;
43
44
//  TCCR0     = 1<<CS02^1<<CS00;      // divide by 1024
45
//  TIMSK     |= 1<<TOIE0;          // Time 0 Initialisieren
46
47
  //PORT-Initialisierung
48
  LCD_DDR    =    0xFF;    // PORT A Ausgang Text LCD
49
  DDRB = 0xFF;
50
  DDRC = 0xFF;
51
52
  while (1)
53
  {
54
    PORTB ^= (1<<PB2);
55
    
56
  }
57
  
58
}

von Oliver J. (skriptkiddy)


Lesenswert?

Für deinen Fall hat Atmel den CTC-Modus in die AVR-Timer integriert.
Versuchs mal damit.

von spess53 (Gast)


Lesenswert?

HI

>Aber in der realität variert das toggel mal von 10 auf 30 oder 20 ms.

Woran siehst du das?

>ISR (TIMER1_COMPA_vect)
>{
>  TCNT1L    =  0x00;  //Timer1-Compare-Wert wieder
>  TCNT1H    =  0x00;  //auf 0 setzen
>  PORTB ^= (1<<PB0);
>}

>SIGNAL (SIG_OVERFLOW0)
>  TCNT0 = (unsigned char)(signed short)-(XTAL / 1024 * 10e-3 + 0.5);  // >preload 
for 10ms
>  PORTB ^= (1<<PB1);

Schon mal etwas von CTC gehört?

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

>
1
> ISR (TIMER1_COMPA_vect)
2
> {    
3
>   TCNT1L    =  0x00;  //Timer1-Compare-Wert wieder 
4
>   TCNT1H    =  0x00;  //auf 0 setzen
5
>   PORTB ^= (1<<PB0);
6
> }
7
>

Bei allen 16 Bit Registern. Beim Schreiben: erst High, dann Low

Durch das Beschreiben des Highbytes wird das Register gesperrt, diese 
Sperre wird durch das Beschreiben des Lowbytes wieder aufgelöst.

Aber dein Compiler weiß das alles und bietet dir Pseudo-16 Bit Register 
an, die das alles berücksichtigen

   TCNT1 = 0;

und fertig. (Besser wäre der CTC Modus, der macht das nämlich alles von 
alleine)

Auch hier
>
1
> OCR1AH    =  0x07;          // einschalten. Interrupt bei:
2
> OCR1AL    =  0x53;          // OCR1 Inhalt = 12Mhz / 64 * 0,0005s (500us)
3
>                       // =005E(500us)
4
>                       // =00BC(1ms)
5
>                       // =0753(10ms)
6
>

warum so kompliziert? Kein Mensch zwingt dich High und Low Register 
getrennt zu beschreiben bzw. da Hex-Zahlen anzugeben.
1
  OCR1A = 1875;

oder so
1
  OCR1A = 12000000 / 64 / 100;

oder so
1
  OCR1A = F_CPU / 64 / 100;

von Frank (Gast)


Lesenswert?

mhm Daran hats nicht gelegen.

Habe nun den Timer1 soweit nach euren Vorschlägen verbessert.

Ich habe mit einem Scope mal an dem PIN den ich Toggle gemessen und da 
Messe ich immer eine Folge von ca8 bis 10 Flankenwechsel und dann liegt 
das signal 30 ms auf Low bzw auf High.

Wa ich kannst mir irgendwie nicht erklären

von Karl H. (kbuchegg)


Lesenswert?

UNbeabsichtigter Hardware-Reset aus irgendeinem Grund?

einfach mal am Anfang in der main eine LED einschalten, 1/2 Sekunde 
brennen lassen und wieder aussschalten. Die LED darf nur das erste mal 
brennen, wann immer sie danach noch einmal kommt, ist ein Reset 
dazwischen passiert.

von Frank (Gast)


Lesenswert?

Ich habe den Fehler gefunden, aber ich Verstehe ihn nicht :(.
Und zwar hatte ich unten in der While schleife das stehen

PORTB ^= (1<<PB4);

Wenn ich das reinsetze dann spinnt das toggeln in der Timer 1 Interrupt 
Routine

Ist das ^ eventuell kein bitweises XOR?



Hier nochmal der Code
1
//Alle 10ms tritt ein Timer1 Output Compare Interrupt auf.  
2
ISR (TIMER1_COMPA_vect)
3
{    
4
//  TCNT1L    =  0x00;  //Timer1-Compare-Wert wieder 
5
//  TCNT1H    =  0x00;  //auf 0 setzen
6
  PORTB ^= (1<<PB0);
7
8
//  PORTB++;
9
}
10
11
12
// Interrupt speichert den Taster-Status in Variablen ab.
13
SIGNAL (SIG_OVERFLOW0)      // every 10ms
14
{
15
  //static unsigned char ct0, ct1, rpt;
16
   //unsigned char i;
17
18
  TCNT0 = (unsigned char)(signed short)-(XTAL / 1024 * 10e-3 + 0.5);  // preload for 10ms
19
  PORTB ^= (1<<PB1);
20
}
21
22
23
//////////////////////////////////////////////////
24
// Main-Schleife                //  
25
//////////////////////////////////////////////////  
26
int main (void)
27
{
28
  //Initialisierung
29
  TCCR1A    |=  (1<<COM1A1);          // Timer 1 Initialisieren
30
  TCCR1B    |=  ((1<<CS11) | (1<<CS10) | (1<<WGM12));// Prescaler auf "64"
31
  TIMSK    |=  (1<<OCIE1A);      // Output Compare A Interrupt  
32
  
33
  OCR1A = XTAL / 64 / 100;
34
  /*OCR1AH    =  0x07;          // einschalten. Interrupt bei:
35
  OCR1AL    =  0x53;          // OCR1 Inhalt = 12Mhz / 64 * 0,0005s (500us)
36
                      // =005E(500us)
37
                      // =00BC(1ms)
38
                      // =0753(10ms)
39
*/
40
  // Im SIMULATOR etwas realistischer bei F_CPU = 1MHz
41
  //OCR1AH    =  0x01;
42
  //OCR1AL    =  0x08;
43
44
//  TCCR0     = 1<<CS02^1<<CS00;      // divide by 1024
45
//  TIMSK     |= 1<<TOIE0;          // Time 0 Initialisieren
46
47
  //PORT-Initialisierung
48
  LCD_DDR    =    0xFF;    // PORT A Ausgang Text LCD
49
  DDRB = 0xFF;
50
  DDRC = 0xFF;
51
  
52
  
53
  cli();
54
 //   i2c_init();
55
    sei();                               // now enable interrupt
56
  
57
  while (1)
58
  {
59
    //PORTB ^= (1<<PB4);
60
    PORTC ^= (1<<PC0);
61
    
62
  }
63
  
64
}

von Karl H. (kbuchegg)


Lesenswert?

Frank schrieb:
> Ich habe den Fehler gefunden, aber ich Verstehe ihn nicht :(.
> Und zwar hatte ich unten in der While schleife das stehen
>
> PORTB ^= (1<<PB4);
>
> Wenn ich das reinsetze dann spinnt das toggeln in der Timer 1 Interrupt
> Routine
> Ist das ^ eventuell kein bitweises XOR?

Doch, doch.

Aber da passiert ganz was anderes:

Das Hauptprogramm läuft und läuft und läuft.

   PORTB ^= (1^<<PB4);

wie implementiert der Compiler das?

Da es keine Assembler Instruktion für Pin Toggeln gibt (das können erst 
neuerere Prozessoren), muss der Compiler das so implementieren.

   Register = Inhalte vom PORTB
   Bit mit einem XOR toggeln
   Register zurück schreiben auf PORTB


ab und an passiert es dann mal, dass der Interrupt genau dann kommt, 
wenn gerade die Instruktion

   Register = Inhalt vom PORTB

durch ist.
Der Interrupt macht was? Der toggelt das Bit 0 am PORTB.
Danach ist er fertig und es geht zurück ins Hauptprogramm. Was macht das 
Hauptprogramm?
Es toggelt seinerseits PB4 im Register und schreibt alles zurück an 
PORTB.
Aber! Dadurch ist auch PB0 wieder in seinem Ursprungszustand. Das 
Toggeln von PB0 würde rückgängig gemacht!

So etwas nennt man eine "Race Condition", wenn abhängig von den genauen 
Details und je nachdem wer schneller ist andere Ergebnisse entstehen.

Das Problem war also, dass die Operation
    PORTB ^= (1^<<PB4);
durch einen Interrupt unterbrochen wird.
Das Problem kannst du nur so lösen, dass du diese Unterbrechung nicht 
zulässt.
1
  ...
2
3
  while( 1 ) {
4
    cli();
5
    PORTB ^= (1^<<PB4);
6
    sei();
7
  }

von Frank (Gast)


Lesenswert?

Wow - Vielen Vielen Dank für diese Ausführlich Erklärung ich habe das 
gerade mal ausprobiert und zu 100% nachvollziehen können. Genial.


Ich Danke dir.

Einen wunderschönen Freitag noch.

Nun bin ich Happy :-)

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.