Hallo nochmal, pünktlich zum Wochenende(eig schon gestern) hat sich mir
noch ein für mich nicht mehr begreifbares Problem eröffnet. Eig möchte
ich nur Daten über den UART senden. Das funktioniert nur bedingt.
Hier mein ursprünglicher Code funktioniert nicht (interessanterweise
werden die ersten beiden Zeichen immer richtig übertragen):
Und jetzt, zu meiner völligen Verwirrung, nehme ich wieder meinen
ursprünglichen Code und sende einfach zwei mal hintereinander, kommt
einmal der richtige String und einmal der schon bekannte kaputte String
an. Auch hier werden die ersten beiden Zeichen immer richtig übertragen.
Hat hier jemand eine Erklärung bzw kann mir einer weiterhelfen. Ich
würde schon gerne char* und Interrupt nutzen. Aber ohne Delay. Ich
verwende den STM32G0. Interrupts sind zwar noch weitere eingeschaltet,
aber alle geringerwertig priorisiert.
Dein array ist nicht wirklich lang genug...
strlen() braucht die abschließende \0, die passt aber gar icht mehr ins
msg2[11].
Wenn du die Länge nicht angibst, nimmt der Compiler genug für den String
inkl. der \0.
HI,
schau dir die Gültigkeit der Variable an.
Bsp 1: msg2 liegt auf dem Stack und wird ungültig wenn du die Funktion
verlässt und wird wieder überschrieben. Der UART ist aber noch nicht
fertig mit senden.
Bsp 2: Der String liegt als Konstante irgendwo im Speicher und nur der
Zeiger darauf wurde übergeben. Der Uart kann also weiterhin auf diesen
String zugreifen und schicken.
In den anderen Beispielen wird die Gültigkeit der Variable verlängert
bis der String draussen ist.
DAniel
Natürlich fehlt auch die abschließende 0.
Klaus W. schrieb:> Dein array ist nicht wirklich lang genug...> strlen() braucht die abschließende \0, die passt aber gar icht mehr ins> msg2[11].> Wenn du die Länge nicht angibst, nimmt der Compiler genug für den String> inkl. der \0.
Da hast du Recht, dummerweise ist mir hier ein Fehler beim Abschreiben
passiert. Das X gehört nicht rein. Aber auch mit der richtigen Länge
funktioniert es nicht.
Daniel S. schrieb:> Bsp 1: msg2 liegt auf dem Stack und wird ungültig wenn du die Funktion> verlässt und wird wieder überschrieben. Der UART ist aber noch nicht> fertig mit senden.>> Bsp 2: Der String liegt als Konstante irgendwo im Speicher und nur der> Zeiger darauf wurde übergeben. Der Uart kann also weiterhin auf diesen> String zugreifen und schicken.>> In den anderen Beispielen wird die Gültigkeit der Variable verlängert> bis der String draussen ist.
Ahhhhhhhhh, Danke <3
Dann könnte ich die Gültigkeit mit static doch erhalten, oder?
Jana K. schrieb:> Dann könnte ich die Gültigkeit mit static doch erhalten, oder?
ja, aber natürlich darf die Funktion dann nur einmal laufen zu jedem
Zeitpunkt.
Klaus W. schrieb:> Dein array ist nicht wirklich lang genug...
Also auch wohl die meisten Warnungen aus. Das sollte immer der erste
Schritt sein, die (ignorierten) Warnungen mindestens einmal
durchzugehen, bevor man Vodoo versucht.
Jana schrieb:> Hier mein ursprünglicher Code funktioniert nicht (interessanterweise> werden die ersten beiden Zeichen immer richtig übertragen):
Irgedwie habe ich immer den Eindruck, daß HAL und Konsorten keine andere
Funktion haben, als deren Anwendern das Leben möglichst schwer zu
machen.
Bei mir würde das Ganze etwa so aussehen:
1
voidCOM_SendNFI(void)
2
{String_Out(toUART1,"NFI(X0001)\n");}
Aber ich benutze ja auch mein eigenes Zeugs und nicht sowas wie die HAL.
Mein Rat wäre, sich die HAL zu verkneifen und stattdessen sich seine
eigene Sammlung an Lowlevel-Treibern zu schreiben. OK, das macht am
blutigen Anfang etwas mehr Arbeit, aber dann zahlt es sich aus. Nebenbei
gesagt: ein kleines Beispiel-Projekt zu so etwas hatte ich vor Jahren
hier schonmal gepostet.
W.S.
W.S. schrieb:> Irgedwie habe ich immer den Eindruck, daß HAL und Konsorten keine andere> Funktion haben, als deren Anwendern das Leben möglichst schwer zu> machen.
Das ist leider wirklich so. Ich habe immer gerne mit der StdPerihpLib
auf STM32 gearbeitet. Das waren recht klare Funktionen und Definitionen.
Man wusste was da passiert.
Jede Funktionalität aus dem Datenblatt war mehr oder weniger 1:1 dort
abgebildet.
Bei HAL weiß man das nicht.
z.B. HAL_TIM_Base_Start() vs. HAL_TIM_Base_Start_IT().
Da wird der Timer gestartet. Mit und ohne Interrupt. Welcher Interrupt?
Tja. Erschließt sich nicht direkt. Muss man nachgucken. Es ist der
Update Interrupt. Für die HAL bedeutet das "Den Timer im Interrupt Modus
starten". Das ist eine völlig willkürliche Festlegung.
Es gibt im DB keine Funktion "Timer Start" oder "Timer Start mit
Interrupt". Diese Funktionalität erfindet die HAL. Und da fangen die
Probleme an. Die HAL abstrahiert mir deutlich zu weit. Bis hin zum
Ratespiel was da passiert.
Bei diesem Beispiel hier: HAL_UART_Transmit_IT
Da passiert jede Menge. Dieser Aufruf benötigt im Hintergrund das ganze
HAL Zeug zum Interrupt Handling. Dann kann die HAL Daten selbständig bei
jedem Interrupt senden. So läuft das Senden im Hintergrund. Die HAL
puffert die Daten, zählt, horcht auf den Interrupt, setzt irgendwelche
UART States. Das ist zu heftig.
Bei der StdPeriphLib haben solche Aufrufe ein Byte in den TDR Register
geschoben und das wars.
Leider gibt es die StdPeriphLib nicht mehr für viele neuere STM32. Also
bleibt nur HAL oder selber Registerbits setzen rein mit CMSIS.
Cyblord -. schrieb:> Die HAL abstrahiert mir deutlich zu weit. Bis hin zum> Ratespiel was da passiert.
Naja, ich verstehe, was du meinst. Allerdings ist das, was diese HAL
tut, aus meiner Sicht keine Abstraktion, sondern eher Herumgelaber. Bei
meinem Beispiel weiter oben wird abstrahiert: ob man nun toUART1 oder
toUSB oder toSTRING, toFILE usw. angibt, und ob dieser Aufruf nun auf
einem ARM oder einem 8051 oder sonstwo stattfindet, ist egal. Ich sehe
das so, daß sich die Ebene der Algorithmen in einer Firmware nicht mit
den Befindlichkeiten irgendwelcher Standard-Peripherie herumschlagen
muß. Da ist ein Kanal zur Zeichenausgabe so gut wie jeder andere.
Nebenbei zum vom TO geäußerten Mysterium: Wer das Manual lesen kann (und
tut), ist da im Vorteil: der in den meisten Peripheriecores dem
eigentlichen Ausgaberegister vorgeschaltete Speicher macht es möglich.
Das allererste Zeichen wird sofort in das Ausgaberegister durchgereicht,
wo es Bit für Bit herausgeschoben wird. Das zweite Zeichen landet in dem
vorgeschalteten Vorrats-Register, von dem aus es erst nach dem Senden
des ersten Zeichens ins eigentliche Ausgaberegister transportiert wird.
Und da die Ausgaberoutine offenbar solange ausgibt, wie der
Peripherie-Core kein 'Bin voll' meldet, werden von dem ursprünglichen
Thread genau die beiden ersten Zeichen korrekt gesendet, nicht jedoch
die wohl auf dem Stack befindlich gewesenen weiteren Zeichen, denn die
sind inzwischen von dem Zeugs anderer inzwischen aufgerufenen Funktionen
überschrieben worden.
W.S.
W.S. schrieb:> Naja, ich verstehe, was du meinst. Allerdings ist das, was diese HAL> tut, aus meiner Sicht keine Abstraktion, sondern eher Herumgelaber.
Die Benamung von Funktionen ist das Eine.
Dazu kommt dann noch der lustige Mix aus Funktionen und Makros. Warum
werden alle RCC Clocks und einzelne Interrupts mit Makros eingeschaltet,
statt auch mit Funktionen?
Aber damit hört es bei HAL ja nicht auf. HAL bringt ja richtig Code mit
um genau solche Sachen wie Interrupt-Gesteuertes Senden oder DMA selbst
abzukaspern. Beim UART kümmert sich dieser Code auch um den FIFO, der
ein paar Byte lang sein kann, wenn man will.
Damit kann man super Demos machen um zu zeigen wie schnell und einfach
man Dinge erledigen kann. Bei echten Aufgaben stört das allerdings, weil
es, innerhalb von HAL, auch keine Alternative gibt.
Cyblord -. schrieb:> Also bleibt nur HAL oder selber Registerbits setzen rein mit CMSIS.
Hi,
Ich selbst habe mich nie richtig mit HAL beschäftigt, weil es für mich
immer in ein Ratespiel geendet hat. Deshalb CMSIS.
Aber, rein aus Interesse:
Hast du dich mal mit den LL Bibliotheken von ST beschäftigt? Wie stehst
du dazu, im Vergleich zu der ursprünglichen StdPeriphLib?
Gruß,
Kleiner Tip:
Die HAL-Doku wird ständig gepflegt, und es gibt zieml. regelmäßig neue,
überarbeitete Versionen.
Am Anfang war ja die Doku der grösste Kritikpunkt - nicht zu unrecht!
Inzwischen haben die aber nen guten Job gemacht, und mit der aktuellen
Version der Doku kann man arbeiten.
Also immer mal wieder bei ST vorbeischauen, und die aktuelle Version
herunterladen!
Gemeint sind diese Docs - Bsp. für STM32F1xx:
https://www.st.com/resource/en/user_manual/dm00154093-description-of-stm32f1-hal-and-lowlayer-drivers-stmicroelectronics.pdf
just my2cent
Man sollte auf Controllern auch nicht Null-terminierte C-Strings
verwenden, sondern besser Pascal Strings, welche die Laenge zuerst
haben. Die haben ausschliesslich Vorteile.
Pandur S. schrieb:> Man sollte auf Controllern auch nicht Null-terminierte C-Strings> verwenden, sondern besser Pascal Strings, welche die Laenge zuerst> haben. Die haben ausschliesslich Vorteile.
Nein, natürlich nicht. Sie haben den Nachteil einer "unnatürliche"
Grenze für die maximale Länge. Nullterminierte Strings sind hingegen in
der maximalen Länge nur durch die natürliche Grenze des verfügbaren
Speichers begrenzt.
Ganz allgemein kann man übrigens immer davon ausgehen, dass, wenn es
irgendwelche Varianten der Implementierung irgendeiner Sache gibt, keine
dieser Varianten frei von irgendwelchen Nachteilen ist.
Weil: Wäre es anders, gäbe es keine Varianten. Jedenfalls nicht auf
lange Sicht, da würde sich immer die durchsetzen, die tatsächlich
keine objektiven Nachteile hat.
c-hater schrieb:> Nein, natürlich nicht. Sie haben den Nachteil einer "unnatürliche"> Grenze für die maximale Länge.
Wie lange Strings willst du denn auf deinem µC machen?
Also gerade auf einem µC ist deutlich weniger Platz als am PC. Das führt
also nicht zum Vorteil der nullterminierten Zeichenketten. Der
eigentliche Knackpunkt ist, daß gerade für µC am häufigsten C zum
Programmieren benutzt wird und daß C keine Strings (mit Längenangabe)
kennt. Da sind Behauptungen und Gegenbehauptungen ein bissel
gegenstandslos. Ich sag's mal so: Wir haben hier in Berlin zwar keine
Gebirge, aber wenn wir hier welche hätten, dann wären sie viel höher als
die Alpen. So. Nur als Verdeutlichung der Gegenstandslosigkeit.
W.S.
W.S. schrieb:> Wie lange Strings willst du denn auf deinem µC machen?
Auf jeden Fall kommen auch auf relativ kleinen µC recht häufig Strings
vor, die länger als 255 Zeichen sind.
Harry L. schrieb:> Die HAL-Doku wird ständig gepflegt, und es gibt zieml. regelmäßig neue,> überarbeitete Versionen.
Toll. Gestern lief mein Programm doch noch? Was ist jetzt wieder anders?
> Also immer mal wieder bei ST vorbeischauen, und die aktuelle Version> herunterladen!
Beschäftigungstherapie für Rentner? Warum sollte ich mir das antun?
Egal, wie gut oder schlecht die Doku ist, ich müsste das alles erstmal
lesen, zusätzlich zum Reference Manual. Wo ist da der Vorteil?
W.S. schrieb:> Bei mir würde das Ganze etwa so aussehen:void COM_SendNFI (void)> { String_Out(toUART1, "NFI(X0001)\n"); }
warum nicht gleich so, dann braucht man garkeine extra Doku?
haha, W.S. und printf...
der Wrapper um die HAL Funktion war doch schon im Eröffnungspost ok,
wenn der Buffer den man ausgeben möchte zu klein ist, bekommt man in
jeder Variante Probleme.
Ich weiß echt nicht was ihr alle gegen die HAL Funktionen habt, die sind
recht eindeutig und verständlich. Klar hat sie einen gewissen Overhead,
aber verständlich sind die allemal, wenn man mal Nachdenkt, statt
einfach zu sagen nein ich mach das seit 30 Jahren anders und deshalb ist
alles neue und andere Böse. Dann kommt man ohne viel gelese drauf was
die Funktionen tun, allein über die Funktionsbeschreibung im Code
selbst. Wegen Leuten wie euch gibt es hier zu wenige schnelles Internet,
da kommt der Anbieter und macht eine Umfrage um auszubauen und alle
älteren Menschen meine nein dieses Neuland brauchen wir nicht.
Selbst low level Funktionen zu schreiben ist ja gut und schön, wenn man
aber z.B. viele verschiedene µC hat wird das sehr schnell aufwendig.
Allein für die Initialisierung ist die HAL schon gut ob man die
Peripherie nachher mit HAL oder anders anspricht, kann man dann immer
noch entscheiden.
Alex -. schrieb:> Du könntest ja mal das genannte Beispiel von Cyblord anwenden und> zeigen, wir man anhand der Beschreibung von HAL Funktionsnamen aus dem> Unklaren herauskommt.
Du meinst den Timer?
Das eine startet einfach den Timer und das andere eben noch den
entsprechenden Interrupt zum Timer. Welche Interrupts dieser unterstützt
muss man natürlich nachschaun, das ist aber immer so egal HAL oder
nicht. Idr. ist es aber der Update Interrupt, auch wenn Timer wie 1 und
8 noch weitere haben.
Kevin M. schrieb:> Ich weiß echt nicht was ihr alle gegen die HAL Funktionen habt, die sind> recht eindeutig und verständlich.
Die Funktionalität der HAL von ST ist keine HardwareAbstraktion. Bei all
diesem Zeug ist drauf geachtet worde, daß die Schnittstellen 'nach oben'
nicht plattformneutral sind, sondern möglichst fest an den Hersteller ST
gebunden sind. Das ist keine Abstraktion, sondern eine Art
Kundenbindung. Abgesehen davon sehe ich, daß alle hier in diesem Forum
gezeigten Beispiele imer wieder einen Code-Aufwand in main.c nach sich
ziehen, der den geneigten Prohgrammierer wirklich nicht entlastet,
sondern ihm zusätzlichen Streß macht.
Das ist es, was ich gegen die HAL von ST habe.
Wenn du hingegen nur bei den Produkten von ST bleiben willst und in
jedem Projekt all das schreiben willst, was man braucht, um die
Standard-Sachen mit der HAL zu tun, dann ist das alles für dich
natürlich nicht wichtig.
W.S.
W.S. schrieb:> Die Funktionalität der HAL von ST ist keine HardwareAbstraktion. Bei all> diesem Zeug ist drauf geachtet worde, daß die Schnittstellen 'nach oben'> nicht plattformneutral sind, sondern möglichst fest an den Hersteller ST> gebunden sind.
Genau so ist das.
W.S. schrieb:> sondern möglichst fest an den Hersteller ST> gebunden sind
und warum sollte ST das anders machen? Code für NXP schreiben?
Für Plattformunabhängigkeit gibt es genügend andere OS, aber auch da
sind ST mit ihrem Support weit vorne um ihre Controlle zu pushen. Es
reicht heute nicht mehr nur ein Stück Silicon und Papier dazu zu
verkaufen.
Johannes S. schrieb:> und warum sollte ST das anders machen?
Sehe ich auch so, das ganze drumherum ist mit ein Grund warum ich zu ST
gewechselt bin.
ST bietet dahingehend einfach einen Mehrwert in verschiedenen Dingen.
Man kann von der Cube Umgebung halten was man will, aber CubeMX ist ein
sehr mächtiges Tool allein um einen passenden µC auszuwählen und die
Belegung im Schaltplan zu machen, ohne dass man Seitenweise Datenblätter
lesen muss. Natürlich ersetzt es nicht das Lesen von Reference Manuals
oder Datenblättern, aber es ist eine große Unterstützung.
Bauform B. schrieb:> ich müsste das alles erstmal> lesen, zusätzlich zum Reference Manual. Wo ist da der Vorteil?
Das habe ich auch gefragt, und bekam eine plausible Antwort: Die HAL
hilft dir dabei, Programmteile zu schreiben, die auf mehrerenSTM32
Modellen laufen können.
Für meine wenigen Hobby-Projekte reicht das nicht als Rechtfertigung mir
die HAL anzutun.
Stefan ⛄ F. schrieb:> Für meine wenigen Hobby-Projekte reicht das nicht als Rechtfertigung mir> die HAL anzutun.
Aktuell muss ich in verschiedenen Projekten mit 8 verschiedenen uC
Modellen arbeiten die sich teilweise Funktionalität teilen. Wenn ich da
für jeden Controller nochmal auf Register Ebene anfangen muss die LL
Treiber zu schreiben hätte ich viel zu tun.
Hallo, ich wollt euch mal meinen Code zeigen.
Der basiert auf der HAL von STM und benutzt optional einen Ringbuffer
beim Senden.
Der Empfang ist mittels DMA und verpflichtenden Ringbuffer
implementiert.
Ich finde es sinnvoll die Hersteller-API zu benutzen in der Hoffnung,
dass damit alle "Tricks" der Hardware angegangen werden.
Die Konfiguration erfolgt mittels CubeMX.
beispielsweise sieht das dann so aus: