Forum: Mikrocontroller und Digitale Elektronik ATmega32 AVR C Programmierphänomen Codefehler


von Dirk D. (sureai)


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

von Ingo L. (corrtexx)


Lesenswert?

Dirk D. schrieb:
> printf("%d\n",counter256);
Gibt einen signed int aus, du hast aber einen unsigned long. Ändere 
deine printf-Formatierung zu %lu

Merkwürdig das der Compiler keine Warnung schmeißt...

Was machst du eigentlich mit der Variable "counterUmdrehung3"? Die 
scheint überflüssig!?

: Bearbeitet durch User
von Dirk D. (sureai)


Lesenswert?

dankeschön!!!
Ich nehme mal an, unsingned int wäre dann %du.

von Peter II (Gast)


Lesenswert?

Dirk D. schrieb:
> Ich nehme mal an, unsingned int wäre dann %du.

nein.

(C ist gut dokumentiert!)

von Dirk D. (sureai)


Lesenswert?

Ingo L. schrieb:
> Was machst du eigentlich mit der Variable "counterUmdrehung3"? Die
> scheint überflüssig!?

Ja noch. Habe ein komplexeres Programm und Schritt für Schritt den Code 
vereinfacht um den Fehler zu finden.

von Ingo L. (corrtexx)


Lesenswert?


von Dirk D. (sureai)


Lesenswert?

danke für das Lesezeichen :)

von Stefan K. (stefan64)


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

von Dirk D. (sureai)


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?

von S. R. (svenska)


Lesenswert?

Auch das schützt nicht vor den negativen Ausgaben vom printf(), aber ja.

von Walter S. (avatar)


Lesenswert?

Dirk D. schrieb:
> Meinst du so?

fast, üblicherweise holt man sich nur kurz die Variable und gibt dann 
den
Interupt wieder frei. Zeitaufwendigere Sachen wie printf kann man danach 
in Ruhe machen

von Stefan K. (stefan64)


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

von Dirk D. (sureai)


Angehängte Dateien:

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.

von Walter S. (avatar)


Lesenswert?

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

von Dirk D. (sureai)


Angehängte Dateien:

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 :/

von Dirk D. (sureai)


Angehängte Dateien:

Lesenswert?

Oder auch nicht, bei hohen Geschwindigkeiten tritt es wieder häufiger 
auf

von lalala (Gast)


Lesenswert?

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

von Stefan K. (stefan64)


Lesenswert?

Sorry, das Löschen des counter256 hinter dem printf() ist natürlich ein 
Fehler. Das kann da ersatzlos verschwinden.  Das war wieder typisch 
copy/paste ...

Viele Grüße, Stefan

von Dirk D. (sureai)


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.

von M. K. (sylaina)


Lesenswert?

Dirk D. schrieb:
> Ich kann dir auch einen Schaltplan erstellen, aber dafür hab ich kein
> Programm zur Hand.

Eagle von Cadsoft kann da helfen. Die haben auch einen formschönen 
Schaltplaneditor.

von lalala (Gast)


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?)

von Dirk D. (sureai)


Lesenswert?

Es war die Unwucht der Welle und das leichte Eiern der 
Inkrementalscheibe. Hab das behoben und jetzt funktioniert es, wie es 
soll.

Danke an alle die mir geholfen haben!

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.