Forum: Mikrocontroller und Digitale Elektronik STM32: Umswitchen zu einer anderen UART Schnittstelle


von paule (Gast)


Lesenswert?

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

von Gebhard R. (Firma: Raich Gerätebau & Entwicklung) (geb)


Lesenswert?

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

von Matthias K. (matthiask)


Lesenswert?

Formulier Dein Problem am Besten nochmal von vorn;-)

Was soll umgeschaltet werden? Die Pins, die USART selber, die Buffer...

von cskulkw (Gast)


Lesenswert?

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.

von paule (Gast)


Lesenswert?

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.
1
void USART3_IRQHandler(void) 
2
{
3
  uint16_t sr = USART3->SR;
4
  portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
5
  portBASE_TYPE xTaskWokenByReceive  = pdFALSE;
6
  
7
  // Check for received Data
8
  if (sr & USART_SR_RXNE) 
9
  {
10
    if (rxReadPtr < rxWritePtr)
11
    {
12
      uint16_t byte = (uint16_t) USART3->DR;
13
      if(byte & 0x100)
14
        rxReadPtr = 0;
15
      rxBuffer[rxReadPtr++]=byte;
16
      if(rxReadPtr == 6)
17
      {
18
        rxWritePtr =(((6 + byte) + 3) & ~0x03);
19
        rxWritePtr=rxWritePtr+4;
20
      }
21
      if(rxReadPtr>6 && rxReadPtr==rxWritePtr)
22
      {
23
        length=rxWritePtr;
24
        rxWritePtr=7;
25
        rxReadPtr=0;
26
        xQueueSendFromISR (hSerialRx, &length, &xHigherPriorityTaskWoken); 
27
      }
28
    }
29
  }
30
31
  // Check if transmit buffer is empty
32
  if (sr & USART_SR_TXE) 
33
  {
34
    if(txReadPtr < txWritePtr)
35
    {
36
      uint16_t byte = txReadPtr==0 ? 0x100 : 0;
37
      byte |= txBuffer[txReadPtr++];
38
      USART3->DR = byte;
39
    }
40
    else 
41
      // disable TXE interrupt
42
      USART3->CR1 &= ~USART_CR1_TXEIE;
43
  }
44
}

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.

von cskulkw (Gast)


Lesenswert?

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.

von paule (Gast)


Lesenswert?

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ß

von cskulkw (Gast)


Lesenswert?

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...

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.