Forum: Mikrocontroller und Digitale Elektronik Verständnisfrage stm32 uart


von elektro01 (Gast)


Lesenswert?

Hallo zusammen,

ich versuche gerade ein bzw. mehrere Zeichen über die rs232 zu senden 
und das im Interruptmodus. Das funktioniert auch schon mehr oder weniger 
gut. Ich verwende dazu CooCox und die HAL library.
Controller: stm32f429ni

Was ich aber nicht verstehe ist folgender Befehl:

HAL_UART_Receive_IT(&UartHandle, rx_data, 1);

Muss der "Befehl" in der while(1) schleife stehen?? Dann ist das ja kein 
richtiger Interrupt oder??
Außerdem muss man vorher die Anzahl der Zeichen angeben, die weiß ich ja 
vorher noch nicht. Wie wäre denn das zu lösen.

Vielen Dank schonmal und schöne Grüße

von Claus M. (claus_mueller) Benutzerseite


Lesenswert?

So viele Fragen auf einmal :-)

Fangen wir mal an...

Stell erst mal bitte den Code vor. Also deine Routine + 
HAL_UART_Receive_IT. Dann kann man schon mal sagen, wie diese 
zusammenarbeiten.

"Normal" wäre: uC bekommt ein Zeichen per UART-Rx - Interrupt wird 
ausgelöst - im INT_Handler wird das empfangene Byte "weggeschrieben" 
(z.B. in ein Struct oder Buffer oder was auch immer)- in der 
Haupschleife verarbeitet man das Byte

Thema String empfangen: entweder per DMA (einfache Lösung, wenn die 
Länge des Strings vorher bekannt ist) oder Interruptgesteuert (dann muss 
man die Routinen dafür aber selbst schreiben). Schau dir mal als erstes 
das hier an, damit du mal eine Idee bekommst wie man sowas macht:

https://www.mikrocontroller.net/articles/FIFO

von elektro01 (Gast)


Lesenswert?

Danke schonmal für die rasche Antwort.

Also mein Code sieht wie folgt aus:


#include "stm32f4xx_hal.h"
#include "stm32f4xx_hal_rcc.h"
#include "stm32f4xx_hal_gpio.h"
#include "stm32f4xx_hal_uart.h"
#include "stm32f4xx_hal_cortex.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


UART_HandleTypeDef uart_config;

int x=0;
char data[10];
char rx_byte[1];

int main(void)
{
  int i=0;

  HAL_Init();

  SystemInit();

//************  UART CONFIG  *****************************//

  __USART1_CLK_ENABLE();

  uart_config.Instance=USART1;

  uart_config.Init.BaudRate=115200;
  uart_config.Init.WordLength=UART_WORDLENGTH_8B;
  uart_config.Init.StopBits=UART_STOPBITS_1;
  uart_config.Init.Parity=UART_PARITY_NONE;
  uart_config.Init.Mode=UART_MODE_TX_RX;
  uart_config.Init.HwFlowCtl=UART_HWCONTROL_NONE;
  uart_config.Init.OverSampling=UART_OVERSAMPLING_8;

  HAL_UART_Init(&uart_config);

  HAL_NVIC_SetPriority(USART1_IRQn,0,1);
  HAL_NVIC_EnableIRQ(USART1_IRQn);

//**********************************************************//

//************ UART GPIO CONFIG  *********************//

  GPIO_InitTypeDef uart_gpio;

  __GPIOA_CLK_ENABLE();

  uart_gpio.Pin=GPIO_PIN_9 | GPIO_PIN_10;
  uart_gpio.Mode=GPIO_MODE_AF_PP;
  uart_gpio.Pull=GPIO_NOPULL;
  uart_gpio.Speed=GPIO_SPEED_FAST;
  uart_gpio.Alternate=GPIO_AF7_USART1;


  HAL_GPIO_Init(GPIOA, &uart_gpio);

//******************************************************//


//************  LED  ****************************//

  GPIO_InitTypeDef gpio_led;

  __GPIOG_CLK_ENABLE();

  gpio_led.Pin=GPIO_PIN_12;
  gpio_led.Mode=GPIO_MODE_OUTPUT_PP;
  gpio_led.Pull=GPIO_NOPULL;
  gpio_led.Speed=GPIO_SPEED_FAST;

  HAL_GPIO_Init(GPIOG, &gpio_led);

//***********************************************//


  while(1)
    {

    HAL_UART_Receive_IT(&uart_config, rx_byte, 1);

      HAL_GPIO_WritePin(GPIOG, GPIO_PIN_12, 0);

      for (i=0; i<=500000; i++);  //delay für LED

      HAL_GPIO_WritePin(GPIOG, GPIO_PIN_12, 1);

      for (i=0; i<=500000; i++);      //delay für LED


    }
}


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{

  data[x]=rx_byte[0];

  if (data[x] =='\r')
  {
    HAL_UART_Transmit_IT (&uart_config, data, x);
    x=0;
  }
  else
  {
    x++;
  }
}

void USART1_IRQHandler(void)
{
  HAL_UART_IRQHandler(&uart_config);

}


Wäre meine Überlegung so prinzipiell richtig??
An der Ausführung muss natürlich noch gearbeitet werden :)

von Patrick B. (p51d)


Lesenswert?

Hab mal etwas gesucht, und im Jahr 2014 war das auch schon ein Problem:
https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32Java/Flat.aspx?RootFolder=https%3a%2f%2fmy.st.com%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fSTM32Java%2fSTM32Cube%20%20UART%20Receive%20Interrupt&FolderCTID=0x01200200770978C69A1141439FE559EB459D758000F9A0E3A95BA69146A17C2E80209ADC21&currentviews=4114

Ich habe mich nie richtig mit den ganzen Cube von ST beschäftigt, aber 
du könntest z.B den USART1_IRQHandler anpassen:
1
void USART1_IRQHandler(void)
2
{
3
//  HAL_UART_IRQHandler(&uart_config);
4
  data[x]=rx_byte[0];
5
6
  if (data[x] =='\r')
7
  {
8
    HAL_UART_Transmit_IT (&uart_config, data, x);
9
    x=0;
10
  }
11
  else
12
  {
13
    if(x < 10)
14
    {
15
      x++;
16
    }
17
    else
18
    {
19
      // Error
20
    }
21
  }
22
}

Die Funktion HAL_UART_Receive_IT sieht für mich aus, als ob hier eine 
grosse und definierte Anzahl an Bytes empfangen werden soll. Der 
Interrupt selber wird erst ausgeführt, wenn die x Bytes auch wirklich 
empfangen wurden. Also Timeout und solches Zeugs müsstest du selber 
handhaben.

Wenn du aber nicht weisst, wieviel Übertragen wird, dann würde ich 
wieder "altmodisch" direkt den IRQ-Hanlder verwenden. Da hast du die 
volle Kontrolle.

: Bearbeitet durch User
von elektro01 (Gast)


Lesenswert?

Vielen Dank für die Antworten aber leider bekomme ich das Ding nicht 
richtig zu laufen.

Was ich immer noch nicht verstehe folgender Befehl:
HAL_UART_Receive_IT(&uart_config, rx_byte, 1);

Der muss scheinbar in der Hauptschleife stehen, sonst funktioniert 
nichts. Das ist aber meiner Meinung nach dann keine Interruptsteuerung, 
wenn ich das jedes mal aufrufen muss, oder?? Kann mir das jemand 
erklären??

Hat vielleicht jemand ein simples Beispielprogramm mit der HAL library, 
welches er posten kann!?

Danke schonmal.

von elektro01 (Gast)


Lesenswert?

Hat denn keiner einen Tipp??

von Jim M. (turboj)


Lesenswert?

Mein Tipp: Use the source, Luke!

Schau halt selber im Source der Hal Lib nach was die da so treibt.

von Johannes S. (Gast)


Lesenswert?

elektro01 schrieb:
> Was ich immer noch nicht verstehe folgender Befehl:
> HAL_UART_Receive_IT(&uart_config, rx_byte, 1);
>
> Der muss scheinbar in der Hauptschleife stehen, sonst funktioniert
> nichts. Das ist aber meiner Meinung nach dann keine Interruptsteuerung,
> wenn ich das jedes mal aufrufen muss, oder?? Kann mir das jemand
> erklären??

Der Thread ist zwar schon alt, wurde aber gerade in 
Beitrag "Re: STM32 HAL Tutorial" referenziert und da 
passt die Antwort besser hierhin.

Die Funktion  HAL_UART_Receive_IT(&uart_config, rx_byte, 1); liefert 
einen Status zurück der ausgwertet werden sollte. Wenn ein Receive 
gestartet und noch nichts empfangen wurde wird die beim erneuten Aufruf 
Busy zurückliefern. Im HAL_UART_RxCpltCallback() kann man ein Flag 
setzen wenn der Empfang fertig ist (weil z.B. ein Endezeichen empfangen 
wurde) und im Hauptprogramm kann man andere Sachen erledigen und 
gelegentlich das Empfangsflag abfragen.

Aus dem anderen Thread:
technikus schrieb:
> HAL_UART_Receive_IT(&UartHandle, rx_data, 10);
>
> Füllt fleißig den Puffer bis 10 Bytes empfangen wurden und beginnt dann
> wieder von vorne - alles Interrupt gesteuert.
>
> Was ist aber, wenn ich in der Hauptschleife auslese ob schon was da ist
> und dann den Startzeiger vom Puffer neu initialisieren möchte?
> Qick and dirty kriege ich das hin, geht aber irgendwie am HAL Gedanken
> vorbei...

Das halte ich für keine gute Strategie, das behandelt man besser in 
einer Statemachine. Wenn das Protokoll über die Schnittstelle z.B. mit 
fixem Header+Daten aufgebaut ist startest du einen Receive mit Anzahl 
Headergrösse und setzt den Status auf 'STATE_RECEIVE_HEADER'. Im 
RxCpltCallback wird dann nur der Status auf Header empfangen gesetzt. 
Wenn das Hauptprogramm diesen Status erkennt startet es dann den Receive 
mit der nötigen Anzahl Daten.

von technikus (Gast)


Lesenswert?

Danke für den konstruktiven Beitrag!
Nur was mache ich, wenn der Empfangspuffer mit z.B. 10 Zeichen definiert 
ist, 9 empfangen werden und das Endzeichen nicht empfangen wird?
Dann müsste die Funktion ja nach einem Timeout den Puffer von vorne 
füllen ?!

von Johannes S. (Gast)


Lesenswert?

ja, timeout ist das Stichwort nachdem ich auch suchen würde. Im User 
Manual ist ein Kapitel 'Timeout and error management'. Das wird für 
mehrere Funktionen benutzt ist damit übergeordnet.
Im Kapitel UART ist noch ein 'how to use' da findest du schonmal eine 
kurze Erklärung zum nötigen Ablauf. Und die Interrupt und DMA 
(non-blocking) Funktionen können mit den Abort Funktionen abgebrochen 
werden, damit muss man dann etwas spielen.
Wenn man ein Protokoll ohne Endezeichen hat kann man auch Blockweise mit 
Timeout lesen, solche Protokolle finde ich aber weniger gut. Ansonsten 
ist die Nutzung von Timeouts sehr zu empfehlen. Weil das mehr Aufwand 
ist spart man sich das gerne, aber wenn der HAL das schon bietet sollte 
man das auch mitnehmen.
Für mich macht das den Unterschied zwischen funktionierenden und 
robusten Programmen aus: es gibt viele die tun was sie sollen, aber 
einmal Stecker raus-rein und das Programm hängt und muss neugestartet 
werden. Programmiere industrielle Steuerungen und du weisst was das 
kostet...

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.