Forum: Compiler & IDEs AVR-Toolchain optimization + volatile = Problem


von Tobias S. (Firma: none) (tobimc) Benutzerseite


Lesenswert?

Hi,

ich habe ein kleines aber lästiges Problemchen...
folgender ISR-Code liest einen Rotary-encoder aus:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#include "globals.h"
5
#include "abstractions.h"
6
7
#include "ui/ui.h"
8
9
volatile uint8_t test = 0;
10
ISR(INT0_vect){     // SIG_A from encoder
11
    LED_TOG
12
    if(SIG_A){
13
        if(SIG_B){
14
            g_ui_cursor++;
15
            test+=1; /// NOTE: DEBUG
16
            uart_putc('a'); /// NOTE: DEBUG
17
            g_ui_status |= uistat_CursorINC;
18
        }else{
19
            g_ui_cursor--;
20
            test-=1; /// NOTE: DEBUG
21
            uart_putc('b'); /// NOTE: DEBUG
22
            g_ui_status |= uistat_CursorDEC;
23
        }
24
    }else{
25
        if(SIG_B){
26
            g_ui_cursor--;
27
            test-=1; /// NOTE: DEBUG
28
            uart_putc('c'); /// NOTE: DEBUG
29
            g_ui_status |= uistat_CursorDEC;
30
        }else{
31
            g_ui_cursor++;
32
            test+=1; /// NOTE: DEBUG
33
            uart_putc('d'); /// NOTE: DEBUG
34
            g_ui_status |= uistat_CursorINC;
35
        }
36
    }
37
38
        uart_putc((char) test); /// NOTE: DEBUG
39
        uart_puts("\n"); /// NOTE: DEBUG
40
}

Dabei soll die globale Variable g_ui_cursor (volatile int8_t) erhöht 
oder vermindert werden. Das Erkennen der Richtung etc. funktioniert 
alles, das LED_TOG Makro lässt auch mit jedem Schritt des Encoders meine 
LED toggeln.
Das Problem ist, dass g_ui_cursor nur bis +16 (manchmal auch nur bis 
+15) inkrementiert, und dann wieder bei 0 zurückgesetzt wird, statt bis 
+127 weiterzuzählen.
Ich dachte mir es könnte an der etwas komplexen (und zugegebenermaßen 
verbogenen) Variablendeklaration liegen, die in einer anderen .c-Datei 
als volatile uint_8 definiert, aber in einem header-file als extern 
uint8_t deklariert wird. Daher habe ich eine extra zu debugzwecken 
definierte volatile uint8_t Variable in der ISR-Datei eingefügt, mit der 
das Problem aber genauso auftritt. Es schient, als würden nur mit den 
unteren 4 Bit der Variable gezählt, was ja eigentlich keinen Sinn macht. 
Mit der extra Testvariable habe ich auch das 
'es-könnte-auch-anderer-code-auf-den-Speicher-zugreifen'-Problem 
ausgeschaltet, denn die test-Variable wird nur und ausschließlich in der 
ISR verwendet (habe gesamtes Projekt durchsucht!).

Ich habe das Programm dann mal ohne optimization (vorher -Os) 
compiliert, woraufhin das Problem auf wundersame Weise verschwand.
Ich verwende den AVR Toolchain von Atmel:
1
C:\avrtoolchain\bin>avr-gcc --version
2
avr-gcc (AVR_8_bit_GNU_Toolchain_3.4.2_939) 4.7.2
3
Copyright (C) 2012 Free Software Foundation, Inc.
4
This is free software; see the source for copying conditions.  There is NO
5
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Das Witzige ist, dass wenn ich den guten alten WinAVR von 2010 zum 
Compilieren verwende, der Code auch mit -Os funktioniert.
...was ist da denn jetzt das Problem ?!

Ich danke euch,
73 Tobi

von Peter II (Gast)


Lesenswert?

was kommen denn für ausgaben wenn das Programm läuft?

von inc (Gast)


Lesenswert?

>Das Problem ist, dass g_ui_cursor nur bis +16 (manchmal auch nur bis +15) 
inkrementiert,

Woher weißt du das?

von Michael (Gast)


Lesenswert?

Tobias Schlegel schrieb:
> ...was ist da denn jetzt das Problem ?!

Unter welchen Bedingungen wird in die ISR gesprungen?

von Kaj (Gast)


Lesenswert?

Tobias Schlegel schrieb:
> Das Witzige ist, dass wenn ich den guten alten WinAVR von 2010 zum
> Compilieren verwende, der Code auch mit -Os funktioniert.
Tja, mitlerweile haben wir das Jahr 2014 und dinge haben sich 
weiterentwickelt, so auch der compiler.

Tobias Schlegel schrieb:
> optimization + volatile = Problem
volatile schließt praktisch jede optimierung durch den compiler für 
diese variable aus! Ergo, kann es da auch kein Problem geben!

Was sind SIG_A und SIG_B?

Tobias Schlegel schrieb:
> Mit der extra Testvariable habe ich auch das
> 'es-könnte-auch-anderer-code-auf-den-Speicher-zugreifen'-Problem
> ausgeschaltet, denn die test-Variable wird nur und ausschließlich in der
> ISR verwendet (habe gesamtes Projekt durchsucht!).
Nö, hast du nicht.
Nur weil die Variable nur an dieser Stelle verwendet wird, heißt das ja 
nicht, das da sonst niemand an diese Speicheradresse schreiben könnte.

Tobias Schlegel schrieb:
> Das Problem ist, dass g_ui_cursor nur bis +16 (manchmal auch nur bis
> +15) inkrementiert, und dann wieder bei 0 zurückgesetzt wird, statt bis
> +127 weiterzuzählen.
inc schrieb:
> Woher weißt du das?
Tobias Schlegel schrieb:
> uart_putc((char) test); /// NOTE: DEBUG
> uart_puts("\n"); /// NOTE: DEBUG
Debugging mittels printf...tja, zur not frisst der Teufel fliegen.

Tobias Schlegel schrieb:
> ...was ist da denn jetzt das Problem ?!
Ist echt nicht böse gemeint, aber:
Das du offenischtlich keinen Debugger hast,... ist ja auch voll unnötig 
und uncool. Damit könnte man ja in den Speicher gucken, den 
Programmablauf anhalten usw. und Problem viel leichter finden... aber 
wer will das schon.

Ich behaupte mal ganz dreist, das Problem liegt in Code, den wir hier 
nicht zu sehen bekommen... so wie is in 99% der Fälle der fall ist, in 
denen gerne dem Compiler die Schuld in die schuhe geschoben wird.
Böser Compiler aber auch... und leg dir einen ordentlichen programmer 
zu, dann kannst du auch gleich debuggen, z.B. den Atmel JTAG-ICE3 oder 
gleich den Atmel-ICE. Richtig, kosten geld die Dinger, dafür spart man 
zeit.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Tobias Schlegel schrieb:
> Ich dachte mir es könnte an der etwas komplexen (und zugegebenermaßen
> verbogenen) Variablendeklaration liegen, die in einer anderen .c-Datei
> als volatile uint_8 definiert, aber in einem header-file als extern
> uint8_t deklariert wird.

Das ist Hack.  Das sollte einen Fehler vom Compiler geben weil sich 
Definition und Deklaration widersprechen.  Wenn es keinen Fehler gibt 
dann trifft mindestens einer der folgenden Punkte zu:

- es stimmt was mit deinen Headern / Includes nicht
- du verwendest zwei unterschiedliche Variablen gleichen Namens (z.B. 
eine static)
- dein Code hat Undefined Behaviour

> Ich habe das Programm dann mal ohne optimization (vorher -Os)
> compiliert, woraufhin das Problem auf wundersame Weise verschwand.
> Ich verwende den AVR Toolchain von Atmel:

Nachdem man den Zoo von benötigten Makros und Deklarationen nachgeflickt 
hat, so daß sich der Code übersetzen lässt, erzeugen folgende Compiler 
mit -Os -mmcu=atmega168 exakt den gleichen Code:

- avr-gcc 4.3.3 (WinAVR-20100110)
- avr-gcc 4.6.3
- avr-gcc 4.7.2
- avr-gcc 4.8.0
- avr-gcc 4.9.2

Wie in 99% der Fälle liegt das Problem nicht im gezeigten Code — 
bestenfalls evtl. darin, daß durch das Debug-Geraffel die ISR zu lange 
dauert, daher rekursiert, daher der RAM ausgeht, daher Unsinn passiert 
bzw. der µC resettet und nicht weiter kommt als 16.

Erste Hausaufgabe ist, den Code aufzuräumen und die "etwas komplexen 
(und zugegebenermaßen verbogenen) Variablendeklaration" zu korrigieren.

Weiters ist sicherzustellen, daß der Encoder adäquat entprellt ist. 
Ansonsten gibt's eine Flut von ISRs mit o.g. Folgen.

von Tobias S. (Firma: none) (tobimc) Benutzerseite


Lesenswert?

Hi,

Peter II schrieb:
> was kommen denn für ausgaben wenn das Programm läuft?

Ich habe gerade für die Ausgabe das Problem zu reproduzieren versucht 
und den (gleichen) Code nochmal mit dem AVR-Toolchain und -Os 
compiliert. Eigentlich habe ich ja das selbe Spielchen erwartet, 
allerdings war das Zählen diesmal komplett wild und konfus. Das habe ich 
dann zum Anlass genommen, das Problem doch wo anders zu suchen und die 
Hardware nochmals in Augenschein zu nehmen. Es scheint, als sei die 
Stromversorgung über den Programmer das Problem gewesen, über ein 
Labornetzteil funktioniert der Code jetzt tadellos. Ich habe da auch 
schon einen Verdacht... (wobei man eigentlich schon erwarten könnte, 
dass ein LT1117-3.3 auf dem Programmer 80mA 3,3V liefern kann...)
Trotzdem interessant, dass man mit dem Optimizing die Hardwarestabilität 
beeinflussen kann.

Trotzdem Vielen Dank für eure Mühe!
Tobi

: Bearbeitet durch User
von Tobias S. (Firma: none) (tobimc) Benutzerseite


Lesenswert?

Hi,

Johann L. schrieb:
> Das ist Hack.  Das sollte einen Fehler vom Compiler geben weil sich
> Definition und Deklaration widersprechen.

Stimme ich dir voll und ganz zu. Wenn du mir sagst, wie ich eine globale 
Variable volatile deklarieren kann, sodass ich sie in meinen restlichen 
20 c-Files benutzen kann ohne davon einen vom Compiler übergezogen zu 
bekommen mache ich es so!

Johann L. schrieb:
> Nachdem man den Zoo von benötigten Makros und Deklarationen nachgeflickt
> hat, so daß sich der Code übersetzen lässt

Tut mir leid; das Projekt hat ca. 1000 Zeilen reinen Code und ca. 4000 
Zeilen insgesamt, verteilt auf 29 Files. Ich wollte es so übersichtlich 
wie möglich halten.

Johann L. schrieb:
> Weiters ist sicherzustellen, daß der Encoder adäquat entprellt ist.
> Ansonsten gibt's eine Flut von ISRs mit o.g. Folgen.

Es scheint als hätte eines meiner RC-Glieder am Encoder irgendwo einen 
Kurzschluss nach GND; ein Kontakt braucht nämlich ca. 10mA mehr Strom 
als der andere...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Tobias Schlegel schrieb:
> Johann L. schrieb:
>> Das ist Hack.  Das sollte einen Fehler vom Compiler geben weil sich
>> Definition und Deklaration widersprechen.
>
> Stimme ich dir voll und ganz zu. Wenn du mir sagst, wie ich eine globale
> Variable volatile deklarieren kann, sodass ich sie in meinen restlichen
> 20 c-Files benutzen kann ohne davon einen vom Compiler übergezogen zu
> bekommen mache ich es so!

1) In dem Modul, zu dem die Variable / Funktion natürlicherweise gehört, 
wird sie definiert / implementier.

2) In den zum Modul gehörenden Header kommt die Deklaration bzw. 
Prototyp.

3) Alle Module, die die Variable / Funktion benötigen, includen diesen 
Header.

Und das ist unabhängig davon, ob es eine Variable ist oder eine Funktion 
oder volatile oder nicht.

Wenn 20(!) C-Module die gleiche volatile-Variable brauchen scheint was 
konzeptionell nicht zu stimmen.

von Rolf M. (rmagnus)


Lesenswert?

Tobias Schlegel schrieb:
> Johann L. schrieb:
>> Das ist Hack.  Das sollte einen Fehler vom Compiler geben weil sich
>> Definition und Deklaration widersprechen.
>
> Stimme ich dir voll und ganz zu. Wenn du mir sagst, wie ich eine globale
> Variable volatile deklarieren kann, sodass ich sie in meinen restlichen
> 20 c-Files benutzen kann ohne davon einen vom Compiler übergezogen zu
> bekommen mache ich es so!

Ich verstehe nicht, wo das Problem sein soll. Das geht ganz genauso wie 
mit einer nicht-volatile-Variablen, nur daß eben volatile vor dem Typ 
steht. Bei der Definition bekommst du das doch auch hin.

Tobias Schlegel schrieb:
> volatile uint8_t test = 0;

Deklaration:
1
extern volatile uint8_t test;

von Tobias S. (Firma: none) (tobimc) Benutzerseite


Lesenswert?

Johann L. schrieb:
> 1) In dem Modul, zu dem die Variable / Funktion natürlicherweise gehört,
> wird sie definiert / implementier.
>
> 2) In den zum Modul gehörenden Header kommt die Deklaration bzw.
> Prototyp.
>
> 3) Alle Module, die die Variable / Funktion benötigen, includen diesen
> Header.

Ja. So ist das ja auch umgesetzt...

Rolf Magnus schrieb:
> Deklaration:extern volatile uint8_t test;

...und so ist es auch konform finde ich.
Ich glaube nur mich dunkel zu erinnern dass das mir mal eine Warning 
eingebracht hat, kann aber auch sein dass ich mich da täusche.

Johann L. schrieb:
> Wenn 20(!) C-Module die gleiche volatile-Variable brauchen scheint was
> konzeptionell nicht zu stimmen.

Es sind natürlich nicht 20, ich wollte nur die Pluralität 
unterstreichen. Es sind unterm Strich vllt. 2..3 (je nach dem was ich 
mir noch einfallen lasse).

von Bastler (Gast)


Lesenswert?

Nur mal so nebenbei: gibt es nicht gut funktionierende 
Software-Entprellung für Drehgeber? Ich kann ja verstehen, daß man gern 
alles selber schreiben will, aber mal anschauen ist nicht verboten.
http://www.mikrocontroller.net/articles/Drehgeber

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.