Forum: Mikrocontroller und Digitale Elektronik AtMega16 Timer Problem


von Michael (Gast)


Lesenswert?

Hallo!

Ich möchte mit einem AtMega16 eine Countdown Uhr bauen. Jetzt scheiter 
ich aber schon beim der Timebase für die Sekunden. Die Funktion läuft 
soweit, nur die Zeitabstände, bis Pin PB5 getoogelt wird schwanken 
stark. Die ersten sieben, acht Sekunden passen noch, danach sind die 
nächsten zwei, drei "Sekunden" nur 700ms lang. Dann beginnt das ganze 
von vorne. Hat jemand eine Idee dazu? Den Code habe ich meist von 
Vorlagen aus diesem Forum. Ich benutzte das AVR Studio 6.2 auf einem XP 
Rechner. Den AtMega16 hab ich über EBay bezogen. Ist es möglich, das ich 
hier fehlerhafte Chips bekommen habe?

Grüße Michael

1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
5
#define F_CPU    8300000
6
7
volatile uint16_t flag_1ms;
8
9
10
ISR( TIMER0_OVF_vect )    // every 1ms
11
{
12
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 64 * 10e-4 + 0.5);  // preload for 1ms
13
  PORTB^=(1<<PB4);
14
  flag_1ms++;
15
}

int main(void)
{
  // DDRx=0x00      Eingang
  // DDRx=0xFF      Ausgang

  // PORTx=0x00      Ausgang auf 0    Eingang Pull Up deaktivieren
  // PORTx=0xFF      Ausgang auf 1    Eingang Pull Up aktivieren

  DDRA=0x00;          // Port A
  PORTA=0x00;      // Pull Up deaktivieren

  DDRB=0xFF;          // Port B auf Ausgang
  PORTB=0xFF;      // alle Ausgänge auf 1

  DDRC=0xFF;      // Port C auf Ausgang
  PORTC=0x00;      // alle Ausgänge auf 0

  DDRD=0x40;          // Port D auf  Bit 7 (EAEEEEEE) Bit 0
  PORTD=0xFF;      // Pull Up aktivieren / Ausgänge auf 1

  // TIMER
  TCCR0 = (0<<CS02) | (1<<CS01) | (1<<CS00);      // mit Vorteiler 64

  TIMSK = (1<<TOIE0);                  // Timer Interrupt 0 ein
  sei();

    while(1)
    {
    PORTB^=(1<<PB6);
    // Jede Sekunde
    if (flag_1ms >=1000) {
      flag_1ms = 0;
      PORTB^=(1<<PB5);
    }
    }
}

von Codeleser (Gast)


Lesenswert?

Michael schrieb:
> Den Code habe ich meist von
> Vorlagen aus diesem Forum.

Ich kann mir nicht vorstellen dass dieser Code in einer ISR
in diesem Forum "unbemeckert" geblieben ist:
1
TCNT0 = (uint8_t)(int16_t)-(F_CPU / 64 * 10e-4 + 0.5);  // preload for 
2
1ms

von Stefan E. (sternst)


Lesenswert?

Die Zugriffe auf flag_1ms in main müssen atomar sein.

von Route_66 H. (route_66)


Lesenswert?

Codeleser schrieb:
> Ich kann mir nicht vorstellen dass dieser Code in einer ISR
> in diesem Forum "unbemeckert" geblieben ist:
> TCNT0 = (uint8_t)(int16_t)-(F_CPU / 64 * 10e-4 + 0.5);  // preload for
> 1ms

Wird diese Konstante nicht bereits zur Compile-Zeit ausgerechnet?

von Arno (Gast)


Lesenswert?

Route 6. schrieb:
> Codeleser schrieb:
>> Ich kann mir nicht vorstellen dass dieser Code in einer ISR
>> in diesem Forum "unbemeckert" geblieben ist:
>> TCNT0 = (uint8_t)(int16_t)-(F_CPU / 64 * 10e-4 + 0.5);  // preload for
>> 1ms
>
> Wird diese Konstante nicht bereits zur Compile-Zeit ausgerechnet?

Ja, wird sie. Habe das gerade mal kurz kompiliert, das 
Assembler-Output-Listing ist unten angehängt. Herauskommen tut 126 
(Adresse 0x8a), was 130 Timer Ticks pro Überlauf bedeutet und bei den 
8,3MHz 1.002...ms entspricht. Genau ist das also nicht, aber auch nicht 
vollkommen daneben :)

Schöner wäre noch, statt 10e-4 1e-3 zu schreiben, dann findet man die 
Millisekunde auch in der Berechnung offensichtlich wieder. Hat mich 
gerade durcheinander gebracht. Ansonsten ist die Berechnung von 
Konstanten durch den Präprozessor vollkommen in Ordnung :)

Der Fehler dürfte eher da liegen, wo Stefan ihn vermutet. flag_1ms ist 
16Bit breit: Für den Vergleich wird bei 0xde/0xe2 erst das Low-Byte 
geladen, dann das High-Byte. Wenn dazwischen genau der Timer-Interrupt 
kommt, der von 767 (0x2ff) auf 768 (0x300) hochzählt, wird im Vergleich 
1023 (0x3ff) verwendet. Und das passiert eben ab und zu, wenn man nicht 
für den Vergleich die Interrupts deaktiviert, eben atomar darauf 
zugreift.

Genauso theoretisch beim Nullen bei 0xec/0xf0 - erst wird das High-Byte 
genullt, dann das Low-Byte. Wenn flag_1ms vorher den Wert 1023 (0x3ff) 
hätte, könnte erst das High-Byte genullt werden (0x0ff), dann der 
Timer-IRQ kommen und hochzählen (0x100) und dann das Low-Byte genullt 
werden. Das ist hier unwahrscheinlich, weil die Main-Loop zu schnell 
ist, als dass flag_1ms über 1000 werden könnte, in größeren Programmen 
aber denkbar. Dann hat man natürlich mit dieser Herangehensweise noch 
ein weiteres Problem - dann wird die Sekunde nämlich länger, wenn der 
Timer-IRQ genau nach dem Code-Abschnitt mit dem Vergleich kommt.

MfG, Arno
1
Disassembly of section .text:
2
3
00000000 <__vectors>:
4
   0:   0c 94 2a 00     jmp     0x54    ; 0x54 <__ctors_end>
5
   4:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
6
   8:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
7
   c:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
8
  10:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
9
  14:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
10
  18:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
11
  1c:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
12
  20:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
13
  24:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
14
  28:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
15
  2c:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__vector_11>
16
  30:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
17
  34:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
18
  38:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
19
  3c:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
20
  40:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
21
  44:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
22
  48:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
23
  4c:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
24
  50:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
25
26
00000054 <__ctors_end>:
27
  54:   11 24           eor     r1, r1
28
  56:   1f be           out     0x3f, r1        ; 63
29
  58:   cf e5           ldi     r28, 0x5F       ; 95
30
  5a:   d8 e0           ldi     r29, 0x08       ; 8
31
  5c:   de bf           out     0x3e, r29       ; 62
32
  5e:   cd bf           out     0x3d, r28       ; 61
33
34
00000060 <__do_clear_bss>:
35
  60:   10 e0           ldi     r17, 0x00       ; 0
36
  62:   a0 e6           ldi     r26, 0x60       ; 96
37
  64:   b0 e0           ldi     r27, 0x00       ; 0
38
  66:   01 c0           rjmp    .+2             ; 0x6a <.do_clear_bss_start>
39
40
00000068 <.do_clear_bss_loop>:
41
  68:   1d 92           st      X+, r1
42
43
0000006a <.do_clear_bss_start>:
44
  6a:   a2 36           cpi     r26, 0x62       ; 98
45
  6c:   b1 07           cpc     r27, r17
46
  6e:   e1 f7           brne    .-8             ; 0x68 <.do_clear_bss_loop>
47
  70:   0e 94 5b 00     call    0xb6    ; 0xb6 <main>
48
  74:   0c 94 7e 00     jmp     0xfc    ; 0xfc <_exit>
49
50
00000078 <__bad_interrupt>:
51
  78:   0c 94 00 00     jmp     0       ; 0x0 <__vectors>
52
53
0000007c <__vector_11>:
54
  7c:   1f 92           push    r1
55
  7e:   0f 92           push    r0
56
  80:   0f b6           in      r0, 0x3f        ; 63
57
  82:   0f 92           push    r0
58
  84:   11 24           eor     r1, r1
59
  86:   8f 93           push    r24
60
  88:   9f 93           push    r25
61
  8a:   8e e7           ldi     r24, 0x7E       ; 126
62
  8c:   82 bf           out     0x32, r24       ; 50
63
  8e:   98 b3           in      r25, 0x18       ; 24
64
  90:   80 e1           ldi     r24, 0x10       ; 16
65
  92:   89 27           eor     r24, r25
66
  94:   88 bb           out     0x18, r24       ; 24
67
  96:   80 91 60 00     lds     r24, 0x0060
68
  9a:   90 91 61 00     lds     r25, 0x0061
69
  9e:   01 96           adiw    r24, 0x01       ; 1
70
  a0:   90 93 61 00     sts     0x0061, r25
71
  a4:   80 93 60 00     sts     0x0060, r24
72
  a8:   9f 91           pop     r25
73
  aa:   8f 91           pop     r24
74
  ac:   0f 90           pop     r0
75
  ae:   0f be           out     0x3f, r0        ; 63
76
  b0:   0f 90           pop     r0
77
  b2:   1f 90           pop     r1
78
  b4:   18 95           reti
79
80
000000b6 <main>:
81
  b6:   1a ba           out     0x1a, r1        ; 26
82
  b8:   1b ba           out     0x1b, r1        ; 27
83
  ba:   8f ef           ldi     r24, 0xFF       ; 255
84
  bc:   87 bb           out     0x17, r24       ; 23
85
  be:   88 bb           out     0x18, r24       ; 24
86
  c0:   84 bb           out     0x14, r24       ; 20
87
  c2:   15 ba           out     0x15, r1        ; 21
88
  c4:   90 e4           ldi     r25, 0x40       ; 64
89
  c6:   91 bb           out     0x11, r25       ; 17
90
  c8:   82 bb           out     0x12, r24       ; 18
91
  ca:   83 e0           ldi     r24, 0x03       ; 3
92
  cc:   83 bf           out     0x33, r24       ; 51
93
  ce:   81 e0           ldi     r24, 0x01       ; 1
94
  d0:   89 bf           out     0x39, r24       ; 57
95
  d2:   78 94           sei
96
  d4:   20 e4           ldi     r18, 0x40       ; 64
97
  d6:   90 e2           ldi     r25, 0x20       ; 32
98
  d8:   88 b3           in      r24, 0x18       ; 24
99
  da:   82 27           eor     r24, r18
100
  dc:   88 bb           out     0x18, r24       ; 24
101
  de:   40 91 60 00     lds     r20, 0x0060
102
  e2:   50 91 61 00     lds     r21, 0x0061
103
  e6:   48 3e           cpi     r20, 0xE8       ; 232
104
  e8:   53 40           sbci    r21, 0x03       ; 3
105
  ea:   b0 f3           brcs    .-20            ; 0xd8 <main+0x22>
106
  ec:   10 92 61 00     sts     0x0061, r1
107
  f0:   10 92 60 00     sts     0x0060, r1
108
  f4:   88 b3           in      r24, 0x18       ; 24
109
  f6:   89 27           eor     r24, r25
110
  f8:   88 bb           out     0x18, r24       ; 24
111
  fa:   ee cf           rjmp    .-36            ; 0xd8 <main+0x22>
112
113
000000fc <_exit>:
114
  fc:   f8 94           cli
115
116
000000fe <__stop_program>:
117
  fe:   ff cf           rjmp    .-2             ; 0xfe <__stop_program>

von Michael (Gast)


Lesenswert?

@Codeleser: So hab ich das aus Peter Danneggers Tasterentprellung 
kopiert. Hier im Original so im Wiki zu finden
1
TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms

@Stefan: Das was mit den 16 Bit zu tun, oder? Wenn ich die Variable auf 
8-Bit umschreib und den Interrupt nur alle 10ms auslöse müsste es 
gehen!? Oder in der Main ein Cli und Sei vor und nach dem Zugriff 
einfügen.
Vielen Dank Dir, das hat mich sehr viel weiter gebracht...

von Arno (Gast)


Lesenswert?

Michael schrieb:
> @Stefan: Das was mit den 16 Bit zu tun, oder? Wenn ich die Variable auf
> 8-Bit umschreib und den Interrupt nur alle 10ms auslöse müsste es
> gehen!? Oder in der Main ein Cli und Sei vor und nach dem Zugriff
> einfügen.

Richtig.

Denk daran, dass du den Prescaler mit anpassen musst, weil du mit 
Prescaler 64 bei 8.3MHz mit einem 8-Bit-Timer keine 10ms erreichen 
wirst.

MfG, Arno

von Felix F. (wiesel8)


Lesenswert?

Codeleser schrieb:
> Michael schrieb:
>> Den Code habe ich meist von
>> Vorlagen aus diesem Forum.
>
> Ich kann mir nicht vorstellen dass dieser Code in einer ISR
> in diesem Forum "unbemeckert" geblieben ist:
>
>
1
TCNT0 = (uint8_t)(int16_t)-(F_CPU / 64 * 10e-4 + 0.5);  // preload 
2
> for
3
> 1ms
4
>
Ich kann mir nicht vorstellen, dass hier im Forum noch keiner 
angemeckert hat, dass es auch noch den CTC Mode gibt, wo man sich ein 
"preload" spart :)

mfg

von spess53 (Gast)


Lesenswert?

Hi

> So hab ich das aus Peter Danneggers Tasterentprellung
>kopiert. Hier im Original so im Wiki zu finden

>TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for >10ms

Warum nimmst du das umständliche Preload? Das geht doch mit OC-Interrupt 
einfacher.

Wo kommen die 8,3MHz her?

MfG Spess

von Michael (Gast)


Lesenswert?

@Arno: Jepp, daran hab ich schon gedacht beim Umstellen auf 10ms.

@Spess53: Bis jetzt hab ich immer mit dem Overflow gearbeitet. Das hatte 
bis dahin auch immer geklappt. Den OC_interrupt kenne ich nicht. Die 
8,3MHz brachte die erste grobe Messung des internen Oszilators. Laut 
Oszi liege ich damit bei 1,002 Sekunden. Natürlich geht das noch 
genauer, aber da war ja erstmal nicht mein Problem

Hab es jetzt auf 10ms pro Overflow umgebaut und jetzt passt es.

Vielen Dank allen für die Antworten

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.