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


von Lorenz .. (lorenz)


Angehängte Dateien:

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:
1
void sputchar(unsigned char val )  // send byte
2
{
3
  while( stx_counter );      // until last byte finished
4
  stx_data = ~val;      // invert data for Stop bit generation
5
  stx_counter = 10;      // 10 bits: Start + data + Stop
6
}
7
8
void sputs(unsigned char* txt)    // send string
9
{
10
  while( *txt )
11
    sputchar( *txt++ );
12
}
13
14
ISR( SIG_OUTPUT_COMPARE0B )    // tx bit
15
{
16
  unsigned  dout;
17
  unsigned char mycount;
18
19
  OCR0B += BIT_TIME;      // next bit slice
20
  mycount = stx_counter;
21
22
  if( mycount ){
23
    mycount = --stx_counter;    // count down
24
    dout = 1<<COM0B1;      // set low on next compare
25
    if( mycount != 9 ){      // no start bit
26
      if( !(stx_data & 1) )    // test inverted data
27
  dout = 1<<COM0B1^1<<COM0B0;  // set high on next compare
28
      stx_data >>= 1;      // shift zero in from left
29
    }
30
    TCCR0A |= dout;
31
  }
32
}

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

von neuer (Gast)


Lesenswert?

Hänge nun schon seit 2 Nächten daran...

nimm den tag noch dazu...

von Stefan B. (stefan) Benutzerseite


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.

von Lorenz .. (lorenz)


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.

von Bj (Gast)


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

von Stefan B. (stefan) Benutzerseite


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

von Lorenz .. (lorenz)


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

von Bj (Gast)


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

von Falk B. (falk)


Lesenswert?


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.