mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Fehler bei volatile-variable und ISR


Autor: Lorenz .. (lorenz)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

basierend auf dem SoftwareUART und dem RC5 Librarys von PeDa versuche 
ich mit einem Tiny13 einen RC5-Decoder zu bauen, welcher seine Daten 
decodiert per rs232 an den PC liefert. Die RC5-Routine läuft prima, 
jedoch habe ich Probleme beim SoftwareUART.
Dabei geht es weniger um ein programmspezifisches Problem, sondern um 
ein allg. Datenaustauschproblem zwischen ISR und dem Hauptprogramm.

Im Hauptprogramm habe ich eine globale Variable (stx_counter) vom Typ 
unsigned char, in welcher die Anzahl der noch zu sendenden Bits 
gespeichert ist. Diese Variable ist volatile-deklariert, damit sie 
innerhalb der ISR und im Hauptprogramm verwendet werden kann.
Ändere ich diese Variable nun im Hauptprogramm (in sputchar()) auf 10, 
so wird mir das im Simulator auch so angezeigt. Sobald kurz danach aber 
die zur Abarbeitung notwendige ISR gestartet wird, ist die Variable aber 
auf einmal 0!

Das macht mich noch total wahnsinnig. Ich komme einfach nicht darauf 
warum.

Auszug aus dem Code an besagter Stelle:
void sputchar(unsigned char val )  // send byte
{
  while( stx_counter );      // until last byte finished
  stx_data = ~val;      // invert data for Stop bit generation
  stx_counter = 10;      // 10 bits: Start + data + Stop
}

void sputs(unsigned char* txt)    // send string
{
  while( *txt )
    sputchar( *txt++ );
}

ISR( SIG_OUTPUT_COMPARE0B )    // tx bit
{
  unsigned  dout;
  unsigned char mycount;

  OCR0B += BIT_TIME;      // next bit slice
  mycount = stx_counter;

  if( mycount ){
    mycount = --stx_counter;    // count down
    dout = 1<<COM0B1;      // set low on next compare
    if( mycount != 9 ){      // no start bit
      if( !(stx_data & 1) )    // test inverted data
  dout = 1<<COM0B1^1<<COM0B0;  // set high on next compare
      stx_data >>= 1;      // shift zero in from left
    }
    TCCR0A |= dout;
  }
}

Ich hoffe es hat jemand einen Rat für mich. Den gesamten Code habe ich 
auch noch angehägt. Hänge nun schon seit 2 Nächten daran...

Danke

Lorenz

Autor: neuer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hänge nun schon seit 2 Nächten daran...

nimm den tag noch dazu...

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dein C-Listing und das Assemblerlisting eines Minimalprogramms sieht für 
mich OK aus.

Wie voll ist dein RAM? Kann es passieren, dass der Stack von oben her in 
deine Variablen rein läuft (Stackoverflow)? Tausche mal 
Variablenpositionen im Quelltext.

Autor: Lorenz .. (lorenz)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Okay, danke für den Stacktipp. Der scheint mir Probleme zu machen. So 
merke ich beim Einzelschrittdebugging (bisher nur Breakpoints benutzt) 
einige Resets des ProgramCounters. Nach dem Abarbeiten der Init-Routinen 
bleibt er mir ab und zu hängen und fängt dann von vorne an... In der 
Simulation kann ich jedoch keinen genauen Zusammenhang erkennen - mal 
gehts, mal nicht.

Trotzdem stehe ich immer noch vor einem unlösbaren Problem. Ich habe 
einfach keine Ahnung was ich sonst noch ändern könnte. Variablen habe 
ich so weit wie möglich reduziert und nur die nötigsten als "volatile" 
deklariert. Aber selbst bei einer golbalen und einer volatile-variablen 
kommt er aus dem Tritt.

Gibt's da irgendeine besonders geeignete Debuggingmethode solchen 
Fehlern auf die Schliche zu kommen?

Danke

Lorenz

PS: die Dateien SUART.* im zip-file sind unnötig, der Inhalt ist in der 
main.c integriert.

Autor: Bj (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
... mal bei K&R nachlesen was das Attribut "volatile" bedeuted. Es wird 
lediglich die Optimierung der Sequenz unterbunden, aber nicht 
automatisch eine Interrupt-Sperre gesetzt. Wenn du also weiterhin mit 
globalen Variablen in verschiedenen Kontexten arbeiten möchtest, dann 
kommst du nicht umhin die "Critical Sections" mit Interrupt-Sperren zu 
schützen.

Vielleicht ist's ja z.Zt. wirklich der Stack, aber inkonsistente Daten 
auf Grund fehlender rmw-Protection sind hier nur eine Frage der Zeit.

Bj

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Tipp von Bj ist gut. Wie das dort angesprochene funktioniert, steht 
im Artikel AVR Interrupts.

Zum Sparen: Schmeiss' den langen Begrüssungstext raus oder kürze. Das 
spart heftig. Wenn du den Text so brauchst, verlagere den ins ROM (im 
AVR-GCC-Tutorial steht wie das mit PROGMEM geht).

Autor: Lorenz .. (lorenz)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

also ich habe das Projekt mittlerweile erfolgreich zum Laufen gebracht.
Zuerst habe ich die Aufteilung auf mehrere Funktionen rückgängig 
gemacht. So wird z.B. der Init direkt im Hauptprogramm durchgeführt, 
nicht mehr in einer Unterroutine. Das hat bei den Sprüngen schon mal gut 
geholfen, der Stack läuft nun nicht mehr über.
Was Bj mit dem Hinweis auf "volatile" meint ist mir rätselhaft. Wie das 
genutzt wird ist schon klar, jedoch ist meinem Code keine critical 
secion wovor die IRQs deaktiviert werden sollten. Aber egal, hat mein 
Interesse geweckt, da werde ich mich gleich mal einlesen.

Danke für eure Ratschläge

Gruß

Lorenz

Autor: Bj (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
...wenn du einen K&R zur Hand hast, dann lese !

Ansonsten sei dir erklärt dass eine Compiler keine Ahnung von Hardware 
hat. Wenn man also nun auf Port's oder Hardware-Zellen zugreift, dann 
ist es für die Toolchain nur eine Schreib/Lese Operation auf eine 
Speicherzelle, mehr nicht. Um nun dem Optimizer mitzuteilen, das eine 
Sequenz genau so abgearbeitet werden soll wie im Quellcode geschrieben, 
erklärt man die "Variablen", die eigentlich eine Hardware ansprechen als 
"volatile".

Liest man z.B. von einem Port, rechnet etwas und liest erneut von dem 
Port, so können diese Daten durchaus verschieden sein da ja die Signale 
in der Zwischenzeit anders sein können. Teilt man das dem 
Compiler/Optimizer nicht mit, so erkennt dieser lesen von Adr. XXXX und 
eine Zeit später wieder lesen von XXXX und wird das zusammenfassen, da 
ja zwischenzeitlich im Speicher nichts an Adresse XXXX passiert ist. Um 
das sinnvolle zweifache Lesen zu erzwingen musst du dem Tool halt 
mitteilen, dass die Daten volatil sind.

Das hat rein gar nichts mit deiner Interpretation zu tun (Diese Variable 
ist volatile-deklariert, damit sie innerhalb der ISR und im 
Hauptprogramm verwendet werden kann.)

Weiterhin gibt es fast immer Critial Sections bei Verwendung von 
globalen Variablen in Foreground und Interrupt. Schalte mal die 
Optimierung aus und schaue das Assembler-Listing an. 
Variablen-Manipulation ist fast ausnahmslos read-modify-write. Wird 
diese nicht-atomare Sequenz unterbrochen, dann kann es durchaus 
vorkommen dass der "write" was anders zurückschreibt als das "modify" 
generiert hat. Kenne zwar den Befehlssatz des AVR nicht auswendig, man 
möge mich aber korrigieren wenn die CPU direkt im Speicher manipulieren 
kann.

Bj

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.