Forum: Mikrocontroller und Digitale Elektronik STM32 volatile statusvariable in while loop nicht aktualisiert


von Willem B. (mr_willem)


Lesenswert?

Hallo liebe Forumsmitglieder,

ich habe eine Entdeckung in einem meiner Programme für den STM32 gemacht 
und wollte wissen ob das ein Problem des Compilers oder meines 
Verständnisses ist.

Zur Hardware,
angetrieben wird alles von einem STM32F103RBT6 auf dem Board befinden 
sich mehrere LED Controller die über SPI angesprochen werden, 9 
Pushbuttons die über zwei ADC Kanäle ausgelesen werden und 7 
Drehenkoder. Der Status der ADC Kanäle und der Drehenkoder wird über 
einen Timer alle 500 uS abgerufen.

DMA Channel 1 versorgt den ADC
DMA Channel 2 und 3 sind für SPI1 zuständig

Insofern sich etwas geändert werden dann die neuen werte in ein Array 
geschrieben, die Adresse des DMA Controllers wird auf das Array gesetzt 
und der Transfer gestartet.

Um den Status des SPI zu verfolgen habe ich die globale variable
volatile uint8_t SPI1_BLOCK definiert.

Möchte ich einen SPI transfer starten prüfe ich zunächst ob der SPI Bus 
blockiert ist, dann setze ich zuvor meine global definierte variable auf 
einen definierten wert. Zum einen wird hierdurch gezeigt das der SPI Bus 
busy ist, zum anderen weiss die ISR hierdurch später welcher Chip Select 
bedient werden muss. Am Ende der ISR wird dieser Wert wieder auf 0 
gesetzt und somit der SPI Bus frei gegeben.

Zu der Beobachtung. In meinem Programm prüfe ich wie gesagt ob der SPI 
Bus frei ist.
Der Code den ich zunächst verwendet habe war

while(SPI1_BLOCK != SPI_BLOCK_FREE);

das funktioniert allerdings nicht. Der Status von SPI1_BLOCK ändert sich 
nicht, das Programm blockiert an der stelle.
Folgender Code läuft allerdings.

while(SPI1_BLOCK != SPI_BLOCK_FREE)
{
  if(i<64)
  {
    i++;
  }
  else
  {
    i=SPI1_BLOCK;
  }
}

Ich habe erwartet, das auch das erste Konstrukt funktioniert weil die 
Variable ja volatile definiert ist und deswegen doch bei jedem Aufruf 
erneut aus dem Speicher gelesen wird.
Denke ich da falsch?

von (prx) A. K. (prx)


Lesenswert?

Willem B. schrieb:
> Der Status von SPI1_BLOCK ändert sich
> nicht, das Programm blockiert an der stelle.

Woher weisst du, dass SPI_BLOCK1 sich ändert?
Also sich wirklich ändert, nicht nur im Kopf.

von Peter II (Gast)


Lesenswert?

schau doch einfach mal im ASM code nach, ob er die Variabel wirklich 
prüft.

Außerdem ist es eine schlechte Idee alles Großbuchstaben für Variablen 
zu verwenden. Das sollte man nur für defines machen.

von Willem B. (mr_willem)


Lesenswert?

@A. K.
Also ich weiss aus dem Debugger, dass das Programm an der Stelle denkt 
SPI1_BLOCK bleibt unverändert.

und andersherum weiss ich aus dem zweiten Code das SPI1_BLOCK sich 
ändert. Es wird 0 und das Programm läuft weiter.

Die Zeilen sind die einzigen Unterschiede die ich gemacht habe und
der eine Code läuft, der andere nicht.

Um es zu vereinfachen habe ich auch folgendes getestet

while(SPI1_BLOCK != SPI_BLOCK_FREE);  // läuft nicht

uint8_t i;
while(SPI1_BLOCK != SPI_BLOCK_FREE) // läuft
{
    i=SPI1_BLOCK;
}

das sind die einzigen Unterschiede im Programm.

@Peter II
schau doch einfach mal im ASM code nach, ob er die Variabel wirklich
prüft.

OK, das werde ich machen

Außerdem ist es eine schlechte Idee alles Großbuchstaben für Variablen
zu verwenden. Das sollte man nur für defines machen.

Ich hatte ausschliesslich meine globalen Variablen auch mit 
Großbuchstaben definiert um sie zu unterscheiden. Aber da lasse ich mir 
dann was anderes einfallen.

: Bearbeitet durch User
von Philipp R. (njsd)


Lesenswert?

Poste mal deinen ganzen Code (Pastebin oder so).

Zudem sollte deine Variable nicht nur volatile sein, nein, sie sollte 
auch atomic sein.

http://www.gnu.org/software/libc/manual/html_node/Atomic-Types.html

Imho könntest du dich in die Multithreading Problematik einlesen, dort 
werden Locks, Mutex, Semaphore usw. aufgezeigt.

http://www.csc.villanova.edu/~mdamian/threads/posixsem.html

von (prx) A. K. (prx)


Lesenswert?

Philipp R. schrieb:
> Zudem sollte deine Variable nicht nur volatile sein, nein, sie sollte
> auch atomic sein.

Wieviele auch in C programmierbare Controller kennst du, bei denen Lade- 
und Speicheroperationen auf ein Byte nicht atomar sind?

Willem B. schrieb:
> volatile uint8_t SPI1_BLOCK definiert.

Philipp R. schrieb:
> Multithreading Problematik einlesen

Worin besteht der Zusammenhang mit diesem Problem?

: Bearbeitet durch User
von Willem B. (mr_willem)


Lesenswert?

@Philipp R.
OK, hier die relevanten Funktionen oder soll ich alles posten?

http://pastebin.com/Yfg3vrEu

von Hanswurst (Gast)


Lesenswert?

Zunächst mal kannst Du Code auch hier als Anhang posten. Das ist besser, 
weil man dann alles an einem Ort hat. Ob Pastebin langfristig lesbar 
sind, weiß man nicht. Soviel dazu.

Ich habe den Code nicht detailliert untersucht, aber mir fällt auf, dass 
Du die Variablen an mehreren Stellen schreibst und das sie mehrere 
Zustände haben kann. Dazu ist es eigentlich unnötig, so ein Flag 
innerhalb einer ISR zu setzen und dann wieder zurückzusetzen, denn es 
kann während der Ausführung der ISR sowieso kein anderer Code (ausser 
anderen ISR beim ARM) ausgeführt werden. Aber ich kann nicht erkennen 
(wohlgemerkt: ich habe nur oberflächlich hingeguckt) dass diese Variable 
in anderen ISRs verwendet wird.

Diese mehrfachen Bedeutungen der Variable solltest Du voneinander 
trennen. Das macht es einfacher und übersichtlicher. Falls das 
tatsächlich ein konkretes Problem darstellt, solltest Du im Debugger 
auch den Wert sehen können, wenn die while-Schleife nicht verlassen 
wird. Entspricht der Wert einem der anderen, die Du verwendest?

An sich wäre es auch gut, wenn Du einmal ausführlich erklären würdest, 
welche Wirkung die verschiedenen möglichen Werte der Variablen haben 
sollen. Wenigstens für Dich um Klarheit zu erlangen. Ein 
Zustandsdiagramm wäre vieleicht auch nützlich.

von Willem B. (mr_willem)


Lesenswert?

Also es gibt vier tlc5947 led driver, einen tlc5926 led driver und einen 
as1108 siebensegment driver. Alle teilen den spi Bus, jedes hat ein 
eigenes Chip Select.
Jeder chip hat eine Zahl zugeordnet. Möchte ich nun Daten an Chip 1 
senden prüfe ich ob SPI1_BLOCK 0 ist und setze es  auf 1, dadurch weiss
1. Das Programm der spi Bus ist blockiert. Denn der Rest des Programms 
läuft ja weiter wenn der dma gestartet ist und könnte auf die Idee 
kommen auch den spi Bus zu starten.
2. Die Spi1_send Funktion welcher Chip select bedient wird.
3. Die ISR welcher Chip select bedient wird. Nachdem die ISR 
durchgelaufen ist wird SPI1_BLOCK auf 0 gesetzt. Der Bus ist dadurch 
wieder frei gegeben.

: Bearbeitet durch User
von Hosenmatz (Gast)


Lesenswert?

Nun, daraus scheint mir hervorzugehen, dass es keineswegs unmöglich ist, 
dass die while-Schleife nicht verlassen wird, denn die Variable kann 
noch andere Werte als SPI_BLOCK_FREE haben.

Diese Konstruktion mit dem kopieren nach i, sollte eine Optimierung an 
sich nicht austricksen, denn wenn der Compiler - von Dir unbeabsichtigt 
- davon ausgehen sollte, dass sich die Variable nicht ändern kann, dann 
kann er auch schliessen, dass i immer den Wert der Variablen haben wird. 
Die Details kann man so nicht erkennen. Es fehlt auch die 
Variablendeklaration.

Ich würde zunächst, wie oben schon gesagt, einfach mal schauen, welchen 
Wert die Variable hat, wenn die Schleife nicht verlassen wird, obwohl 
Du es erwartest. Vermutlich ist einfach einer der Interrupts noch nicht 
ausgeführt worden und die Umkopiervariante braucht einfach zusätzlich 
Zeit, die dann ausreicht, dass der Interrupt ausgeführt wird. Das 
würdest Du an dem tatsächlichen Wert sehen können, vermute ich.

Dennoch würde ich meinen obigen Rat wiederholen und ein Flag, dass 
anzeigt strikt von einer Zielangabe (die das CS-Bit auswählt) trennen. 
Das ist sozusagen mehr "straight-forward". Zwar ist, was Du versuchst 
nicht streng falsch aber es verquickt zwei Bedeutungen miteinander, 
die ihrem inneren Sinn nach, nichts miteinander zu tun haben.

von Nop (Gast)


Lesenswert?

Willem B. schrieb:
> Um den Status des SPI zu verfolgen habe ich die globale variable
> volatile uint8_t SPI1_BLOCK definiert.

Hast Du die in den anderen Modulen denn auch als
1
extern volatile uint8_t SPI1_BLOCK;
deklariert? Oder hast Du da etwa
1
extern uint8_t SPI1_BLOCK;
gemacht?

von Willem B. (mr_willem)


Lesenswert?

ich habe in meiner spi.h
1
extern volatile uint8_t SPI1_BLOCK;
 stehen und dann einfach den header eingebunden

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

... von welchem Compiler reden wir denn überhaupt?

von Willem B. (mr_willem)


Lesenswert?

Es ist ein gcc arm-none-eabi version 4.8

von Hosenmatz (Gast)


Lesenswert?

Willem B. schrieb:
> ich habe in meiner spi.h
>
>
1
extern volatile uint8_t SPI1_BLOCK;
 stehen und dann einfach den
> header eingebunden

Eine "extern"-Deklaration ist noch keine "Definition", die Speicherplatz 
reserviert. Es ist nur die Information an den Compiler, dass in einer 
anderen Quellcode-Datei eine "Definition" existiert. Man müsste die 
"Definition" sehen. Dazwischen besteht ein klar beschriebener 
Unterschied.

Falls da was schief läuft müsste man (bei den Compilern, die ich kenne) 
auch Warnings sehen. Wie schon mal geschrieben: Zeige am besten den 
kompletten Code. Oder reduziere den Code auf ein äquivalentes, 
compilierbares Beispiel, bei dem das Verhalten auch auftritt.

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.