STM32 USART2 wie mehrere bytes empfangen?

Gast #2816894
Lesenswert?

Hallo,

ich nutze den STM32. In den Beispielen habe ich keine funktion gefunden, 
mehrere Bytes oder strings zu empfangen. Senden geht wunderbar.

Ich wuerde naemlich gerne ein byte[] array vom pc welcher aufgebaut ist 
wie folgt an dem stm32 empfangen und in variablen schreiben.

myPCbyte[40]

int32 myint1 = 12345678;
...
..
int16 myint2 = 128;
uint16 myint3 = 244;
...


auf dem STM32 benutze ich momentan folgenden code zum empfangen 
einzelner bytes (uint16_t)

1
char ReceivedData = NULL;
2

3
void main() {
4
.
5
.
6
.
7
while(1){
8

9
   /* Wait until a byte is received */
10
   while(USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET)
11
    {
12
    }
13
    /* Lesen des empfangnen bytes */
14
    ReceivedData = USART_ReceiveData(USART2);
15
  printf("Positionen: %d \r\n", ReceivedData);
16
    
17
  //Wenn 3 empfangen wird abbrechen!
18
  if (ReceivedData == 3)
19
  break;
20
}
21
.
22
.
23
}

Hat jemand von euch eine Idee?

Gruss
Karl
Gast #2816910
Lesenswert?

Ich habe keinen STM32-spezifischen Code, aber im Prinzip könte das so 
funktionieren wie bei mir mit MSP430-Code.
In der Interrupt-Empfangsroutine wird nach dem Empfang eines Zeichens 
über den UART ein Timeout gesetzt, innerhalb dem ein weiteres Zeichen 
eempfangen werden kann, und danach wieder, ...
Ist der Timeout ohne den Empfangs eines neuen Zeichens abgelaufen, löst 
das einen Timer-Interrupt aus und der empfangene String wird 
zurückgegeben und ausgewertet.

interrupt (UART0RX_VECTOR) usart0_rx(void)  // Interrupt_Empfangsroutine
{
...
  if (uart0_RX_anz < RX0_BUFFER_SIZE) // Um Pufferüberlauf zu vermeiden
    {
      UART0_RX_Buffer[uart0_RX_anz] = RX0IN_CHAR;  // Empfangenes 
Zeichen in Buffer abspeichern
      uart0_RX_anz++;
    }

    // Timeout für Zeichenempfang über UART setzen
    set_UART0_rcv_Timeout(24576);  // Einheit für "l_timeout" sind 
Taktzyklen (0<=l_timeout < 32767), f=32768 Hz
}

Stell den Code für den STM32 doch ein, wenn er funktioniert.
Gast #2816954
Lesenswert?

Ne sorry dein Code macht was ganz anderes aber trotzdem vielen dank!!

bei mir gehts darum die Funktion

  ReceivedData = USART_ReceiveData(USART2);

so umzubauen das es nicht nur ein byte empfaengt sondern mehrere.

Gruss
Karl
Persönliche Seite #2817024
Lesenswert?

hmmm...
1
void USART_ReadChunk(USART_TypeDef* USARTx, uint8_t* pChunk, uint16_t nBytes) {
2
  while (nBytes--) {
3
    *pChunk++ = stm32_usart_rw(USARTx, 0) & 0x00FF;
4
  }
5
}
6

7
static __INLINE uint16_t stm32_usart_rw(USART_TypeDef* USARTx, uint16_t data) {
8
  /* Wait for Tx buffer empty */
9
  while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
10
  /* Write one byte in the USART Transmit Data Register */
11
  USART_SendData(USARTx, data);
12
  /* Wait until end of transmit */
13
  while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
14
  /* Return the received byte from the USART */
15
  return USART_ReceiveData(USARTx);
16
}
Suchst Du sowas?
#2817027
Lesenswert?

Wie schon gesagt : am besten mit eine ISR die jeden empfangen Zeichen 
abspeichert in eine Array. Beim empfangen von eine \r wird dan in main 
diese String weiter verarbeitet.
1
void USART1_IRQHandler(void)
2
{
3
  if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
4
  {
5
    /* Read one byte from the receive data register */
6
    bufferRx[RxCounter++] = USART_ReceiveData(USART1);
7
   // USART_SendData(USART1, bufferRx[RxCounter-1]);//echo
8
    if(bufferRx[RxCounter-1]=='\r'){
9
       strcpy(receiveRx, bufferRx);
10
       receiveRx[RxCounter-1]='\0';
11
       RxCounter=0;printRx=1;
12
      }
13
    if(RxCounter > 125)
14
    {
15
      /* Disable the USART1 Receive interrupt */
16
      USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
17
    }
18
  }
19
 }  
20
void NVIC_Configuration(void)
21
{
22
   NVIC_InitTypeDef NVIC_InitStructure;
23

24
  /* Enable the USART1 Interrupt */
25
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
26
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
27
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
28
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
29
  NVIC_Init(&NVIC_InitStructure);
30
  NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn  ;
31
  NVIC_Init(&NVIC_InitStructure);
32
}
#2817053
Lesenswert?

nunja...der Code ist schon nicht ganz unpassend.
Dort wird in einem Interrupt ein Puffer befüllt...das benötigst du auch.

Definitionsgemäss werden bei UART immer 8 Datenbits übertragen. Wie du 
diese dann interpretierst, bleibt Dir überlassen.

Nehmen wir an, du willst eine Int16 Zahl übertragen, dann musst du diese 
Zahl vorm Senden in zwei 8bit Werte aufsplitten.

z.b. mit
 int16 send = 0x4321;
 unsigned char hi_byte = send >> 8;  //enthält dann 0x43
 unsigned char lo_byte = (unisgned char)(send & 0x00ff); //enthält 0x21


diese beiden Bytes sendest du nun mit deiner Sende-Routine an den 
Empfänger.


Der Empfänger liest die beiden Werte ein, und hat nun zwei Byte 
Variablen mit deinem hi_byte und deinem lo_byte Werten, und bastelt 
daraus wieder eine int16 Variable

int16 receive = (hi_byte << 8) | lo_byte;
Gast #2817115
Lesenswert?

Danke jungs!

Ja, man kann das mit Interrupts machen aber ich brauch den USART2 
handler eigentlich nicht weil die abfrage eh in meiner loop lauft und 
der DR beim empfang geschrieben wird. (oder irre ich mich da?)

Stellt euch einfach eine UART-Loopback-Funktion vor.
Ich sende per HTERM bspw. : 00 AA FF EE 0B
und das gleiche soll wieder zurueck kommen.

so wie ich das gemacht habe klappt das nicht:

1
while(1)
2
{
3
                
4
//Bytes Empfangen ( RXBUFF[248] )
5

6
/* Wait until a byte is received */
7
while(USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET);
8
{}
9
  
10
  RXBUFF[j++] = USART_ReceiveData(USART2);
11
  if (j >= buflgth) 
12
  j = 0;
13
      
14

15
  //Bytes Zuruecksenden ( RXBUFF[248] )
16
  while ( j>= buflgth)
17
  {
18
  USART_SendData(USART2, RXBUFF[j]);
19
  j++;
20
                    
21
    /* Loop until the end of transmission */
22
    while (USART_GetFlagStatus(IMU_COM2, USART_FLAG_TC) == RESET)
23
    {}
24
  }
25
  
26
printf("alle bytes empfangen und zurueckgesendet\r\n");
27

28
}


VG
Karl
Gast #2817308
Lesenswert?

>while(USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET);
>{}

Zufälligerweise nicht falsch, aber der ";" gehört da nicht hin...

>  RXBUFF[j++] = USART_ReceiveData(USART2);

Hier wird der Index inkrementiert...

>  if (j >= buflgth)

Soll wahrsch eher j < buflgth heissen

>  j = 0;
>
>
>  //Bytes Zuruecksenden ( RXBUFF[248] )
>  while ( j>= buflgth)

hier auch.

Wenn Du den KOMPLETTEN, bis dahin empfangenen String
ausgeben willst, solltest Du Dir eine weitere
Indexvariable gönnen.
Eine for-Schleife hätte auch was für sich.

>  {
>  USART_SendData(USART2, RXBUFF[j]);

....und hier greifst Du in's Leere

>  j++;

...und vielleicht noch mehr...

Falls die Code-Formatierung nicht durch die Forensoftware verursacht
wird, solltest Du da dringend nachbessern.
#2817329
Lesenswert?

Im Grunde ist Dein Code nicht völlig falsch, aber man kann schwer in 
Worte fassen, was zu ändern wäre. Daher hier ein konkreter Vorschlag, 
wie es funktionieren könnte. Der Punkt ist, dass zu jeder Zeit beide 
UARTs überwacht werden müssen, um einen UART-Overrun zu verhindern. 
Damit dürfen im Rahmen der Puffergröße die UARTs auch mit 
unterschiedlichen Baudraten arbeiten.
1
void Loop(int cnt)
2
{
3
  uint8_t buffer[100];
4
  int bufput = 0,bufget = 0;
5

6
  while (cnt > 0)
7
  {
8
    // Byte received from UART?
9
    if (USART_GetFlagStatus(USART2, USART_FLAG_RXNE) != RESET)
10
    {
11
      // Yes, store it in our ringbuffer
12
      buffer[bufput++] = USART_ReceiveData(USART2);
13
      if (bufput >= sizeof(buffer)) 
14
        bufput = 0;
15
    }
16
    // UART free for transmission?
17
    if (USART_GetFlagStatus(IMU_COM2, USART_FLAG_TC) != RESET)
18
    {
19
      // Yes. Bytes there to be sent?
20
      if (bufput != bufget)
21
      {
22
        // Yes, send one
23
        USART_SendData(USART2, buffer[bufget++]);
24
        if (bufget >= sizeof(buffer))
25
          bufget = 0;
26
        cnt--;
27
      }
28
    }
29
  }
30
  printf("alle bytes empfangen und zurueckgesendet\r\n");
31
}
#2818244
Lesenswert?

Hier noch mein Versuch
1
  unsigned char RXBUFFER[50];
2
  int bufput = 0;
3
  int cnt = 50;
4
  while (cnt > 0)
5
  {
6
    // Byte received from UART?
7
    if (USART_GetFlagStatus(USART2, USART_FLAG_RXNE) != RESET)
8
    {
9
      // Yes, store it in our ringRXBUFFER
10
      RXBUFFER[bufput++] = USART_ReceiveData(USART2);
11
      if (bufput >= sizeof(RXBUFFER)) 
12
        bufput = 0;
13
    }
14
c--;
15
  }
#2818354
Lesenswert?

Da ist nur das c-- an der falschen Stelle.

1
unsigned char RXBUFFER[50];
2
  int bufput = 0;
3
  int cnt = 50;
4
  while (cnt > 0)
5
  {
6
    // Byte received from UART?
7
    while (USART_GetFlagStatus(USART2, USART_FLAG_RXNE) != RESET)
8
    {
9
      // Yes, store it in our ringRXBUFFER
10
      RXBUFFER[bufput++] = USART_ReceiveData(USART2);
11
      if (bufput >= sizeof(RXBUFFER)) 
12
        bufput = 0;
13
      c--;
14
    }
15
  }


Aber für so eine Anwendung geht es auch einfacher, quasi straight 
forward, weil nur eine Schnittstelle bedient werden muss.

1
  unsigned char RXBUFFER[50];
2
  int bufput;
3
  for (bufput = 0; bufput<sizeof(RXBUFFER); bufput++)
4
  {
5
    // Wait for character arriving
6
    while (USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET)
7
      ;
8
    RXBUFFER[bufput] = USART_ReceiveData(USART2);
9
  }
#2820755
Lesenswert?

Ich hab das jetzt per DMA und Interrupt gemacht ;)

War bissl arbeit, weil ich nicht gewusst hatte das der DMA für jeden 
Transferkanal neu erzeugt werden musste. (Danke an dieser stelle an das 
Errata sheet von ST!)

Ich mach das jetzt noch bissl schön und dan poste ich den code.
#2821092
Lesenswert?

Sven S. schrieb:
> ich wuerde gerne deine Funktion so umschreiben, das sie genau 50bytes in
> den buffer empfaengt und sich dann beendet.

Das geht nicht.

Die UART kann nicht wissen, welches das erste und welches das 50-te Byte 
ist. Die UART schmeißt alle Bytes in die FIFO.

Erst durch ein Protokoll kannst Du sicher sein, daß die Bytes die 
richtige Position haben.
Z.B. ist es ein Text, dann kannst Du ihn mit CR,LF abschließen.
Deine Parser weiß also, daß alle Bytes nach CR,LF zum nächsten Paket 
gehören.
Ein Byte nach CR,LF ist also immer das erste Byte eines Pakets.

Für Binärdaten braucht man ein anderes Protokoll. Z.B. durch ein 
Escape-Zeichen (z.B. 0xA5) werden Protokoll und Daten unterschieden.

Ohne Protokoll reicht es, wenn einmal ein Byte gestört ist oder eine 
Station ein Reset macht und Du bist für alle Zeit asynchron und 
empfängst nur Mumpitz.
Mit Protokoll ist nur das fehlerhafte Paket gestört, das nächste ist 
wieder synchron.


Peter
#2821351
Lesenswert?

Ja, genau bspw. mit einem End-Identifier etc.

Ich habe aber bereits eine andere Lösung gefunden. Denn wenn bekannt 
ist, das bspw. dein Datenframe auf den du wartest 50bytes lang ist, dann 
kann man sich da mit einer einfachen Schleife helfen:

Der Code ist getestet und funktioniert
1
u8 Buffer[50];
2

3
void Uartlesen() 
4
{
5
  
6
  int j =0;
7
  int cnt = 0;       
8
  while(cnt <= 50-1)  //zu erwartende bytes -1
9
  {
10
       while(USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET);
11
          
12
       Buffer[j] = USART_ReceiveData(USART2);
13
       cnt++;          
14
       j++;
15
       if (j >= 50) //i >= bytes
16
        j = 0;  
17
  }
18

19
}
#2821384
Lesenswert?

Sven S. schrieb:
> Denn wenn bekannt
> ist, das bspw. dein Datenframe auf den du wartest 50bytes lang ist, dann
> kann man sich da mit einer einfachen Schleife helfen:

Aber nur äußerst notbehelfen.

Das geht nur solange gut, wie beide Gerät immer zugleich eingeschaltet 
werden, nie resettet werden und nie jemand über das Kabel stolpert und 
es wieder reinsteckt.

Schon wenn ein Gerät mal später angeschaltet wird, das andere aber 
gerade 1 .. 49 Byte rausgerotzt hat, ists Essig.
Oder der Empfänger ist früher eingeschaltet und das Power-On des Senders 
erzeugt ne Startflanke und wird damit als 1.Byte gewertet.

Sowas ist eine reine Edelbastlerlösung nur für Dich zuhause.
In der Praxis haut Dir jeder sowas um die Ohren.


Peter
#2821418
Lesenswert?

Peter Dannegger schrieb:

> Sowas ist eine reine Edelbastlerlösung nur für Dich zuhause.
> In der Praxis haut Dir jeder sowas um die Ohren.

Ganz genau.


Ganz abgesehen davon, das das hier
1
 int cnt = 0;       
2
  while(cnt <= 50-1)  //zu erwartende bytes -1
3
  {
4
    ...
5
       cnt++;          
6
    ...
7
  }

maximalkryptisch für
1
  int cnt = 0;
2

3
  for( cnt = 0; cnt < 50; cnt++ )
4
  {
5
    ...
6
  }

ist.
Wenn etwas eine Zählschleife ist, dann realisiere es auch als 
Zählschleife. Dann muss sich ein späterer Leser des Codes nicht die 
Frage stellen "Was zum Kuckuck hatte er da eigentlich vor? Warum hat er 
nicht einfach eine for-Schleife benutzt? Wäre das etwa zu simpel 
gewesen? Da steckt doch was dahinter - nur was?"
#2821433
Lesenswert?

Ja du hast recht - aber solange es eine definierte Übertragung zu einem 
definerten Zustand ist --- lass ich mir nicht sagen das es eine 
Edelbastlerlösung ist!

Hier in diesem Falle ist der Empfangsablauf folgendermaßen:

1. Controller geht in Empfangsmodus und wartet auf exakt 50 bytes
2. Client sendet nach erhalt der Information, dass die Gegenstelle im 
Empfangsmodus ist, die Daten
3. Controller empfängt und speichert die Daten.
4. Prüft ob die ersten 2 bytes 47-11 ergeben und die letzen ebenfalls 
-Falls ja ist das Datentelegramm i.O.
5. Ende der Übermittlung

man könnte jedes Datenpaar mit einer crc prüfsumme versehen. das wäre 
eine Maßnahme um die Datensicherheit zu erhöhen.



Edit: Karl, das ist der IP-Schutz :)   ... kein umständlich 
ausgedrückter code.

Und wenn du schon ... dann bitte so:
1
  for( int cnt = 0; cnt < 50; cnt++ ) //c99
2
  {
3
    ...
4
  }
#2821442
Lesenswert?

Sven S. schrieb:

> 4. Prüft ob die ersten 2 bytes 47-11 ergeben und die letzen ebenfalls
> -Falls ja ist das Datentelegramm i.O.

Und genau hier hast du dein Problem.
Sobald du aus irgendeinem Grund unsychron wirst, hast du keine Garantie 
mehr, dass das was du (als Empfänger) als die ersten beiden Bytes 
ansiehst auch aus Sicht des Senders die ersten beiden Bytes waren.

Ab da geht, wenn die Übertragung unsynchron wird, alles den Bach runter. 
Und was noch viel schlimmer ist: es fängt sich auch nicht mehr von 
alleine. Die Asynchronität bleibt bis zum Strom-abdrehen bestehen.

Bei einem guten Protokoll kannst du den RS232 Stecker ziehen und wieder 
anstecken. Beliebig oft. Zu beliebigen Zeiten.
Die beiden Partner mögen ein wenig 'husten' und ein paar Üertragungen 
als ungültig im Log-File vermerken aber danach regelt das Protokoll 
wieder alles und die beiden arbeiten korrekt weiter als sie nie 
irgendwas geschehen.
#2821446
Lesenswert?

Karl,

wenn ich 50 bytes empfangen habe ... egal wie und ich validieren kann ob 
das Telegramm plausibel ist oder nicht, dann ist doch der Fisch wieder 
ins Wasser geworfen. In dem Fall, wie soll da die asynchronität 
stattfinden?
Es wird ja byte für byte empfangen , die empfangsschleife läuft ja nicht 
weiter wenn nichts gesendet wird sondern wartet jedes byte ab.

Ich will dir nicht wiedersprechen da du vollkommen recht hast! Nichts 
geht über ein gutes Protokoll. Ich selbst komme aus Socketprogrammierung 
und TCP/UDP Ecke. Ich weiß das zu schätzen. Das wird die leider beim 
Controller nicht geschenkt! Gute Transmissionsprotokolle sind schwer zu 
finden und nur anstrengend nachzutippen.

Für die Übermittlung von den paar Daten war es aus meiner sicht nicht 
nötig, da es keine Sicherheitskritische Anwendung ist.

Aber wenn du lust hast können wir gerne ein Github Projekt eröffnen und 
wir schreiben den Benutzern das schönste Protokoll der Welt ;)

ODER man benutzt einfach CAN oder TCP/IP und wir vergraben die RS232!
#2821451
Lesenswert?

Sven S. schrieb:
> Karl,
>
> wenn ich 50 bytes empfangen habe ... egal wie und ich validieren kann ob
> das Telegramm plausibel ist oder nicht, dann ist doch der Fisch wieder
> ins Wasser geworfen.

Der springende Punkt ist, dass ab dem ersten Fehler ALLE weiteren 
Telegramme nicht mehr plausibel werden. ALLE!

Die beiden sind dann zeitversetzt.

Der Sender sendet (am Beispiel mit einer Telegramgröße von 8)

        Frame            Frame
   +-------------+  +-------------+  +--- Frame
   |             |  |             |  |
   0 1 2 3 4 5 6 7  0 1 2 3 4 5 6 7  0 1 2 3 ...

und der Empfänger versteht

       +----- Frame --+ +-- Frame -----+ +---
       |              | |              | |
       2 3 4 5 6 7  0 1 2 3 4 5 6 7  0 1 2 3 ...

und diesen Versatz wirst du auch nie wieder los. Der Sender sendet 
jeweils 8 Byte, der Empfänger wartet jeweils auf 8 Bytes. Aber er fängt 
an einer anderen Stelle im Datenstrom mit dem zählen bis 8 an!

Und alles nur, weil der Empfänger die ersten beiden Bytes nicht richtig 
mitbekommen hat (aus welchem Grund auch immer. Kabel abgefallen, zu spät 
eingeschaltet, Funkstörung, im Reset ein klein wenig zu lange gebraucht, 
...)
#2821455
Lesenswert?

Sven S. schrieb:

> und TCP/UDP Ecke. Ich weiß das zu schätzen. Das wird die leider beim
> Controller nicht geschenkt! Gute Transmissionsprotokolle sind schwer zu
> finden und nur anstrengend nachzutippen.

Übertreib nicht. So schwer ist das auch wieder nicht.
Wenn man sich (zur Not mittels Escaping) 2 definierte Bytewerte 
freischaufelt, hat man schon mal den ersten Schritt.
#2821463
Lesenswert?

Karl Heinz Buchegger schrieb:
> Die werden NIE mehr richtig ankommen! Den Versatz wirst du bis zum St.
> Nimmerleinstag nicht mehr los. Das renkt sich NIE WIEDER von selber ein.

Doch das werden Sie da ja die Gegenstelle weiß wann empfangen wurde und 
wann gesendet wurde und wieviel gesenet wurde und ob die daten richtig 
angekommen sind und dem entsprechend wir der buffer auf der 
empfängerseite wieder geleert.

Und die Empfängerseite wartet und wartet bis die 50 da sind und wenn ein 
versatz da ist, dann stimmen die daten nicht und dann geht die show von 
neuem los!
#2821466
Lesenswert?

Sven S. schrieb:
> Karl Heinz Buchegger schrieb:
>> Die werden NIE mehr richtig ankommen! Den Versatz wirst du bis zum St.
>> Nimmerleinstag nicht mehr los. Das renkt sich NIE WIEDER von selber ein.
>
> Doch das werden Sie da ja die Gegenstelle weiß wann empfangen wurde und
> wann gesendet wurde und wieviel gesenet wurde und ob die daten richtig
> angekommen sind und dem entsprechend wir der buffer auf der
> empfängerseite wieder geleert.
>
> Und die Empfängerseite wartet und wartet bis die 50 da sind und wenn ein
> versatz da ist, dann stimmen die daten nicht und dann geht die show von
> neuem los!
Und die nächsten 50 Bytes sind wieder falsch.
Und die darauf folgenden 50 Bytes sind wieder falsch.
ad infinitum.

Aber was solls:
Wenn du meinst. Ist ja nicht mein Bier.
Der Rest der Welt verwendet ein dezidiertes Startbyte und/oder ein 
dezidiertes Endbyte und synchronisiert jedes Telegramm darauf erneut 
ein.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren