ATmega32 AVR C Programmierphänomen Codefehler

OP #4421202
Lesenswert?

Hi,
ich habe einen sehr einfachen Versuchsaufbau und einen sehr sehr simplen 
Code und bekomme da einen Fehler.
Ich habe eine Inkrementalscheibe mit Gabellichtschranke und diese an 
einem externen Interrupt angeschlossen. Zwischen zwei externen 
Interrupts zähle ich wie oft der Timer0 überläuft in einer volatile 
UNSINGED int oder long und bekomme hin und wieder wenn ich die 
Inkrementalscheibe drehe negative Werte für die Variable. Wie zur Hölle 
kann das sein?

(ATmega32 UART Code ausgeblendet)
1
#include <stdint.h>
2
#include <avr/io.h>
3
#include <stdio.h>
4
#include <avr/interrupt.h>
5

6
volatile unsigned long counter256 = 0;
7
ISR( TIMER0_OVF_vect ){       // Overflow Interrupt Vector Timer0
8
  counter256++;
9
}
10

11
volatile uint8_t flagUmdrehung3 = 0;
12
volatile unsigned int counterUmdrehung3 = 0;
13
ISR(INT1_vect){
14
  //Externer Interrupt 2
15
    counterUmdrehung3 ++; //Eine Umdrehung wurde registriert
16
    flagUmdrehung3 = 1;
17
}
18

19
int main(void){
20
    TIMSK |= (1<<TOIE0);
21
    TCCR0 = (1<<CS00); //kein Prescaler
22
    DDRD &= ~(1<<3); //PD3 als Eingang
23
    PORTD|= (1<<3); //Pullup aktivieren
24
    GICR|=(1<<INT1); //INT1 enable im General Interupt Control Register
25
    MCUCR|=(1<<ISC11)|(1<<ISC10); //Steigende Flanke löst aus INT1
26
    sei();
27
    while(1){
28
        if (flagUmdrehung3 == 1) {
29
            flagUmdrehung3 = 0;
30
            printf("%d\n",counter256);
31
            counter256 = 0;
32
        }
33
    }
34
}

Beispielausgabe im Terminal:
11208
2299
1071
736
696
15318
-12307
19177
16627
22843
8265
1836
1114
19570
12010
3456
23045
845
1259

Grüße Sureai
#4421248
Lesenswert?

Erstmal gibst Du mit
  printf("%d\n",counter256);
eine signed in aus. Du hast aber eine unsigned int, also benutze %u.

Und dann ist es sinnvoll, auf Deine counter256 Variable vor Interrupts 
geschützt zuzugreifen. Sonst kann z.B. sowas passieren:

counter256  hat den Wert 1ffh
main löscht High-Byte von counter256 -> Wert jetzt 0ffh
ISR-Einsprung
ISR incrementiert counter256 -> Wert jetzt 0x100h
ISR-Ende
main löscht Low-Byte von counter256 -> Wert jetzt 100h

Dasselbe kann Dir auch bei der printf-Ausgabe passieren: High-und 
Low-Byte müssen an printf als Parameter übergeben werden, tritt 
dazwischen ein ISR mit Überlauf auf, ist das Ergebnis falsch.

Gruß, Stefan
OP #4421257
Lesenswert?

Stefan K. schrieb:
> Erstmal gibst Du mit
>   printf("%d\n",counter256);
> eine signed in aus. Du hast aber eine unsigned int, also benutze %u.
>
> Und dann ist es sinnvoll, auf Deine counter256 Variable vor Interrupts
> geschützt zuzugreifen. Sonst kann z.B. sowas passieren:
>
> counter256  hat den Wert 1ffh
> main löscht High-Byte von counter256 -> Wert jetzt 0ffh
> ISR-Einsprung
> ISR incrementiert counter256 -> Wert jetzt 0x100h
> ISR-Ende
> main löscht Low-Byte von counter256 -> Wert jetzt 100h
>
> Dasselbe kann Dir auch bei der printf-Ausgabe passieren: High-und
> Low-Byte müssen an printf als Parameter übergeben werden, tritt
> dazwischen ein ISR mit Überlauf auf, ist das Ergebnis falsch.
>
> Gruß, Stefan
1
if (flagUmdrehung3 == 1) {
2
    cli();
3
    flagUmdrehung3 = 0;
4
    printf("%d\n",counter256);
5
    counter256 = 0;
6
    sei();
7
}

Meinst du so?
#4421320
Lesenswert?

Ja, schon besser.
Noch genauer wird es so:
1
volatile unsigned long counter256 = 0;
2
volatile unsigned long counterAtExtIsr = 0;
3

4
ISR( TIMER0_OVF_vect ){       // Overflow Interrupt Vector Timer0
5
  counter256++;
6
}
7

8
volatile uint8_t flagUmdrehung3 = 0;
9
volatile unsigned int counterUmdrehung3 = 0;
10
ISR(INT1_vect){
11
  //Externer Interrupt 2
12
    counterUmdrehung3 ++;          //Eine Umdrehung wurde registriert
13
    flagUmdrehung3 = 1;
14
    // kopiere den aktuellen Timerwert nach CounterAtExtIsr und 
15
    // setze counter256 danach wieder zurück.
16
    // Weil wir in einer ISR sind, sind beide Operationen atomar:
17
    CounterAtExtIsr = counter256;
18
    counter256 = 0;                
19
}
20

21
int main(void){
22
...
23
    while(1){
24
        if (flagUmdrehung3 == 1) {
25
            // kopiere CounterAtExtIsr in lokale Variable und
26
            // sperre dabei ISRs, um ein atomares Verhalten zu erzwingen.
27
            // Durch die Verwendung von CounterAtExtIsr wird der 
28
            // interrupt-genaue Zählerwert ausgegeben, auch wenn main() 
29
            // ein Delay bis zu einer Umdrehung besitzt:
30
            cli();
31
            unsigned long myCounter = CounterAtExtIsr;
32
            sei();
33
            flagUmdrehung3 = 0;
34
            // gib den Zählerwert als unsigned long aus:
35
            printf("%lu\n",myCounter);
36
            counter256 = 0;
37
        }
38
...

Viele Grüße, Stefan
OP #4422641
Lesenswert?

Stefan K. schrieb:
> Ja, schon besser.
> Noch genauer wird es so:

Vielen Dank! Die richtige Formatierung bei printf mit %lu oder %u hatte 
den Fehler schon behoben. Allerdings glaube ich, dass durch die korrekte 
Behandlung der Variablen in und um die ISR die du vorgeschlagen hast 
mein Hauptfehler beseitigt oder verbessert wurde, nach dem ich 
eigentlich auf der Suche war.
1
 // gib den Zählerwert als unsigned long aus:
2
           printf("%lu\n",myCounter);
3
           counter256 = 0;

ist counter256 nicht an der Stelle schon Null, da er ja in der 
ISR(INT1_vect) schon auf Null gesetzt wird?


Wenn ich meine Inkrementalscheibe mit einer konstanten Geschwindigkeit 
angetrieben habe, habe ich ohne deine Korrekturen Messfehler erhalten. 
Dann hatte ich in periodischen Abständen etwa solche Messergebnisse:

Terminal:
80
81
79
80
80
83
78
1
5
1
87
79

Also ein Abfallen der Werte, ohne dass sich die Antriebsgeschwindigkeit 
geändert hätte. Nach deinen Korrekturen ist das Abfallen nicht mehr so 
stark, aber immer noch vorhanden.

Terminal:
81
77
78
79
76
75
79
78
73
47
27
76
48
29
46
28
45
28
28
49
28
76
74
74
76
77

Dass die Werte nicht permanent auf einem konstanten Wert sind führe ich 
auf einen nicht perfekten Rundlauf der Welle mit der Inkrementalscheibe 
zurück. Aber das Abfallen der Werte kann ich mir immer noch nicht 
erklären.
Angehängte Dateien:
OP #4423986
Lesenswert?

Walter S. schrieb:
> Dirk D. schrieb:
>> ist counter256 nicht an der Stelle schon Null, da er ja in der
>> ISR(INT1_vect) schon auf Null gesetzt wird?
>
> das stimmt, aber er ist vermutlich nicht mehr null sondern wurde
> zwischenzeitlich schon erhöht, das wird vermutlich auch deine zu kleine
> Werte verursachen

Ich bilde mir ein, dass es besser ist. Aber es tritt leider immer noch 
auf :/
Angehängte Dateien:
OP #4424051
Lesenswert?

lalala schrieb:
> Kann auch die Anbindung der Scheibe an den Controller sein. Wie stellst
> Du sicher, das mix einstreut? Schaltplan?

Wie meinst du das? Ich verwende das AVR-NET-IO Board und am INT1 Pin vom 
ATmega32 liegt der Ausgang des Sensors an. Alle Kabel sind vernünftig 
gelötet und mit Schrumpfschläuchen abisoliert. Ein Wackeln an den Kabeln 
beeinflusst auch nicht das Messergebnis.

Ich kann dir auch einen Schaltplan erstellen, aber dafür hab ich kein 
Programm zur Hand.
Gast #4424288
Lesenswert?

Dirk D. schrieb:
> lalala schrieb:
> Kann auch die Anbindung der Scheibe an den Controller sein. Wie stellst
> Du sicher, das mix einstreut? Schaltplan?
>
> Wie meinst du das? Ich verwende das AVR-NET-IO Board und am INT1 Pin vom
> ATmega32 liegt der Ausgang des Sensors an. Alle Kabel sind vernünftig
> gelötet und mit Schrumpfschläuchen abisoliert. Ein Wackeln an den Kabeln
> beeinflusst auch nicht das Messergebnis.
>
> Ich kann dir auch einen Schaltplan erstellen, aber dafür hab ich kein
> Programm zur Hand.

Wackel nicht, aber was passiert wenn Du mal Dein Handy daneben legst 
(und dann noch  mal anrufst?)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren