Forum: Mikrocontroller und Digitale Elektronik C-Unverständnis - STM32F4 Ethernet


von Reginald L. (Firma: HEGRO GmbH) (reggie)


Angehängte Dateien:

Lesenswert?

Abend!

Wie ist das auf dem Bild gezeigte möglich? Steh ich aufm Schlauch?
Wie kann 0x36 AND 0x1FFF = 0x30AD0801 ergeben?

Grüße
Reggie

von hp-freund (Gast)


Lesenswert?

Hast Du die aktuelle Zeile überhaupt schon ausgeführt?

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Das sieht man auf dem Bild nicht, aber ich steh auf dem Breakpoint.

von hp-freund (Gast)


Lesenswert?

Dann mach einen Einzelschritt...

von Nop (Gast)


Lesenswert?

Also erstmal geht es dabei nicht um C, sondern um C++, wie man an Zeile 
05 sieht.

Zweitens wäre es möglich, daß DMATxDescToSet in den Wald zeigt. 
Vielleicht kein Speicher dafür alloziert, oder use after free. Oder kein 
Heap definiert (falls man für C++ an der Stelle mit dynamischem 
Speichermanagement arbeitet, was man nicht tun sollte). Oder der Stack 
ist in den Heap reingelaufen, bzw. in die globalen Variablen.

Drittens, DMA geht nicht im CCM. Nicht daß der Zeiger da aufs CCM zeigt.

Viertens, wenn es dabei um DMA geht, eventuell verändert die DMA-Routine 
ja den Wert hinterrücks und irrtümlich?

von Oliver J. (skriptkiddy)


Lesenswert?

Step doch mal über die Zeile drüber. Debugger führen erfahrungsgemäß die 
Instruktion, die am Breakpoint steht, beim Anhalten nicht aus.

Grüße Oliver

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Oliver J. schrieb:
> Step doch mal über die Zeile drüber. Debugger führen erfahrungsgemäß die
> Instruktion, die am Breakpoint steht, nicht aus.
Wenn er da am Breakpoint steht, ist die if-Anweisung schon durchgeführt. 
Der Wert steht definitiv.

Nop schrieb:
> Also erstmal geht es dabei nicht um C, sondern um C++, wie man an Zeile
> 05 sieht.
gnagnagna

Nop schrieb:
> Zweitens wäre es möglich, daß DMATxDescToSet in den Wald zeigt.
Definitiv nicht, das Verhalten zeigt sich erst nach einiger Zeit. 
Bisschen komplex zu erklären, da is noch anderes Zeug im Spiel, welches 
allerdings mit dem Pointer nix zu tun hat. Das geschieht übrigens in 
einer ISR mit höchster Prio. Das was ist, kann eigentlich nicht sein.

Nop schrieb:
> Vielleicht kein Speicher dafür alloziert, oder use after free. Oder kein
> Heap definiert (falls man für C++ an der Stelle mit dynamischem
> Speichermanagement arbeitet, was man nicht tun sollte).
In meinem Code ist kein Furz dynamisch, wenn ich das so mal ausdrücken 
darf. Auf den Glaubenskrieg lasse ich mich aber nicht ein.

Nop schrieb:
> Oder der Stack
> ist in den Heap reingelaufen, bzw. in die globalen Variablen.
Genau an sowas habe ich irgendwie gedacht, aber wie gesagt, mein Code is 
sowas von statisch :>

Nop schrieb:
> Drittens, DMA geht nicht im CCM. Nicht daß der Zeiger da aufs CCM zeigt.
Kein CCM.

Nop schrieb:
> Viertens, wenn es dabei um DMA geht, eventuell verändert die DMA-Routine
> ja den Wert hinterrücks und irrtümlich?
Ja, der Ethernet-DMA, der läuft so ziemlich automatisch über die 
Descriptors gesteuert. Daran habe ich noch nicht gedacht. Schau ich mir 
gleich mal an ob ich hier Bockmist gebaut habe.

von Nop (Gast)


Lesenswert?

Reginald L. schrieb:

> gnagnagna

Naja C++ ist doch schon anders als C.

> Definitiv nicht, das Verhalten zeigt sich erst nach einiger Zeit.

Auch dann kann der Zeiger in den Wald zeigen. Beispielsweise, weil er 
unerwartet von irgendwoher überschrieben wurde. Ich hatte mal einen 
interessanten Fall, wo ein Buffer Overrun die nachfolgende Variable 
überschrieben hatte, jedenfalls manchmal. Bei seltenen Eingabedaten. Der 
Absturz kam dann erst bei Verwendung dieser nachfolgenden Variable, 
welche ein Zeiger war und somit sonstwohin zeigte. Häufigkeit, so etwa 
in 5% der Programmläufe.

> In meinem Code ist kein Furz dynamisch, wenn ich das so mal ausdrücken
> darf. Auf den Glaubenskrieg lasse ich mich aber nicht ein.

Ich wollte darauf hinaus, daß in C++ Dinge manchmal implizit dynamisch 
passieren können. Ich gehe dann mal davon aus, Du kennst C++ genug, um 
dynamisches implizites Verhalten zu vermeiden. (:

> Genau an sowas habe ich irgendwie gedacht, aber wie gesagt, mein Code is
> sowas von statisch :>

Stack Overflow kann auch in statischem Code passieren. Dann hast Du 
keinen Heap, der übergemarmelt wird, sondern dann sind gleich die 
globalen Variablen dran. Jedenfalls, wenn Du den Stack ganz nach oben 
ins RAM gelinkt hast. Ich linke ihn deswegen nach unten, damit ich nen 
sauberen hard fault kriege, wenn ich einen Stack Overflow habe.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Nop schrieb:
> Auch dann kann der Zeiger in den Wald zeigen. Beispielsweise, weil er
> unerwartet von irgendwoher überschrieben wurde. Ich hatte mal einen
> interessanten Fall, wo ein Buffer Overrun die nachfolgende Variable
> überschrieben hatte, jedenfalls manchmal. Bei seltenen Eingabedaten. Der
> Absturz kam dann erst bei Verwendung dieser nachfolgenden Variable,
> welche ein Zeiger war und somit sonstwohin zeigte. Häufigkeit, so etwa
> in 5% der Programmläufe.
Das geht wohl genau in die selbe Richtung. Ich logge mich ins Device 
ein, es tut was es soll. Dann lasse ich einen bestimmten Buffer mit ein 
paar bytes über SPI füllen. Es tut immer noch alles. Sobald ich mich ins 
Device aber wieder relogge tritt das o.g. Problem auf.

Nop schrieb:
> Ich wollte darauf hinaus, daß in C++ Dinge manchmal implizit dynamisch
> passieren können. Ich gehe dann mal davon aus, Du kennst C++ genug, um
> dynamisches implizites Verhalten zu vermeiden. (:
Das hoffe ich :)

Nop schrieb:
> Stack Overflow kann auch in statischem Code passieren. Dann hast Du
> keinen Heap, der übergemarmelt wird, sondern dann sind gleich die
> globalen Variablen dran. Jedenfalls, wenn Du den Stack ganz nach oben
> ins RAM gelinkt hast. Ich linke ihn deswegen nach unten, damit ich nen
> sauberen hard fault kriege, wenn ich einen Stack Overflow habe.
Kann aber doch eigentlich nur passieren, wenn man doof pointed oder?

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Ich glaub ich habs. Ich Vollhonk, habe mal wieder was übersehen.
Dummheit muss einfach bestraft werden.

Mein ehem. Meister hat immer gesagt: Das ist keine Strafe, das ist Übung 
:)

von Hugo (Gast)


Lesenswert?

Reginald L. schrieb:
> Ich glaub ich habs. Ich Vollhonk, habe mal wieder was übersehen.
> Dummheit muss einfach bestraft werden.

Läßt du uns an deinen Erkenntnissen teilhaben? Was war's?

von Rene H. (Gast)


Lesenswert?

Was war es denn?

von Nop (Gast)


Lesenswert?

Reginald L. schrieb:
> Kann aber doch eigentlich nur passieren, wenn man doof pointed oder?

Nee, das passiert, wenn man den Stack zu klein macht. Dessen Größe muß 
man ja explizit irgendwo reservieren. Zuviele Aufruf-Verschachtelungen 
mit jeweils zuviel lokalen Variablen, und es rumst. Besonders, wenn man 
bei der Stackberechnung die Library-Funktionen oder die Interrupts 
vergißt.

Oder wenn man Rekursion verwendet und bei den Abbruchbedingungen keine 
korrekte Maximaltiefe vorgesehen hat.

Aber woran lag es nun? Der Gärtner war's, richtig?

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Naja, ich verliere so langsam den Überblick:
Habe eine Software am PC mit C# programmiert die mit einem STM32F429 
kommuniziert. An dem hängt ein Frequenzumrichter und ein STM32F407. Das 
ist inzwischen eine Unmenge an Code.
Vor kurzem habe ich alle Codes überarbeitet um die Übersichtlichkeit zu 
verbessern. Hier habe ich wahrscheinlich einen Fehler an der PC-Software 
gemacht: Die Kommunikation des FU läuft über RS232, welches auf dem 
STM32F429 auf Ethernet umgesetzt wird. Diese Frames schleichen sich wohl 
zwischen die Frames des STM32F429 und machen die Descriptors "kaputt". 
Heute schau ich mir das nicht mehr genauer an, aber mein alter 
Versionsstand funktioniert ja, weshalb das jetzt das naheliegenste wäre. 
Ich hoffe es zumindest.

EDIT: Wenn ich mir das so durchlese, sollte ich nicht nur die 
Übersichtlichkeit, sondern auch so manche Programmiertaktiken verändern. 
Ist eigentlich Schwachsinn, dass durch fehlerhafte PC-Software der µC in 
einen Hardfault laufen kann.

von Nop (Gast)


Lesenswert?

Reginald L. schrieb:
> EDIT: Wenn ich mir das so durchlese, sollte ich nicht nur die
> Übersichtlichkeit, sondern auch so manche Programmiertaktiken verändern.

Jepp.. ein übliches Paradigma dabei ist "die Welt ist schlecht". Alles, 
was von draußen reinkommt, kann und wird böswillig sein. Dein kleiner 
Controller muß dann mit einer Welt klarkommen, die seine pure Existenz 
schon mißbilligt.

Designreviews sind an der Stelle eine Methode, wo man sich das System 
anschaut mit der Fragestellung "was muß ich tun, damit der Ansatz auf 
die Nase fällt?". Und dann iterativ das Design verbessern, bis die 
Antwort lautet "außer einem großen Hammer gibt es da nichts".

Während die von außen mit allen möglichen gemeinen Tricks dem Controller 
übel mitspielen, muß er sich trotzdem bei seinen Ausgaben genauestens an 
die Regeln halten.

Systeme, die so entwickelt wurden, werden im industriellen Einsatz gerne 
auch deutlich teurer bezahlt als Schönwettersoftware, weil jeder Ausfall 
da richtig Geld kostet. Sprich, das ist ein Weg, wie man gutes Geld 
verdienen kann.

Aber hey, Du bist nicht der Erstem bei dem Refactoring zum Refucktoring 
wird. (;

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Nop schrieb:
> Dein kleiner
> Controller muß dann mit einer Welt klarkommen, die seine pure Existenz
> schon mißbilligt.
Sehr schön forumliert :)

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Nop schrieb:
> Aber hey, Du bist nicht der Erstem bei dem Refactoring zum Refucktoring
> wird. (;
Das is aber auch ein nettes Wortspiel:)

Naja, ich mach mich jetz mal ran.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Vielleicht hat noch jemand eine Denkhilfe für mich:

Die Ethernetframes werden vom PC angestoßen. Der Controller läuft nach 
dem Empfang der Frames in eine Ethernet-Rx-ISR, in der ebendiese 
dekodiert werden. Nun gibt es zwei Möglichkeiten.
Die Frame ist für den Controller bestimmt: In gleicher ISR erstellt der 
Controller eine Antwort und startet die DMA-Übertragung. ISR schließt.
Die Frame ist für den Frequenzumrichter: In gleicher ISR werden die 
benötigten Bytes aus dem Frame in einen neuen Buffer kopiert und der 
UART gestartet (kein UART DMA, lediglich UART-TxEmpty-ISR). ISR Ende.

Nun ist es so, dass der PC eine Antwort vom Frequenzumrichter erwartet. 
Aufgrund der langen Antwortverzugszeit und RS-232 mit ~56kbit/s wäre es 
natürlich Schwachsinn, in der Ethernet-Rx-ISR auf die Antwort zu warten. 
Bisher habe ich das so gelöst:
UART empfängt erst die komplette Nachricht des FUs über eine 
UART-RxNotEmpty-ISR. Sobald das letzte Byte angekommen ist, erstellt die 
gleiche ISR den kompletten Header für die Ethernet-Übertragung, kopiert 
die empfangenen Bytes vom FU in einen neuen Buffer und startet die 
Ethernet-DMA-Übertragung.

Eigentlich wartet der PC nach jeder neu abgesendeten Nachricht auf eine 
Antwort bevor er eine neue versendet. Deshalb wundert es mich etwas, 
dass hier ein Fehler auftritt, zumal es in der alten Version ohne 
Probleme funktioniert hat.

Hättet ihr einen Tipp, wie ich das Problem prinzipiell lösen könnte? Ich 
hätte an einen flag gedacht, den ich setze, aber dann muss man 
sicherstellen, dass das Frame zu einem späteren Zeitpunkt noch versendet 
werden muss. Vielleicht habt ihr eine ganz andere Lösung?


Danke schonmal
Grüße
Reggie

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Vielleicht so zum Verständnis, wie es bisher läuft:
1
void ETH_RX_ISR()
2
{
3
    if (FrameForController)
4
    {
5
        CreateEthTx();
6
        StartEthTxDMA();
7
    }   
8
    else
9
        SendToConverter();  
10
}
11
12
void UART_RX_ISR()
13
{
14
    ...
15
    ...
16
    if (LastByte)
17
    {
18
        CreateEthTx();
19
        StartEthTxDMA();
20
    }
21
}


In der StartEthTxDMA() wird der aktuelle Descriptor bearbeitet und dann 
auf den nächsten Descriptor gezeigt. Ich versteh zwar nicht wirklich, 
warum sich das anfangs erwähnte Verhalten zeigt, aber um das Problem mal 
anzugehen, möchte ich eben erst mal diesen Fehler ausschließen.

von Gerd E. (robberknight)


Lesenswert?

Mir fällt da als erstes auf daß Du relativ viel in den ISRs machst.

Ich versuche das immer so weit wie möglich zu vermeiden. In meinen ISRs 
setzte ich meist entweder nur ein Flag oder sichere Daten und setze dann 
ein Flag. Der Rest wird im normalen Hauptprogramm gemacht.

Grund ist daß Du alle Variablen und Ressourcen, die Du in einer ISR 
verwendest, besonders schützen musst bevor Du sie in einem anderen 
Programmteil verwendest. Desto aufwendiger die ISR, desto schwieriger 
wird es und desto leichter übersieht man da etwas.

Natürlich gibt es Außnahmen davon, z.B. wenn es aus Timinig-Gründen 
nicht anders geht. Aber STM32F429 und Serielle Kommunikation klingt 
jetzt nicht so, als ob Du da bereits am Takte zählen bist.

Dein "CreateEthTx(); StartEthTxDMA();" in der ISR sehen mir da 
gefährlich aus. Was passiert, wenn ein anderer Programmteil auch ein 
Ethernet-Paket senden möchte?

Der zweiter Punkt der mir auffällt, ist daß der Programmfluss relativ 
stark von den ISRs gesteuert werden zu scheint. Also von außen. Dort, wo 
lt. Nop schon die Existenz Deines µC mißbilligt wird:

Was passiert, wenn einfach so, unvermittelt, vom FU Daten kommen die 
Deine "LastByte"-Bedingung erfüllen?

Was passiert, wenn vom PC zwei Frames mit Daten für den FU kurz 
hintereinander kommen, ohne daß der FU Zeit hatte zu antworten?

Was passiert, wenn der FU nicht innerhalb realistischer Zeit antwortet?

etc.

Ich baue für sowas gerne eine Statemachine. Also die Daten werden in den 
ISRs gesichert und ein Flag gesetzt. Das Hauptprogramm bemerkt das Flag 
und bearbeitet diese Eingabe dann nach den Regeln der Statemachine. Also 
ist dieses Flag an der Stelle gültig, in welchen State wechsle ich jetzt 
und was wird dann gemacht etc.

Zwischen den States gibt es dann an vielen Stellen Timeouts. Wenn eine 
Antwort nicht nach soundsoviel Sekunden kam, dann protokolliere einen 
Timeout-Fehler und gehe in einen Failsafe-Status, Resette die Anlage 
etc.

Wenn Du den Programmfluss vom Hauptprogramm aus steuerst und nicht aus 
ISRs raus, hast Du es auch viel leichter den Watchdog des µC zu 
verwenden. Denn Du kannst den bei jedem vollständigem Durchlauf der 
Hauptschleife (und meinetwegen noch ein paar zusätzlichen Checks) 
resetten.

Ein weiterer Vorteil der Statemachine ist das Debugging. Du kannst Dir 
meist im normalen Programmlauf, ohne Debugger, die Statewechsel auf 
einem Terminal protokollieren lassen. Wenn ein Bug sehr selten oder nur 
ohne Debugger auftritt, hast Du dann zumindest schon mal einen guten 
Indikator was da passiert ist.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Gerd E. schrieb:
> Mir fällt da als erstes auf daß Du relativ viel in den ISRs machst.
Ja, darauf habe ich damals meinen Code ausgelegt. Ich habe gedacht, dass 
ich so möglichst definierte Timings und Zustände des Programms erreiche. 
Die MainLoop ist bei mir eigentlich leer, darin hängen nur 
GUI-Aktualisierungen für einen TFT.
Wenn ich dich richtig verstehe, ist dieses Vorgehen unüblich?

Gerd E. schrieb:
> Grund ist daß Du alle Variablen und Ressourcen, die Du in einer ISR
> verwendest, besonders schützen musst bevor Du sie in einem anderen
> Programmteil verwendest.
Was meinst du mit schützen? Ich setze jede Variable in den ISRs auf 
volatile. Andererseits habe ich keine Compileroptimierungen aktiviert. 
Noch nicht.

Gerd E. schrieb:
> Natürlich gibt es Außnahmen davon, z.B. wenn es aus Timinig-Gründen
> nicht anders geht. Aber STM32F429 und Serielle Kommunikation klingt
> jetzt nicht so, als ob Du da bereits am Takte zählen bist.
Dazu zählt beispielsweise die SPI Kommunikation zwischen den beiden 
Controllern. Bei dem Rest hast du sicherlich absolut recht. Ein Grund 
bspws. für die Ethernet-ISR war für mich, dass die Frames möglichst 
schnell losgeschickt werden. Selbstverständlich habe ich auf eine 
korrekte Prioritätenvergabe der Interrupts geachtet. Da hat man beim F4 
echt gute Möglichkeiten.

Gerd E. schrieb:
> Dein "CreateEthTx(); StartEthTxDMA();" in der ISR sehen mir da
> gefährlich aus. Was passiert, wenn ein anderer Programmteil auch ein
> Ethernet-Paket senden möchte?
Das dürfte eigentlich nicht vorkommen. Der FU antwortet nur einmalig. 
Wenn er nicht antwortet wird auch kein Ethernet-Frame versendet. Das 
wird alles am PC gesteuert, weshalb ich jetzt wohl die Probleme her 
habe.

Gerd E. schrieb:
> Was passiert, wenn einfach so, unvermittelt, vom FU Daten kommen die
> Deine "LastByte"-Bedingung erfüllen?
Das kann, wie gesagt, nicht passieren. Sowas macht der FU nicht. Die 
LastByte()-Bedingung kann nur vorkommen, wenn der UART Daten empfängt.

Gerd E. schrieb:
> Was passiert, wenn vom PC zwei Frames mit Daten für den FU kurz
> hintereinander kommen, ohne daß der FU Zeit hatte zu antworten?
Kann eigentlich auch nicht passieren, Die PC-Software ist so 
geschrieben, dass das unmöglich ist / sein sollte.

Gerd E. schrieb:
> Was passiert, wenn der FU nicht innerhalb realistischer Zeit antwortet?
Der Controller kriegt das nicht mit. Die PC-Software hat dafür einen 
Timeout und resendet die EthernetFrame. Das passiert auch relativ oft, 
das ist bei dem FU auch normal. Ist alles auch gegengeprüft, den FU 
kenne ich inzwischen wie meine Westentasche :)

Gerd E. schrieb:
> Ich baue für sowas gerne eine Statemachine. Also die Daten werden in den
> ISRs gesichert und ein Flag gesetzt.
Das werde ich jetzt wohl auch machen, mein Vorgehen mit den ISRs scheint 
also wohl total fehl am Platz gewesen zu sein?

von Nop (Gast)


Lesenswert?

Also grundsätzlich wird da für meinen Geschmack zuviel in Interrupts 
gemacht, was eigentlich in die Applikation gehören würde. Vor allem wird 
der DMA-Transfer aus zwei verschiedenen ISRs angestoßen, ohne daß eine 
Zentralstelle verwalten würde, ob der DMA-Transfer schon zuende ist.

Spurious Interrupts wären da auch noch anzudenken. Bei DMA fiel mir noch 
dieser Beitrag hier auf, wo es zwar um den ADC ging, aber evtl. ist das 
mit LISR und LISH hier ja auch relevant: 
Beitrag "STM32, triple mode Interleaved mode, periodisch falsch"

Wenn Du kein RTOS verwendest, sondern das "bare metal" gehen soll, wäre 
es ein Weg, in der main die Endlosschleife zu nehmen und periodisch das 
RX/TX zu koordinieren. Sofern dann auch noch eine "eigentliche" 
Applikation läuft, was ja auch der Grund sein könnte, wieso soviel in 
den ISRs gemacht wird, wird es ohne RTOS natürlich hakelig.

Aber nicht unmöglich; man kann auch in der Mainloop die einzelnen 
Taskfunktionen durchtickern lassen, die dann eben nicht blockieren 
dürfen. Das bekommt man entweder mit expliziten Zustandsautomaten in den 
Funktionen hin, oder man nimmt computed gotos, was besonders für 
Wartefunktionen sehr bequem geht.

Das würde übrigens auch die Aufteilung der Prios für DMA und 
Ethernet/UART ISR vereinfachen.

Ach ja, und der PC kann auch durchaus ungewollt Ethernet-Frames senden, 
die mit der Applikation nichts zu tun haben. Speziell Windows ist 
berüchtigt dafür, auf allen Ethernet-Interfaces periodisch irgendwelche 
ARP-Pakete rauszublasen, um zu gucken, wer da so alles ist. Du mußt also 
jederzeit damit rechnen, daß dort Pakete ankommen, die Du eh wieder 
verwerfen mußt.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Nop schrieb:
> Vor allem wird der DMA-Transfer aus zwei verschiedenen ISRs angestoßen,
> ohne daß eine Zentralstelle verwalten würde, ob der DMA-Transfer schon
> zuende ist.
Das macht eigentlich nichts, da in Wirklichkeit dort nur die descriptors 
beschrieben werden. Der DMA schnappt sich die Daten dann selbstständig. 
Es geht hier um den Ethernet MacDMA der mit dem STM DMA nichts zu tun 
hat.

Nop schrieb:
> Ach ja, und der PC kann auch durchaus ungewollt Ethernet-Frames senden,
> die mit der Applikation nichts zu tun haben. Speziell Windows ist
> berüchtigt dafür, auf allen Ethernet-Interfaces periodisch irgendwelche
> ARP-Pakete rauszublasen, um zu gucken, wer da so alles ist. Du mußt also
> jederzeit damit rechnen, daß dort Pakete ankommen, die Du eh wieder
> verwerfen mußt.
Ist alles bedacht, implementiert und mit wireshark  überprüft. Hat ja 
bisher auch funktioniert.

Aber ich versuche jetzt mal den Anlauf mit der Mainloop. Wie gesagt, ich 
dachte mit den ISRs wird das ganze einen absolut definierten Zustand 
einnehmen. War wohl zu kurz gedacht.

von Gerd E. (robberknight)


Lesenswert?

Reginald L. schrieb im Beitrag #4674445
> Die MainLoop ist bei mir eigentlich leer, darin hängen nur
> GUI-Aktualisierungen für einen TFT.
> Wenn ich dich richtig verstehe, ist dieses Vorgehen unüblich?

Man kann das schon so machen, aber Du holst Dir halt einige Risiken in 
Dein Design rein.

> Gerd E. schrieb:
>> Grund ist daß Du alle Variablen und Ressourcen, die Du in einer ISR
>> verwendest, besonders schützen musst bevor Du sie in einem anderen
>> Programmteil verwendest.
> Was meinst du mit schützen? Ich setze jede Variable in den ISRs auf
> volatile.

Das ist das eine, reicht aber nicht. Stell Dir z.B. folgenden Code vor
1
volatile int a;
2
3
if (a>10)
4
{
5
    int copy=a;
6
    a=0;
7
8
    do_something(copy);
9
}

Zwischen dem a>10 und dem then-Block könnte die ISR a auf kleiner 10 
gesetzt haben. Auch zwischen dem Kopieren und auf 0 setzten könnte die 
ISR gelaufen sein und noch einen Wert in a reingeschrieben haben, der 
dann verloren geht.

Du müsstest also eigentlich für diesen Teil des Codes die ISR sperren.

> Andererseits habe ich keine Compileroptimierungen aktiviert.

obiges Problem hat nix mit Optimierungen zu tun. Einfach damit, daß die 
ISR an jeder Stelle im Code "zuschlagen" kann.

> Gerd E. schrieb:
>> Dein "CreateEthTx(); StartEthTxDMA();" in der ISR sehen mir da
>> gefährlich aus. Was passiert, wenn ein anderer Programmteil auch ein
>> Ethernet-Paket senden möchte?
> Das dürfte eigentlich nicht vorkommen. Der FU antwortet nur einmalig.

Der FU vielleicht. Aber wenn eine Störung auf das Kabel einkoppelt? Oder 
jemand am Stecker wackelt?

> Gerd E. schrieb:
>> Was passiert, wenn einfach so, unvermittelt, vom FU Daten kommen die
>> Deine "LastByte"-Bedingung erfüllen?
> Das kann, wie gesagt, nicht passieren. Sowas macht der FU nicht. Die
> LastByte()-Bedingung kann nur vorkommen, wenn der UART Daten empfängt.

siehe oben.

> Gerd E. schrieb:
>> Was passiert, wenn vom PC zwei Frames mit Daten für den FU kurz
>> hintereinander kommen, ohne daß der FU Zeit hatte zu antworten?
> Kann eigentlich auch nicht passieren, Die PC-Software ist so
> geschrieben, dass das unmöglich ist / sein sollte.

Die Software macht das vielleicht nicht. Aber Dein OS (siehe Post von 
Nop). Oder Deine Software läuft aus Versehen zwei mal. Oder oder oder.

> Gerd E. schrieb:
>> Was passiert, wenn der FU nicht innerhalb realistischer Zeit antwortet?
> Der Controller kriegt das nicht mit. Die PC-Software hat dafür einen
> Timeout und resendet die EthernetFrame.

Und wenn in dem Moment dann der FU doch noch antwortet?

von Gerd E. (robberknight)


Lesenswert?

Nop schrieb:
> Wenn Du kein RTOS verwendest, sondern das "bare metal" gehen soll, wäre
> es ein Weg, in der main die Endlosschleife zu nehmen und periodisch das
> RX/TX zu koordinieren. Sofern dann auch noch eine "eigentliche"
> Applikation läuft, was ja auch der Grund sein könnte, wieso soviel in
> den ISRs gemacht wird, wird es ohne RTOS natürlich hakelig.
>
> Aber nicht unmöglich; man kann auch in der Mainloop die einzelnen
> Taskfunktionen durchtickern lassen, die dann eben nicht blockieren
> dürfen. Das bekommt man entweder mit expliziten Zustandsautomaten in den
> Funktionen hin, oder man nimmt computed gotos, was besonders für
> Wartefunktionen sehr bequem geht.

Ja, so sehe ich das auch.

Bei einfacheren Programmen kommt man mit einer Mainloop und 
Statemachines gut hin. Wenn es komplexer wird, wird es immer schwerer 
die Funktionen so zu schreiben, daß sie nicht blockieren. Irgendwann 
wird das unübersichtlich. Spätestens dann ist der Zeitpunkt für nen RTOS 
gekommen.

Da hast Du dann mehrere Threads die jeder für sich blockieren können. 
Dafür musst Du aber an allen Stellen, an denen die Threads untereinander 
kommunizieren oder gemeinsame Ressourcen verwenden, aufpassen. Nen RTOS 
stellt dafür normal verschiedene Funktionen bereit. Aber dennoch muss 
man wissen was man tut, man kann bei unbedarftem Einsatz von Locking 
auch schnell in ein Deadlock reinlaufen.

Ich nehme als RTOS gerne ChibiOS. Wenn Du Dich dafür interessierst, hier 
gibt es ein Tutorial dazu:
http://chibios.org/dokuwiki/doku.php?id=chibios:book:start
Dort wird nicht nur ChibiOS erklärt, sondern auch generell worauf man 
bei RTOSsen, Locking etc. achten sollte. Finde ich als Einführung gut 
gemacht.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Gerd E. schrieb:
> Zwischen dem a>10 und dem then-Block könnte die ISR a auf kleiner 10
> gesetzt haben. Auch zwischen dem Kopieren und auf 0 setzten könnte die
> ISR gelaufen sein und noch einen Wert in a reingeschrieben haben, der
> dann verloren geht.
>
> Du müsstest also eigentlich für diesen Teil des Codes die ISR sperren.
Puh, das wird ein Spaß in solchen Konstrukten im Nachhinein den Fehler 
zu finden.

Gerd E. schrieb:
> Der FU vielleicht. Aber wenn eine Störung auf das Kabel einkoppelt? Oder
> jemand am Stecker wackelt?
Ja klar, kann man nicht ausschließen. Aber letztendlich kann man solche 
Fehlerquellen nie ausschließen, egal wie man etwas programmiert. Es wird 
immer einen Fehler geben der dein System zum erliegen bringt.
Auf jeden Fall habe ich diesen Umstand nicht betrachtet, da hier in der 
von dir beschriebenen Hinsicht Fehler bisher nicht aufgetreten sind. Ich 
werde den Code trotzdem umschreiben und versuchen möglichst viele 
Fehlerquellen zu beachten. Ich sehe ja was sonst rauskommt.

Gerd E. schrieb:
> Und wenn in dem Moment dann der FU doch noch antwortet?
Dann kann ich eigentlich nur noch damit arbeiten, dass ich den UART 
immer wieder aktiviere und deaktiviere, je nachdem ob ich eine Antwort 
erwarte. Kann ich mir nicht vorstellen, dass das so gehandhabt wird?

Gerd E. schrieb:
> Spätestens dann ist der Zeitpunkt für nen RTOS
> gekommen.
Wie RealTime ist denn so ein RTOS? Ich bin mit dem STM schon ziemlich an 
der Grenze des Machbaren, vor kurzem habe ich mir überlegt Programmteile 
auf einen FPGA auszulagern. Im Prinzip schaufele ich Daten vom ADC aus 
einem µC zu nem Andern und dann zum PC. Nebenher läuft ein TFT, I2C, 2x 
SPI, UART, Ethernet, Flash, SDRam... wenn ich nichts vergessen habe. 
Kritisch ist da vor allem das SPI, da is das Timing schon ganz knapp an 
den Grenzen.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

HA, jetzt weiß ich auch wieder, warum die ISRs so praktisch waren: Somit 
konnte die GUI in der Mainloop unterbrochen werden. Jetzt müsste ich die 
GUI-Funktionen so anpassen, dass diese unterbrochen werden, sobald eine 
wichtigere Aktion zu handlen ist.

von Gerd E. (robberknight)


Lesenswert?

Reginald L. schrieb:
> Gerd E. schrieb:
>> Du müsstest also eigentlich für diesen Teil des Codes die ISR sperren.
> Puh, das wird ein Spaß in solchen Konstrukten im Nachhinein den Fehler
> zu finden.

Wenn Du nur eine Variable und ein Auge für diesen Fehlermechanismus 
hast, dann geht das noch. Aber bei längeren, verschachtelten Funktionen 
die Du gemischt aus der ISR und normalen Programmteilen aufrufst wird 
das wirklich lustig...

> Gerd E. schrieb:
>> Der FU vielleicht. Aber wenn eine Störung auf das Kabel einkoppelt? Oder
>> jemand am Stecker wackelt?
> Ja klar, kann man nicht ausschließen. Aber letztendlich kann man solche
> Fehlerquellen nie ausschließen, egal wie man etwas programmiert. Es wird
> immer einen Fehler geben der dein System zum erliegen bringt.

Der Trick ist daß das System entweder selbst die Störung beheben kann 
oder aber sich definiert in einen sicheren Zustand bringt bzw. sich 
abschaltet. Irgendwie undefiniert "zum erliegen" zu kommen geht gar 
nicht. Vor allem wenn Du es mit potentiell gefährlichen Maschinen zu tun 
hast.

> Auf jeden Fall habe ich diesen Umstand nicht betrachtet, da hier in der
> von dir beschriebenen Hinsicht Fehler bisher nicht aufgetreten sind.

Du solltest nicht nur im Code auf die Fehler reagieren, die Du bei Dir 
beobachtet hast, sondern im Vorfeld genau bedenken was für Fehler 
theoretisch auftreten können und für diese eine Behandlung definieren.

Stell Dir mal vor Du lieferst Dein Gerät so an einen Kunden aus und der 
hat dann nen FU mit z.B. anderem Firmwarestand oder sowas. Oder starken 
Störungen aus der Umgebung die ins Kabel einstrahlen. Wenn Du dann erst 
mit Fehleranalyse und Umdesignen Deines Programms anfängst, ist die 
Inbetriebnahme gründlich daneben gegangen.

> Gerd E. schrieb:
>> Und wenn in dem Moment dann der FU doch noch antwortet?
> Dann kann ich eigentlich nur noch damit arbeiten, dass ich den UART
> immer wieder aktiviere und deaktiviere, je nachdem ob ich eine Antwort
> erwarte. Kann ich mir nicht vorstellen, dass das so gehandhabt wird?

Damit würdest Du versuchen den Fehler zu ignorieren. Was passiert wenn 
Du jetzt die richtige Antwort ignorierst und nur die falschen Daten 
durchlässt?

Nein, Du solltest erkennen daß da ein Protokollfehler vorliegt und dann 
z.B. versuchen das Protokoll zu resetten und wieder in einen sauberen 
Zustand zu kommen. Bei einem Modem mit AT-Befehlssatz könnte man hierfür 
z.B. <1sec>+++<1sec>AT<CR>
senden. Wenn da dann auch nach 3 Versuchen kein "OK" zurückkommt, würde 
ich alles abschalten und in einen sauberen Fehlerzustand gehen. Bei 
Deiner Maschine also z.B. den E-STOP triggern.

Wichtig ist dabei daß Du ein sauber definiertes Protokoll mit defnierten 
Protokollebenen hast. Also sowohl FU <-> µC als auch µC <-> PC. Und dann 
Dir überlegst wer für welche Fehlererkennung und Behandlung zuständig 
ist.

Da gibt es keine allgemeingültige Vorlage für, das hängt von Deiner 
Anwendung ab.

> Gerd E. schrieb:
>> Spätestens dann ist der Zeitpunkt für nen RTOS
>> gekommen.
> Wie RealTime ist denn so ein RTOS?

siehe mein Link oben.

> Ich bin mit dem STM schon ziemlich an
> der Grenze des Machbaren, vor kurzem habe ich mir überlegt Programmteile
> auf einen FPGA auszulagern. Im Prinzip schaufele ich Daten vom ADC aus
> einem µC zu nem Andern und dann zum PC. Nebenher läuft ein TFT, I2C, 2x
> SPI, UART, Ethernet, Flash, SDRam... wenn ich nichts vergessen habe.
> Kritisch ist da vor allem das SPI, da is das Timing schon ganz knapp an
> den Grenzen.

Das klingt eher nach RTOS als nach großer Mainloop.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Gerd E. schrieb:
> Du solltest nicht nur im Code auf die Fehler reagieren, die Du bei Dir
> beobachtet hast, sondern im Vorfeld genau bedenken was für Fehler
> theoretisch auftreten können und für diese eine Behandlung definieren.
Bin absolut deiner Meinung, am PC kein Thema. Aber beim µC verlangsamt 
man die ganze Geschichte schon ziemlich, zumindest, wenn man 
zeitkritische Dinge macht. Da habe ich ein besonderes Augenmerk drauf 
gehabt.
Witzigerweise hat der Controller in meinem speziellen Fall immer noch 
die softwareseitige Nothalt des FUs bewirkt.

Gerd E. schrieb:
> Stell Dir mal vor Du lieferst Dein Gerät so an einen Kunden aus und der
> hat dann nen FU mit z.B. anderem Firmwarestand oder sowas. Oder starken
> Störungen aus der Umgebung die ins Kabel einstrahlen. Wenn Du dann erst
> mit Fehleranalyse und Umdesignen Deines Programms anfängst, ist die
> Inbetriebnahme gründlich daneben gegangen.
Ich bin Maschinenbauer und programmiere noch nicht so lange :) So hoch 
will ich erst mal nicht hinaus.

Gerd E. schrieb:
> amit würdest Du versuchen den Fehler zu ignorieren. Was passiert wenn
> Du jetzt die richtige Antwort ignorierst und nur die falschen Daten
> durchlässt?
>
> Nein, Du solltest erkennen daß da ein Protokollfehler vorliegt und dann
> z.B. versuchen das Protokoll zu resetten und wieder in einen sauberen
> Zustand zu kommen.
Nee, in meinem Fall handle ich nach Vorgaben. Nach USS-Protokoll soll 
man bei nicht-Beantwortung die Anfrage solange resenden bis die Antwort 
kommt. Klingt komisch ist aber in besagtem Protokoll-Manual nachzulesen.
Ich habe auch noch weitere Sensorik angeschlossen die Auswertbar ist.
Aber darum gehts hier ja jetzt auch nicht.
Ich wollte damit eigentlich nur sagen, dass auch das sicherste System 
offene Fehlerquellen hat.

Gerd E. schrieb:
> Wichtig ist dabei daß Du ein sauber definiertes Protokoll mit defnierten
> Protokollebenen hast. Also sowohl FU <-> µC als auch µC <-> PC. Und dann
> Dir überlegst wer für welche Fehlererkennung und Behandlung zuständig
> ist.
Ist eigentlich auch nicht das Thema, aber der µC ist zwischen PC und FU 
eigentlich nur ein Protokollwandler. Ansonsten habe ich natürlich ein 
eigenes Protokoll zwischen µC und PC.

Gerd E. schrieb:
> Das klingt eher nach RTOS als nach großer Mainloop.
Durch das RTOS kriegt man doch wieder overhead in die ganze Geschichte. 
Aber ich sehs mir mal an.

Hab schon Kopfschmerzen von der ganzen Umschreiberei, für heute is 
genug..

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Wow. Ich lasse jetzt das komplette Ethernet nur über die Mainloop laufen 
und alles funktioniert so wie es soll, besser als vorher. Ich muss wohl 
irgend einen Bockmist gebaut haben. Vorher bekam ich sporadisch etwa 
einmal pro Minute keine Antwort auf eine Frame. Ich hatte die 
physikalische Verbindung im Verdacht, da es dazu ja so 
"Frame-Loss-Statistics" gibt. Jetzt ist alles wunderbar und vor allem 
auch weniger komplex geworden -> somit kann der Code auch schneller 
abgearbeitet werden.

Und ich bekomme eine Antwort in wenigen hundert µs vom Controller.

Top, ich danke euch für den Tipp mit der Mainloop! Ich dachte das Timing 
wird dann problematisch, aber das funktioniert jetzt besser als vorher 
:) Der C# Code am PC kommt mit dem µC gar nicht mehr mit :)

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.