Hallo,
ich hab mal nach einer Funktion gesucht um einen USART loopback zu
erzeugen. Also die Daten die man sendet so wieder zurueck zu erhalten.
Hier die Loesung fuer alle die auch danach gesucht haben:
Dr. Sommer schrieb:> Und jetzt bitte noch mit Interrupt und Sleep mode, sodass der Core nicht> jede Menge Rechenleistung/Energie aufs Nichtstun verschwendet.gg
@Dr.Sommer:
Och komm, das ist nicht dein Ernst, oder? Er meinte es gut und wollte
ein bisschen was zur Community beitragen.
Als Basis, auf der man aufbauen kann reicht sein Beispiel doch, oder?
Zeig mir ein Code-Beispiel, welches du eins zu eins übernehmen konntest.
Ich wette in 99,99% der Fälle nimmst du Änderungen vor, um anzupassen -
und sei es nur die Code-Kommentierung.
Ralf
Ralf schrieb:> Ich wette in 99,99% der Fälle nimmst du Änderungen vor, um anzupassen -> und sei es nur die Code-Kommentierung.Sven S. schrieb:> ihr koennt das ja gerne mal mit Interrupt machen.> Waere dran interessiert!
Offenbar scheint diese Änderung doch nicht ganz trivial zu sein.
Aufgrund des hohen-Coretakts macht die UART-Nutzung auf einem STM32
eigentlich nur mit Interrupts Sinn und wäre daher im Anfangsbeispiel
angebracht. Außerdem fehlt hier die ganze Initialisierung, und die ist
aufgrund der Komplexität der STM32 durchaus interessant. So in etwa:
}// Falls nicht, wird demnächst ein TXE-Interrupt eintreffen und wir können dann senden
88
}
89
}
90
}
In diesem Fall ist das etwas komplizierter als nötig - bei einem
einfachen loopback müsste beim Empfang eines Bytes das TX-Register immer
leer sein und man direkt senden können. Zu Demonstrationszwecken habe
ich aber die Abfrage des "TX Register Empty" Flags eingebaut, d.h. es
wird sichergestellt dass das TX-Register tatsächlich leer ist.
So, und jetzt darf wer anders das ganze mit DMA machen...
Funktioniert der Code Dr. Sommer? (selbst getestet ?)
1)
Wie ich das jetzt verstehe, werden deine Empfangenen Daten in buffer[]
reingeschrieben oder?
2)
Wie koennte ich denn jetzt was ich in buffer[] empfangen habe in meine
Variablen schreiben, wenn ich mal annehme das mir die gegenstelle
mehrere bytes geschickt hat. also bspw. 5 Ints.
Ich will ja die Daten aus dem Buffer weiterverarbeiten.
3)
Aber ist ein Interrupt Empfang wirklich noetig wenn man bspw. nur in
bestimmten faellen was empfangen moechte und nicht staendig?
Zum Beispiel gibts neben der Hauptroutine einen Empfangsmodus den der
nutzer ab und an aktivieren kann.
In diesem Fall ist doch der Interrupt nicht noetig, da der
Empfangsamodus nach getaner Arbeit verlassen wird.
Aber klar, du hast recht. Falls der loopback in der Hauptroutine laeuft
ist es sehr sinnvoll!
gruss
Sven
Sven S. schrieb:> Funktioniert der Code Dr. Sommer? (selbst getestet ?)
Teilweise getestet, das wichtigste funktioniert auf jeden Fall, glaub
aber nicht dass was falsch ist.
> 1)> Wie ich das jetzt verstehe, werden deine Empfangenen Daten in buffer[]> reingeschrieben oder?
Ja.
> 2)> Wie koennte ich denn jetzt was ich in buffer[] empfangen habe in meine> Variablen schreiben, wenn ich mal annehme das mir die gegenstelle> mehrere bytes geschickt hat. also bspw. 5 Ints.
buffer[] ist eine variable, du kannst die daten direkt da heraus
verwenden. wenn du sie als int behandeln willst, musst du das nur dem
compiler sagen:
uint32_t myInt = *((uint32_t*) &buffer);
Dies kopiert die ersten 4 bytes des "buffer" in eine int-Variable.
> 3)> Aber ist ein Interrupt Empfang wirklich noetig wenn man bspw. nur in> bestimmten faellen was empfangen moechte und nicht staendig?
Nicht zwangsweise nötig, aber äußerst sinnvoll - deine busy-wait-Methode
mit while() verbraucht viel Energie (und der Controller wird warm), da
der ganze Core aktiv ist, während man bei der Verwendung eines
Interrupts die Hauptschleife mit __WFE(); schlafen legen kann, und der
Core nur dann etwas tut, wenn tatsächlich etwas angekommen ist. Der
Energieverbrauch ist dann nur noch ein kleiner Bruchteil (ich habs
nachgemessen).
> Zum Beispiel gibts neben der Hauptroutine einen Empfangsmodus den der> nutzer ab und an aktivieren kann.>> In diesem Fall ist doch der Interrupt nicht noetig, da der> Empfangsamodus nach getaner Arbeit verlassen wird.
Nicht unbedingt noetig, aber sinnvoller. Außerden kannst du so noch
gleichzeitig auf andere Events warten - zB auf einen Interrupt bei
Tastendruck o.ä.
Mit DMA wär das ganze wohl noch effizienter, aber da hab ich grad keinen
Code zur hand.
nochmal eine dummer Frage:
den Rx Interrupt verstehe ich ja...also wurde etwas empfangen, wird es
an entsprechene Stelle in buffer geschrieben bis bufFil überläuft. Denn
gehts wieder von vorne los. Aber das Senden wie läuft das genau ab?
Ganz einfach:
Der RX Interrupt kann immer laufen. Kommt ein Zeichen speichert es die
Interrupt Routine in den RX Ringbuffer.
Der TX Interrupt läuft, solange etwas im TX Ringbuffer ist. Mit dem
letzten Zeichen im Buffer schaltet es automatisch den Interrupt aus.
Dann braucht es noch ein paar Funktionen um die Ringbuffer zu bedienen.
Zeichen ausgeben legt ein Zeichen in den Ringbuffer und startet
gegebenfalls den TX Interrupt. Eine Funktion guckt ob etwas im RX
Ringbuffer ist. Eine dritte Funktion holt ein oder mehrere Zeichen aus
dem Ringbuffer.
also die isr wird immer aufgerufen, wenn etwas empfangen wurde (RXNE)
und wenn nichts mehr zu senden ist (TXE).
Wenn ich jetzt was senden möchte schreibe ich das in den buffer etwas
rein oder auch nicht und setze, denn den Bufferfil entsprechend hoch
also wen ich 1 zeichen senden will schreibe ich:
buffer[0]=0x2;
bufFil++;
die isr wird denn aufgerufen weil ja TXE ist, er sendet denn mein
zeichen, beim nächsten isr aufruf ist txPtr denn gleich buFil und der
TXE interrupt wird diabled. der RXNE ist aber aber dieses mal gesetzt
und es wird wider etwas in den buffer geschrieben bufFil wird um 1
erhöht und der RXNE interrupt wird wieder enabled es kann wieder was
gesendet werden.
RX Isr wird aufgerufen, wenn ein Byte empfangen wurde.
TX Isr wird aufgerufen, wenn das Register bereit ist, ein neues Byte
aufzunehmen. Es gibt zwei Register, das Schieberegister (das sendet) und
ein TX Register. Sobald das Schieberegister das letzte Bit gesendet hat,
übernimmt es das TX Register.
So kann man das TX register befüllen, noch während gesendet wird. Man
hat dadurch eine durchgehende Sendung ohne Unterbrechung. Man hat
relativ lange Zeit das TX register zu befüllen (abhängig von der
transfer geschwindigkeit), so lange wie das Senden eines Bytes benötigt.
Die Ringbuffer muss man in Software implementieren.
Hallo Dr. Sommer
Dank dir für den Loopback, er hat mir bei der Einarbeitung sehr
geholfen! Nachdem die Examples in der Coocox Ide recht mager sind (oder
miserabel strukturiert) würde ich diesen Code gerne dort posten und die
Arbeit so für andere vereinfachen. Ist das möglich?
Hat schon jemand mit den Examples (USART) direkt von ST gearbeitet?
Gerade beim USART scheint es mir so als wäre hier die Send Fkt. komplett
unter den Tisch gefallen. Aber wie gesagt ich bin noch in der
Einarbeitungsphase, vielleicht habe ich diese auch übersehen ;)
Grüße
Ralf L.