Hallo,
ich möchte gerne bei meiner Anwendung die Möglichkeit haben die UART
Schnittstelle zu wechseln, beispielsweise, wenn ich auf der einen
Schnittstelle keine Daten mehr empfange soll auf die andere
Schnittstelle umgeschaltet werden. Diese Schnittstelle soll als
Rückfallebene dienen.
Ich weiss nur nicht, wie ich das anstellen soll. Hat da jemand einen
Tipp für mich? Ich muss dann ja einen anderen IRQ Handler verwenden...
Gruß
paule
Ganz versteh ich dein Problem nicht. Wieso überwachst du nicht einfach
alle UART's. Die Interrupt-routine kann für alle Uart's grundsätzlich
gleich sein, nur arbeiten sie mit seperaten Buffern für RX/TX.
Grüsse
Hallo ich habe so etwas mit Funktionspointer gelöst:
(Hier die Schnellvariante, ich hab das ein wenig komponentenorientierter
programmiert.)
void (*ptr_ISR_Funktion_UART0) (uint_8) = 0U;
void (*ptr_ISR_Funktion_UART1) (uint_8) = 0U;
/*Hat den VOrteil, dass es der bearbeitenden Funktion gleichgültig ist,
von woher sie aufgerufen wird. Interruptpriorisierung ist ev. dringend
angeraten. Aber das sollte auf dem STM32 kein Problem sein. */
/*####################################################*/
/* erste Interrupt Service Routine bspw. UART 0*/
ISR ( UART0)
{
(*ptr_ISR_Funktion_UART0) (USART_DATENREGISTER)
}
/*####################################################*/
/* zweite Interrupt Service Routine bspw. UART 1*/
ISR (UART2)
{
(*ptr_ISR_Funktion_UART1) (USART_DATENREGISTER)
}
/*####################################################*/
/* Wo anders im Code oder im Projekt die Bearbeitungsroutine*/
void MachWasDamit(uint_8 recvd_char)
{
... (Deine Aufgaben.)
}
/*####################################################*/
void Initialisiere (void)
// ganz wichtig zu Beginn aufrufen, sonst geht nicht.
{
ptr_ISR_Funktion_UART0 = MacheWasDamit;
ptr_ISP_Funktion_UART1 = MacheWasDamit;
}
/*####################################################*/
Viel Erfolg.
Hallo,
ich möchte die Hardware UART umschalten von USART1 auf USART3.
Wo ich da genau ansetze ist ja meine Frage, setze ich da an den PINs an
und ändere die auf die entsprechende Schnittstelle, oder ändere ich die
Sache im IRQ Handler, den ich geschrieben habe.
Buffer halte ich für zu kompliziert, da ich dazu softwaremäßig einiges
ändern müsste. Buffer sind rxBuffer und txBuffer.
Ich würde bei meinem IRC Handler die Variable sr anpassen und dann mit
einer if schleife abfragen, ob sr = USART3->SR oder USART1->SR ist und
dann entsprechend zum Beispiel: USART3->DR = byte oder USART1->DR=byte
setzen.
Ich hoffe man konnte mich jetzt ein bisschen besser verstehen und das
ganze nachvollziehen.
Naja, wenn es um den Empfang geht, hat ja jede UART ihre jeweils eigenen
zugehörigen Interrupt-Service-Routinen.
Also, in der ISR von UART3 würde ich nicht auf das Datenregister der
UART1 zugreifen. Es sei denn, dass es da hinweisende FLags gibt.
Aber mit der Funktionspointerlösung bist Du, wie ich glaube, gut
bedinent. Du brauchst Dir keinen Kopf darüber machen, wann von woher
etwas rein kommt. Auch solche Sachen wie TimeOut auf bestimmten Kanälen
überwachen, kannst Du getrost auf die Kombination ISR- / Pointerfunktion
verlagern.
Wenn Du eine gegenseitige Verriegelung einbauen willst, dann wird es bei
einer 3. UART, die so behandelt werden soll, langsam unübersichtlich.
Bei den Funktionspointer läuft alles automatisch. Ggf. kannst Du ja vor
dem Funktionspointeraufruf noch den Empfang des anderen Interrupt
sperren.
Viel Erfolg.
Hallo,
dann hätte ich demnach in den 2 Interruptroutinen ja dasselbe stehen wie
die Daten die ankommen oder gesendet werden behandelt werden,oder?
Dann noch eine Frage zum Funktionspointer:
Was bedeutet OU bei:
void (*ptr_ISR_Funktion_UART0) (uint_8) = 0U;
und, diese Schritte verstehe ich noch nicht:
/*####################################################*/
/* Wo anders im Code oder im Projekt die Bearbeitungsroutine*/
void MachWasDamit(uint_8 recvd_char)
{
... (Deine Aufgaben.)
}
/*####################################################*/
void Initialisiere (void)
// ganz wichtig zu Beginn aufrufen, sonst geht nicht.
{
ptr_ISR_Funktion_UART0 = MacheWasDamit;
ptr_ISP_Funktion_UART1 = MacheWasDamit;
}
/*####################################################*/
Was meinst du bei der Initialisierung mit MachWasDamit?
Weil im Prinzip empfangene ich in der ISR die Bytes und schreib sie
direkt in einen Buffer. Beim Senden genauso, da wird aus einem Buffer
gelesen. Ich hatte meine ISR ja mal angehängt. Steht bei deiner Variante
nur das in der ISR?:
ISR (UART2)
{
(*ptr_ISR_Funktion_UART1) (USART_DATENREGISTER)
}
Versteh das noch nicht soo ganz, was du da machst.
Gruß
Hi paule,
1.)
das hier ist eine Initialisierung einer globalen Variablen, die auf eine
Funktion im CODE zeigt, mit NULL.
void (*ptr_ISR_Funktion_UART0) (uint_8) = 0U;
Eigentlich brauchst Du das nicht. Aber wenn Du - warum auch immer -
während des Betriebs so etwas wie einen warmstart durchführst, ist das
definierte Rücksetzen Pflicht. - Das kannst Du halten , wie es Dir
beliebt. Ich muß nach Prozess arbeiten und da ist das Pflicht.
2.)
Den Code in // check for received data ...
würde ich aus der ISR herausnehmen und in eine separate Funktion
schreiben.
Der Trick ist die Schnittstelle. Also der Parameter mit dem die Daten
vom Aufrufer an den Bearbeiter gegeben werden.
Und genau das Schreiben der Daten in den Puffer würde ich in einer
Funktion durchführen, die als Parameter entweder das empfangene Byte
oder einen Datenpointer auf ein Array erhält, in dem ggf. die FiFo-daten
drin sind. (Ich weiß jetzt nicht, ob der STM32 einen FIFO hat oder nicht
...)
Jetzt kann nämlich Deine ISR von UART3 nur das empfangene Byte in den
Parameter des Funktionsaufrufes eintragen und in der
MachWasDamit-Routine springen.
Die Funktionspointer habe ich Dir deshalb empfohlen, weil Du dann nur
die Prototypen konsistent halten mußt und Dir das Verriegeln ersparen
kannst. Du kann auch gern konventionell die Funktion MachWasDraus oder
in Deinem Fall SchreibeByteInPuffer aufrufen.
Wenn Du jetzt aber nicht willst, dass die UART1 dazwischen funkt,
sperrst Du entweder den Interrupt oder Weißt dem Funktionspointer eine
Adresse einer Funktion mit den selben Prototypen zu, die nach Aufruf
sofort wieder zurückspringt (So was wie ein reti in Assembler).
Man nennt so etwas späte Bindung. Das heißt ich kann während der
Laufzeit durch Pointermanipulation den Ablauf der Programms ändern.
Dadurch läßt sich der von Dir erbetene geringere Aufwand - bestehenden
Code zu ändern - gering halten.
3.) Die Initialisierungsfunktion gehört nicht zu MachWasDraus.
/*####################################################*/
void Initialisiere (void)
// ganz wichtig zu Beginn aufrufen, sonst geht nicht.
{
ptr_ISR_Funktion_UART0 = MacheWasDamit;
ptr_ISR_Funktion_UART1 = MacheWasDamit; (hier war was falsch)
}
Diese Funktion muß vor dem Betrieb (In der Regel vor dem while(1)
-Konstrukt) durchgeführt werden. Weil jetzt die Funktionspointer auf die
Adresse von der Funktion MacheWasDamit zugewiesen werden.
Kommt jetzt ein Interrupt auf UART3, dann zwingt die oben angegeben
Syntax den MCU dazu, die Funktion MacheWasDamit(mit den Parametern)
aufzurufen. In Assembler wäre das der CALL ...
Und genauso würde sich jetzt die Empfangsroutine von UART1 verhalten.
Soll UART1 aber nicht MacheWasDamit aufrufen können, dann kannst Du das
durch eine einfache Zuweisung verhindern.
Diese Funktionspointer-Technik ist eine sehr flexibele Vorgehensweise
und hält den Aufwand gering.
Probier es aus. das funktioniert sogar auf den AVRs...