Forum: Mikrocontroller und Digitale Elektronik Warten per leere Anweisung


von Phillip H. (philharmony)


Lesenswert?

Hi,
Ich habe mal wieder ein kleines Problemchen:

Ich möchte Veränderungen an den Eingangspins per UART verschicken. Das 
ganze mache ich über einen Ringpuffer, das senden geschieht dann per 
UDR_Empty Interrupt. Da sich auch mal viele Eingangspins auf einmal 
ändern können und keine Informationen untern den Tisch fallen sollen, 
möchte ich, wenn der Ringpuffer voll ist, einfach so lange warten, bis 
dieser wieder leer ist.
Die Ringpuffer-Geschichte funktioniert auch tadellos, allerdings gibt es 
Probleme, wenn der Puffer tatächlihc voll ist.
Die Funktion sieht so aus:
1
/*String in Ringpuffer schreiben*/
2
void put_string_to_ringbuffer(struct t_buffer* buffer, uint8_t* p_start)
3
{
4
  do
5
  {
6
    /*so lange versuchen, bis kein Fehler mehr zurückkommt*/
7
    while(put_to_ringbuffer(buffer, *p_start) != NO_ERROR)
8
    {
9
                      ; //Hab schon alle Schreibweisen, mit und ohne Klammern versucht
10
    }
11
  }
12
  /*Bis zum Endzeichen inklusive wiederholen*/
13
  while (*(p_start++) != STR_TERMINATOR);
14
15
  /*String freigeben*/
16
  release_last_frame(buffer);
17
}

Dabei hängt er sich auf. Als Rückgabewert der put_to_ringbuffer gibt es 
nur NO_ERROR oder BUFFER_FULL.

Ich hatte dann mal zum debuggen eine uart-Sendefunktion in die leere 
Anweisung geschrieben. Diese wurde zwar nie ausgeführt, aber dafür 
hängte er sich auch nicht mehr auf???
Ich weiß, das ist kein lauffähiges Beispiel, mein gesamter Code ist nur 
SEHR groß, daher einfach nur die Frage: kann diese leere Anweisung 
Probleme hervorrufen?

Grüße
Phil

von Alexander V. (avogra)


Lesenswert?

da wärs jetzt interessant, was in put_to_ringbuffer steht :) Ob der 
leere Block nur aus {} oder aus {;} oder sogar nur aus ; besteht sollte 
eigentlich wurst sein. Ich kann mir nicht vorstellen, dass hier das 
Problem liegt.

Was mir einfällt: Was passiert, falls sich das Programm gerade in 
put_to_ringbuffer befindet, wenn der Interrupt auslöst? Falls der Puffer 
gerade voll ist, ist das ja quasi ständig der Fall.

Gruß, Alex

von Phillip H. (philharmony)


Lesenswert?

>Was mir einfällt: Was passiert, falls sich das Programm gerade in
>put_to_ringbuffer befindet, wenn der Interrupt auslöst? Falls der Puffer
>gerade voll ist, ist das ja quasi ständig der Fall.

Das sollte kein Problem sein. Put to Ringbuffer legt einfach einzelne 
Zeichen in den Puffer. Erst wenn das Terminatorzeichen von 
Put_str_to_ringbuffer gesetzt wurde, wird der Lese-Endzeiger für das 
Interrupt weitergeschoben und der gesamte Frame zum Senden freigegeben.
Wenn der Puffer am Ende voll ist, sendet das Interrupt ja trotzdem vom 
Anfang her die Zeichen raus und gibt den Speicher wieder frei. Einziger 
Abschussfall wäre, wenn ein einzelner Frame länge wäre als der Puffer, 
das kann aber nicht passieren.

Hier die Put to Ringbuffer
1
/*Ein Byte in Ringpuffer schreiben*/
2
uint8_t put_to_ringbuffer(struct t_buffer* buffer, uint8_t data)
3
{
4
  /*Aktuelles Schreibeende an Prüfzeiger übergeben*/
5
  uint8_t* p_check = buffer->p_next_write;
6
  /*Prüfzeiger erhöhen*/  
7
  if(++p_check > (buffer->p_buffer_start + buffer->size)) p_check = buffer->p_buffer_start;
8
9
  /*Prüfen, ob das Schreibeende auf dem nächsten Zeichen liegt*/
10
  /*Schreibeende = Das erste Zeichen daß NICHT mehr geschrieben werden soll*/
11
  if(p_check == buffer->p_end_write)
12
  {
13
    /*Buffer Full zurückgeben*/
14
    return BUFFER_FULL;
15
  }
16
17
  /*Wenn nächstes Zeichen nicht das Schreibeende ist*/
18
  else
19
  {
20
    /*Schreibezeiger auf nächste Stelle*/
21
    buffer->p_next_write = p_check;
22
23
    /*An diese Stelle schreiben*/
24
    *(buffer->p_next_write) = data;
25
26
    /*NO ERROR Zurückgeben*/
27
    return NO_ERROR;
28
  }
29
}

von Stefan E. (sternst)


Lesenswert?

Und in welchem Kontext wird put_string_to_ringbuffer aufgerufen? Dir ist 
schon klar, dass wenn dort Interrupts gerade abgeschaltet sind (z.B. in 
einer ISR), dass dann das "Warten per leere Anweisung" bei vollem Puffer 
zur Endlosschleife wird, oder?

von Alexander V. (avogra)


Lesenswert?

Ok, ich versuch mal zu rekapitulieren, was der Code macht.
p_check ist ne zwischenvariable, mit der überprüft wird, ob der nächste 
platz im puffer frei zum schreiben ist.
p_next_write ist der nächste platz, an dem daten in den puffer 
geschrieben werden sollen.
p_end_write versteh ich nicht zu 100%. Ist das der Zeiger, dessen Daten 
als nächstes vom UART übertragen werden sollen? Siehe dazu nächster 
Absatz.
Du lässt jetzt p_check auf den nächsten zu beschreibenden Platz zeigen. 
Wenn der noch belegt ist, wird mit BUFFER_FULL abgebrochen. Wenn nicht, 
wird p_next_write auf diesen Platz gebogen und die anstehenden Daten auf 
diesen Platz geschrieben.
Ich würde nochmal ganz genau schaun, was passiert, wenn eine beliebige 
dieser Anweisungen von der Sende-Routine unterbrochen wird. Ich kann mir 
gut vorstellen, dass es hier Probleme geben könnte. Bedenke dabei, dass 
das auch mitten in einer Zeile sein kann! Ganz einfach umgehen kannst du 
das, wenn du die gesamte Routine atomar machst, also während der Routine 
keine Interrupts erlaubst mit cli() am Anfang und sei() am Ende. Sollte 
während dessen ein Interrupt aufgetreten sein, wird der sofort nach 
sei() nachgeholt.
Grundsätzlich würde ich deine Routine ein kleines bisschen umbauen. 
Konkret: Ich finde es sinnvoller, wenn p_next_write nicht auf den 
zuletzt beschriebenen Platz zeigt, sondern auf den nächsten zu 
beschreibenden. Damit sparst du dir die ganze Geschichte mit p_check.
Also:
- ist p_next_write == p_end_write ? -> ja: dann Abbruch mit Buffer_full
   -> nein: dann ist der Platz frei
- Daten an diesen Platz schreiben
- p_next_write erhöhen (mit Umbruch wie bisher für p_check)


 Du schreibst, dass ein Frame erst komplett im Puffer liegen muss, bevor 
er übertragen wird.
Das kann ich nirgends finden. Und dann frag ich mich noch, warum du 
darauf wartest? Wenn Daten im Puffer liegen, dann würde ich die von der 
Sende-Routine auch so schnell wie möglich übertragen. Ob das ein 
kompletter Datensatz ist, wäre mir egal. Falls die Daten gemeinsam 
ankommen müssen, würde ich evtl. ein Magic-Byte für "Start frame" 
reservieren oder alternativ ein "End frame". Alles was danach kommt, 
gehört dann zu diesem Frame, bis das nächste "Start frame" kommt. Evtl. 
könnte man stattdessen auch eine Hardware-Handshake Leitung zum selben 
Zweck missbrauchen.

Dann ist mir noch was aus deinem ersten Post aufgefallen, an dem dein 
Problem liegen könnte:

Du schreibst, dass du UDR_empty zum verschicken benutzt. Was passiert, 
wenn gerade keine Daten zu verschicken sind? Dann wird UDR_empty nicht 
mehr aufgerufen (es wurde ja nichts verschickt) und es werden nie wieder 
Daten verschickt, außer, du überträgst irgendwo nochmal was per Hand -> 
deine Debug-routine? Bist du ganz sicher, dass die nicht aufgerufen 
wurde?

So, ich hoffe, mein Roman hilft dir^^

Gruß, Alex

von Phillip H. (philharmony)


Lesenswert?

put_string_to_ringbuffer wird aus der Main heraus aufgerufen (es wird 
gecheckt, ob sich Pinzustände geändert haben, wenn ja dann wird ein 
String gebaut und in den Ringpuffer geschoben.) Wird der Frame 
freigegeben, wird das UDRE-ISR aktiviert und es fängt an, den Puffer 
rauszusenden. Dabei wird der Speicher jedes Zeichens nachdem es in den 
UDR geladen wurde wieder freigegeben. Kommt es also beim Versuch, den 
String zu schreiben zu einem Fehler (Buffer full), so wird die Main so 
lange versuchen, dieses Zeichen zu senden, bis es geklappt hat. Das 
UDRE_Intrerrupt läuft ja "nebenher" und schaufelt den Ringpuffer wieder 
leer, somit kommt es natürlich an dieser Stelle zu einem Flaschenhals 
wenn sehr viel gesendet werden soll, aber der UART ist ja aufgrund 
seiner Baudrate sowieso der Flaschenhals, und es interessiert mich 
eigentlich nicht, ob die Pins weiter ausgelesen würden, wenn ich davon 
eh noch nichts erfahre, darum darf der Controller an der Stelle ruhig 
etwas im Stau stehen, das ganze kommt in der Regel sowieso nur nach der 
Initialisierung vor, bis alle Anfangszustände ienmal gelesen sind. 
Danach ändert sich nicht mehr so viel auf einmal.
Was ich halt nicht verstehe, ist daß er sich mit der leeren Anweisung 
aufhängt, mit der Debuganweisung in der Block aber nicht...

von Klaus (Gast)


Lesenswert?

Alexander v. Grafenstein schrieb:
> Ob der
> leere Block nur aus {} oder aus {;} oder sogar nur aus ; besteht sollte
> eigentlich wurst sein. Ich kann mir nicht vorstellen, dass hier das
> Problem liegt.

Nicht 'sollte wurst sein', sondern es ist einfach definitiv egal.


Wie ist NO_ERROR und BUFFER_FULL definiert?

Sind die Pointer, die von der ISR verändert werden als volatile 
deklariert?

von Phillip H. (philharmony)


Lesenswert?

>Grundsätzlich würde ich deine Routine ein kleines bisschen umbauen.
>Konkret: Ich finde es sinnvoller, wenn p_next_write nicht auf den
>zuletzt beschriebenen Platz zeigt, sondern auf den nächsten zu
>beschreibenden. Damit sparst du dir die ganze Geschichte mit p_check.
>Also:
>- ist p_next_write == p_end_write ? -> ja: dann Abbruch mit Buffer_full
>   -> nein: dann ist der Platz frei
>- Daten an diesen Platz schreiben
>- p_next_write erhöhen (mit Umbruch wie bisher für p_check)

Das hatte ich am Anfang auch so, da ergeben sich aber einige Probleme, 
da ich z.B. nicht unterscheiden kann, wenn beide Zeiger auf einander 
liegen, ob ich am Anfang eines leeren oder am Ende eines vollen 
Speichers stehe. Habe da in der Peter Fleury Lib ein bisschen abgeguckt 
;)

>Du schreibst, dass du UDR_empty zum verschicken benutzt. Was passiert,
>wenn gerade keine Daten zu verschicken sind? Dann wird UDR_empty nicht
>mehr aufgerufen (es wurde ja nichts verschickt) und es werden nie wieder
>Daten verschickt, außer, du überträgst irgendwo nochmal was per Hand ->
>deine Debug-routine? Bist du ganz sicher, dass die nicht aufgerufen
>wurde?

Das ist nicht ganz richtig: Das interrupt "poppt" nicht auf, sondern ist 
immer gesetzt, wenn der leer IST (nicht leer WIRD). Aktiviere ich das 
Interrupt, so wird es durch den zu diesem Zeitpunkt leeren UDR auch 
gleich ausgelöst. Senden stoppen tue ich per Interrupt disable. Das 
ganze ist auch nicht das Problem, die Sende-Geschichte tut anstandslos, 
sendet durchaus mal ne Stunde nicht und ist dann sofort wieder da wenn 
an den Pins was passiert, da sollte das Problem nicht liegen. Das 
Problem trat wirklich erst auf, wenn der Puffer mal wirklich voll läuft.

>p_end_write versteh ich nicht zu 100%. Ist das der Zeiger, dessen Daten
>als nächstes vom UART übertragen werden sollen? Siehe dazu nächster
>Absatz.

End Write markiert das Ende des Schreibe-Bereiches (Man könnte es auch 
gleichsetzen mit dem Anfang des Lesebereiches, damit noch nicht 
gesendete Zeichen nicht überschrieben werden) Ich habe mich hier für 
bessere Übersicht (empfand ich so) und Flexibilität dazu entschieden, 
Schreibebereich und Lesebereich "separat" zu verwalten. So wandert zb 
der Schreibezeiger beim reinschreiben weiter, das Leseende bleibt aber 
erst einmal stehen, bis der Frame freigegeben wird.

>Du schreibst, dass ein Frame erst komplett im Puffer liegen muss, bevor
>er übertragen wird.
>Das kann ich nirgends finden.

Das steht in
1
     release_last_frame(buffer);
>Und dann frag ich mich noch, warum du
>darauf wartest? Wenn Daten im Puffer liegen, dann würde ich die von der
>Sende-Routine auch so schnell wie möglich übertragen. Ob das ein
>kompletter Datensatz ist, wäre mir egal.

Das gibt mir die Möglichkeit, einen Frame bei dem ein Fehler aufgetreten 
ist (mach ich z.B. beim Empfangen über den UART, die Funktionen sind für 
beide Richtungen da) zu verwerfen, damit kein ungültiger Quatsch in die 
Auswertung kommt, indem ich dann anstatt das Leseende weiter zu ziehen 
einfach den Schreibezeiger zurück ziehe.
Aber es stimmt, ich könnte beim senden, da dort sowieso keine 
Fehlerbehandlung stattfindet, nach jedem Zeichen die release_last_frame 
aufrufen und jedes Zeichen sofort freigeben. Das macht das ganze asber 
im Zweifel lediglich minimal schneller und sollte doch eigentlich nichts 
mit meinem Problem zu tun haben oder?

Zu guter Letzt
>Ich würde nochmal ganz genau schaun, was passiert, wenn eine beliebige
>dieser Anweisungen von der Sende-Routine unterbrochen wird. Ich kann mir
>gut vorstellen, dass es hier Probleme geben könnte. Bedenke dabei, dass
>das auch mitten in einer Zeile sein kann!

Sollte eigentlich auch kein Problem sein, das interrupt liest ja nur aus 
dem Puffer, und zwar so lange, bis der Lesezeiger das Leseende erreicht 
hat. Bevor das Leseende überhaupt neu geschrieben wird, ist alles andere 
(Zeichen schreiben...) geschehen. Das Leseende ist ein Byte, daher 
eigentlich ja auch interruptfest. Entweder ist das dann bereits 
aktualisiert, oder eben noch nicht. Wenni ich hier jetzt Stuss erzähle 
dann gebt bitte bescheid ;)

>Wie ist NO_ERROR und BUFFER_FULL definiert?
NO_ERROR 0
BUFFER_FULL irgendne Zahl non Zero

>Sind die Pointer, die von der ISR verändert werden als volatile
>deklariert?
Logisch

von Stefan E. (sternst)


Lesenswert?

Phillip Hommel schrieb:
>>Sind die Pointer, die von der ISR verändert werden als volatile
>>deklariert?
> Logisch

Sehen wollen.
Wie oft schon haben wir in solchen Fällen so was gesehen:
1
volatile uint8_t *ptr;

von Phillip H. (philharmony)


Lesenswert?

Hmm, das is wenn man ne große Klappe hat, danke für den Hinweis ;)
Da könntest mir glaube ich gerne mal auf die Sprünge helfen...

Das is die ISR
1
/*USART Sende Interrupt*/
2
ISR(USART_UDRE_vect)
3
{
4
  /*Wenn keine Daten zum senden mehr vorhanden sind*/
5
  if(number_tx_frames == 0)
6
  {
7
    /*Senden deaktivieren*/
8
    end_tx();  
9
    return;
10
  }
11
12
  /*Zeichen aus Ringpuffer lesen*/
13
  uint8_t buffer = get_from_ringbuffer(&tx_buffer);
14
15
  /*Wenn das Terminatorzeichen gelesen wurde*/
16
  if(buffer == STR_TERMINATOR)
17
  {
18
    /*Frame-End-Zeichen senden*/
19
    UDR = TX_TERMINATOR;
20
21
    /*Framezähler dekrementieren*/
22
    number_tx_frames--;
23
  }
24
25
  /*Alle anderen Zeichen einfach senden*/
26
  else 
27
  {
28
    UDR = buffer;
29
  }
30
31
  return;
32
}

Das hier sind meine Puffer
1
struct  t_buffer{          
2
  
3
  uint8_t size;
4
  uint8_t* p_buffer_start;
5
  uint8_t* p_next_read;
6
  uint8_t* p_end_read;
7
  uint8_t* p_next_write;
8
  uint8_t* p_end_write;
9
};
10
11
//Empfangs- und Sendepuffer reservieren
12
uint8_t tx_buffer_data[TX_BUFFERSIZE];
13
uint8_t rx_buffer_data[RX_BUFFERSIZE];
14
struct t_buffer tx_buffer = {(TX_BUFFERSIZE - 1), tx_buffer_data, tx_buffer_data, tx_buffer_data, tx_buffer_data, tx_buffer_data};
15
struct t_buffer rx_buffer = {(RX_BUFFERSIZE - 1), rx_buffer_data,  rx_buffer_data, rx_buffer_data, rx_buffer_data, rx_buffer_data};

Jetzt häng ich grade etwas. Ich übergebe doch sowieso die Adresse, 
speichere also doch alles direkt an der jeweiligen Speicheradresse ab, 
wozu dann volatile?

von Phillip H. (philharmony)


Lesenswert?

Oh man, disregard. ;)
Also nochal richtig, hoffe ich jedenfalls.

Typzuweisung:
1
struct  t_buffer{          
2
  
3
  uint8_t size;
4
  uint8_t* p_buffer_start;
5
  uint8_t* p_next_read;
6
  uint8_t* p_end_read;        //first adress NOT to read
7
  uint8_t* p_next_write;
8
  uint8_t* p_end_write;        //first adress NOT to write
9
};
10
11
//Empfangs- und Sendepuffer reservieren
12
uint8_t tx_buffer_data[TX_BUFFERSIZE];
13
uint8_t rx_buffer_data[RX_BUFFERSIZE];
14
//               //Size            //start         //next read     //end read    //next write  //end write
15
volatile struct t_buffer tx_buffer = {(TX_BUFFERSIZE - 1), tx_buffer_data,  tx_buffer_data, tx_buffer_data, tx_buffer_data, tx_buffer_data};
16
volatile struct t_buffer rx_buffer = {(RX_BUFFERSIZE - 1), rx_buffer_data,  rx_buffer_data, rx_buffer_data, rx_buffer_data, rx_buffer_data}

und Funktion
1
/*String in Ringpuffer schreiben*/
2
void put_string_to_ringbuffer(volatile struct t_buffer* buffer, uint8_t* p_start)
3
{
4
  do
5
  {
6
    /*so lange versuchen, bis kein Fehler mehr zurückkommt*/
7
    while(put_to_ringbuffer(buffer, *p_start) != NO_ERROR)
8
    {
9
10
    }
11
  }
12
  /*Bis zum Endzeichen inklusive wiederholen*/
13
  while (*(p_start++) != STR_TERMINATOR);
14
15
  /*String freigeben*/
16
  release_last_frame(buffer);
17
}
Damit der Check auf wirklich jedes mal ausgeführt wird und nicht zu 
einmal gemacht und dann in nem Register unverändert abgelegt 
wird...Richtig?

von Phillip H. (philharmony)


Lesenswert?

So, jetzt schwimm ich richtig. Was muss denn da nun alles volatile 
deklariert werden? Ich verändere ja sowohl die Pointer, als am Ende auch 
die Zeichen auf die die Pointer zeigen...

von Alexander V. (avogra)


Lesenswert?

Ok, hast ja alles, was mir eingefallen ist, stichhaltig widerlegt :-P

Stefan Ernst schrieb:
> Und in welchem Kontext wird put_string_to_ringbuffer aufgerufen? Dir ist
> schon klar, dass wenn dort Interrupts gerade abgeschaltet sind (z.B. in
> einer ISR), dass dann das "Warten per leere Anweisung" bei vollem Puffer
> zur Endlosschleife wird, oder?

Das hast du aber noch nicht ausgeschlossen :-P
Wenn noch Daten im Puffer sind, und es wird ein neuer String 
reingeschrieben, der aber nicht mehr reinpasst, wird dann während dessen 
der alte Frame weiter übertragen? Ich nehm aber mal an, daran hast du 
auch gedacht.

wegen volatile:
Phillip Hommel schrieb:
> void put_string_to_ringbuffer(volatile struct t_buffer* buffer, uint8_t* 
p_start)
hier sollte doch das volatile überflüssig sein. die adresse des buffers 
ändert sich ja nicht plötzlich, besser gesagt nie.

Phillip Hommel schrieb:
> uint8_t buffer = get_from_ringbuffer(&tx_buffer);

bist du dir sicher mit dem "&" ? tx_buffer müsste doch schon ein pointer 
sein. Obwohl, der zeigt ja aufs erste Element oder? Puh!
Bei der Definition von t_buffer würde bei mir wahrscheinlich ein typedef 
davorstehen. Aber bin auch schon ganz schön am schwimmen. Soweit ists 
mit meinen C-Kenntnissen nicht her und ich weiß schon, warum ich lieber 
in Assembler programmier :-P

Aber ehrlich gesagt seh ich genau 1 Stelle, wo du auf 
tx_buffer/rx_buffer als globale Variable zugreifst. Das ist in der ISR

> uint8_t buffer = get_from_ringbuffer(&tx_buffer);

Hier sollte der Compiler ja nichts zum weg-optimieren haben. Ansonsten 
übergibst du die Puffer-Adresse ja immer als Funktionsargument und 
greifst dann auf den übergebenen Puffer zu.

Tja, ein munteres herum-gestocher ist das hier :(

Vllt. kannst du dein Programm einfach mal im Simulator laufen lassen. 
Musst halt nach Programmstart den Puffer per Hand befüllen und die 
entsprechenden Pointer eintragen. Geht das auch in C?

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Die Elemente des Structs müssen auch volatile sein, zumindest gab es 
hier mal einen Thread wo das ein Problem war...

von Phillip H. (philharmony)


Lesenswert?

>Wenn noch Daten im Puffer sind, und es wird ein neuer String
>reingeschrieben, der aber nicht mehr reinpasst, wird dann während dessen
>der alte Frame weiter übertragen? Ich nehm aber mal an, daran hast du
>auch gedacht

Klar, das ist ja die Idee bei der Sache. Darum soll das reinlegen eines 
neuen (nicht mehr reinpassenden) Strings(bzw des nächsten Zeichens) so 
lange warten, bis per ISR wieder Platz gemacht wurde.

>hier sollte doch das volatile überflüssig sein. die adresse des buffers
>ändert sich ja nicht plötzlich, besser gesagt nie.

Da bin ich mir eben nicht mehr so ganz sicher. Denn der Pointer zeigt ja 
auf immer neue Stellen des Arrays. Und wenn die UDRE ISR ein Zeichen 
gesendet hat, dann ändert sie ja den Pointer auf das nächste Zeichen. 
Allerdings wird ja auch das Zeichen selbst von der UDR geändert 
(zumindest in der Empfangsrichtung, dann vom RXC ISR). Und wie man das 
dann deklariert bin ich mir eben unsicher.

>Die Elemente des Structs müssen auch volatile sein, zumindest gab es
>hier mal einen Thread wo das ein Problem war...

Ich werde es heute wohl nicht mehr probieren können, werde aber bald 
möglichste berichten.
Hab das jetzt mal so gemacht, daß ich in der Strukturdefinition alle 
Pointer Volatile deklariert habe, dann sollten die schnmal nicht mehr 
der Opt. zum Opfer fallen...
1
/*Typzuweisung*/
2
struct  t_buffer{          
3
  
4
  uint8_t size;
5
  volatile uint8_t* p_buffer_start;
6
  volatile uint8_t* volatile p_next_read;
7
  volatile uint8_t* p_end_read;
8
  volatile uint8_t* volatile p_next_write;
9
  volatile uint8_t* p_end_write;
10
};
11
12
/*Empfangs- und Sendepuffer reservieren*/
13
uint8_t tx_buffer_data[TX_BUFFERSIZE];
14
uint8_t rx_buffer_data[RX_BUFFERSIZE];
15
16
struct t_buffer tx_buffer = {(TX_BUFFERSIZE - 1), tx_buffer_data,  tx_buffer_data, tx_buffer_data, tx_buffer_data, tx_buffer_data};
17
struct t_buffer rx_buffer = {(RX_BUFFERSIZE - 1), rx_buffer_data,  rx_buffer_data, rx_buffer_data, rx_buffer_data, rx_buffer_data};

Oder müssen die eigentlichen Pufferspeicher an sich auch Volatile sein 
(volatile uint8_t tx_buffer_data[TX_BUFFERSIZE];)?

von Klaus (Gast)


Lesenswert?

Hast du das Programm mal ohne Optimierung übersetzt? Damit sollten ja 
Fehler, die von fehlenden volatiles herrühren, erst mal nicht 
zuschlagen. Wenns also ohne Optimierung nicht läuft, ist woanders noch 
was falsch.

von Phillip H. (philharmony)


Lesenswert?

>Hast du das Programm mal ohne Optimierung übersetzt? Damit sollten ja
>Fehler, die von fehlenden volatiles herrühren, erst mal nicht
>zuschlagen. Wenns also ohne Optimierung nicht läuft, ist woanders noch
>was falsch.

Daran habe ich noch gar nicht gedacht, ist das soweit zu empfehlen? Hab 
ich noch nie versucht, danke für den Tipp!

von Klaus (Gast)


Lesenswert?

Es ist zu empfehlen, um zu prüfen, ob die volatiles der Fehler sind. Das 
Abschalten der Optimierung ist natürlich nicht als alternative zu 
korrekten volatiles gedacht ;)

von Phillip H. (philharmony)


Lesenswert?

Ok super, ich werde das alles baldmöglichst mal ausprobieren und 
berichten. Vielen Danke erstmal an alle für Eure Hilfe!

von Phillip H. (philharmony)


Lesenswert?

SO, habs getestet und: Es funktioniert! Lag tatsächlich am vergessenen 
Volatile. Was bei genauerem Hinsehen ja auch total Sinn macht: Das 
Abbruchkriterium der while-Schleife kann sich ja aus Sicht des Compilers 
nicht ändern (da er ja nichts von Interrupts etc weiß sondern nud 
straight-forward "denkt") während diese ausgeführt wird, darum wird 
diese Abfrage Laufzeitoptimiert, aus
1
while(funktion(a,b) == 0)  //funktion wird nach jedem Schleifendurchlauf ausgeführt
2
{
3
}

wird
1
int c = funktion(a,b)  //funktion wird einmal vor der Schleife ausgeführt
2
3
while(c == 0)  //Leider ändert sich c nie wieder und schon hängt das ganze
4
{
5
}
Und schon knallts. Da sich aber die Funktion bei mir durch das interrupt 
eben doch in der Zeit ändern kann, müssen eben die entsprechende 
Variablen als volatile deklariert werden, die dem Compiler diese 
Optimierung dann verbieten.
Soviel der Erklärung mal für alle, die mit dem selben Problem über 
diesen Beitrag stolpern.
Nochmals vielen Dank an alle!

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.