mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik stm32 HAL UART Receive_IT(.) sendet nur 1 Byte


Autor: Marco D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Servus zusammen,

ich versuche gerade auf meinem Nucleo F767ZI Daten über die 
HAL_UART_Receive_IT() Funktion zu empfangen. Leider kommt im Speicher 
nur das erste Bit an welches versende. Hier der Quellcode:


In der main.c:

uint8_t a = 0;
char docklight[10];

int main(void){
   while(1){
    HAL_UART_Receive_IT(&huart3,&docklight,strlen(docklight));
    if(a == 1){
      a = 0;
      ptr = strtok(docklight,",");
      while(ptr != NULL){
        HAL_UART_Transmit(&huart3,(uint8_t*)ptr,strlen(ptr),10);
        ptr = strtok(NULL,",");
      }
    }
}
}

In der stm32f7xx_it.c:

extern a;

void USART3_IRQHandler(void)
{
  a = 1;
  HAL_UART_IRQHandler(&huart3);
}

Die Variable a soll nur den Transmit Code aus der main auslösen. Ich 
wollte den Interrupt so kurz wie möglich halten, die Daten werden in der 
main später noch weiterverarbeitet.

Wenn ich mit dem Debugger durchgehe wird nur das erste Zeichen meines 
gesendeten Strings gespeichert obwohl ich mehrere versende. Wenn ich das 
Programm ohne Breakpoints ausführe, sendet er mir den gesamten String 
zurück was ich ja möchte. Ich hab keine Ahnung warum er das im Debug 
Modus nicht macht. Ich kann so den String nicht weiterverarbeiten weil 
er immer nur das erste Zeichen des gesendeten Strings speichert. Ich 
verwende zum Senden und zum Anzeigen der Daten Docklight.

Autor: Stefan K. (stefan64)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marco D. schrieb:
> Wenn ich mit dem Debugger durchgehe wird nur das erste Zeichen meines
> gesendeten Strings gespeichert obwohl ich mehrere versende.

Wenn der STM direkt beim ersten Zeichen, das er empfängt anhält, dann 
kann der Interrupt auf alle weiteren Zeichen nicht mehr reagieren. Dein 
PC sendet ja den restlichen String, auch wenn der STM sich im Breakpoint 
befindet.

Gruß, Stefan

Autor: pegel (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
In der _*_it.c brauchst du nichts zu ändern.
Füge im die main.c die Funktion HAL_UART_RxCpltCallback ein.
In dieser kommt jedes Zeichen einzeln an und du kannst auf Zeilenende 
auswerten oder einen Puffer mit einer Anzahl Zeichen füllen.

Autor: Marco D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Inwiefern muss die Callback Funktion in der main eingebaut werden ? Ich 
werde aus der Funktion nicht wirklich schlau.

Autor: Harry L. (mysth)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marco D. schrieb:
> Inwiefern muss die Callback Funktion in der main eingebaut werden ? Ich
> werde aus der Funktion nicht wirklich schlau.

In welcher Datei du diese Funktion implementierst ist letztendlich egal. 
Entscheidend ist, daß der Linker die findet.
Es gibt im HAL bereits eine leere Funktion mit diesem Namen, die als 
"weak" deklariert ist, und die überschreibst du so.

Autor: Marco D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Funktion macht scheinbar nicht wirklich was. Ich weiß daher nicht 
was ich mit genau dieser soll wenn ich sie sowiso überschreiben soll.

Autor: Harry L. (mysth)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marco D. schrieb:
> Die Funktion macht scheinbar nicht wirklich was. Ich weiß daher
> nicht
> was ich mit genau dieser soll wenn ich sie sowiso überschreiben soll.

Die Funktion wird aus der Interrupt-Routine heraus aufgerufen und 
bekommt das Handle der Schnittstelle übergeben.
So kommst du an die empfangenen Daten.

Weiter verarbeiten mußt du diese Daten natürlich selber.

Autor: Marco D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kann es sein das es eher an Docklight liegt und ich, wenn ich eine 
Sequenz sende nur einzelne chars mit entsprechendem Nullterminator nach 
jedem Zeichen versende ?

Autor: Harry L. (mysth)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marco D. schrieb:
> Kann es sein das es eher an Docklight liegt und ich, wenn ich eine
> Sequenz sende nur einzelne chars mit entsprechendem Nullterminator nach
> jedem Zeichen versende ?

Nein!
Wenn du die Interrupt-gestützten Funktionen nutzen willst, brauchst du 
zwingend die zugehörige Callback-Funktion.

Woher willst du sonst wissen, daß etwas empfangen wurde?

Autor: Kaj (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
char docklight[10];

int main(void){
    while(1){
        HAL_UART_Receive_IT(&huart3,&docklight,strlen(docklight));

strlen gibt 0 zurueck, da dein Array global ist und damit automatisch 
mit 0 initialisiert wird. Waere das Array nicht global (und nicht 
initialisiert), wuerde strlen zaehlen, bis im Speicher irgendwann mal 
eine 0 steht.
Willst du das? Oder wolltest du eher sizeof nehmen?
#include <iostream>
#include <string.h>

using namespace std;

char docklight[10];

int main(void)
{
  cout << "strlen is: " <<  strlen(docklight) << endl;
  cout << "sizeof is: " <<  sizeof(docklight) << endl;

  return 0;

}
$ c++ -Wall -Wextra -o main main.cpp && ./main
strlen is: 0
sizeof is: 10

Autor: Marco D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn ich die Funktion HAL_UART_RxCpltCallback(...) in der main aufrufe 
wird doch nicht die ISR dadurch aufgerufen. Ich blicke überhaupt nicht 
mehr durch. Mit Handle meinst du das &huart3 welches ich übergebe ? wie 
soll ich daraus die empfangenen Daten herausbekommen ? Ich müsste ja auf 
die Variable docklight zugreifen können um mit den Daten was anzufangen 
was ich aber nicht kann.

Autor: Harry L. (mysth)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marco D. schrieb:
> Wenn ich die Funktion HAL_UART_RxCpltCallback(...) in der main aufrufe
> wird doch nicht die ISR dadurch aufgerufen. Ich blicke überhaupt nicht
> mehr durch. Mit Handle meinst du das &huart3 welches ich übergebe ? wie
> soll ich daraus die empfangenen Daten herausbekommen ? Ich müsste ja auf
> die Variable docklight zugreifen können um mit den Daten was anzufangen
> was ich aber nicht kann.

Du rufst die Callback-Funktion NIEMALS selber auf!

Die wird aufgerufen, sobald 1 Zeichen über die UART empfangen wird.

Und Ja!

huartx ist das Handle.

Die erste Aktion in dem Callback ist es, zu überprüfen ob das Handle zu 
deiner gewünschten UART gehört.

Autor: Marco D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also ich hab jetzt nochmal nachgeschaut wie die Interrupt Funktion 
arbeitet. Schein, was ich nicht wusste, wird die Callback Funktion 
aufgerufen, nachdem der Buffer für die empfangenen Dateien leer ist. 
Also muss ich doch nicht die Callback Funktion in der main aufrufen 
sondern sie da nur überschreiben oder ? Mich stört nur das in 
stm32f7xx_hal_uart.c darauf hingewiesen wird, dass 
HAL_UART_RxCpltCallback möglichst nicht verändert werden sollte.

Autor: Harry L. (mysth)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marco D. schrieb:
> Mich stört nur das in
> stm32f7xx_hal_uart.c darauf hingewiesen wird, dass
> HAL_UART_RxCpltCallback möglichst nicht verändert werden sollte.

Natürlich nicht!
Du sollst ja auch deine eigene Funktion schreiben.

Autor: Marco D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie albern ist das denn ! Ich will nur ganz simpel einen String einlesen 
und muss dafür eine eigene Funktion schrieben ?!

Autor: Harry L. (mysth)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du willst Interrupts nutzen und dazu gehört nun mal eine 
Callback-Funktion.

Offenbar fehlen dir sämtliche grundlagen dazu.
Fang erstmal ohne Interrupt an und nutz HAL_UART_Receive()! (ohne _IT)

Dann musst du allerdings warten bis ein Zeichen empfangen wurde.

Autor: Marco D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich möchte einfach nur einen String empfangen und, wenn dieser dann 
eingelesen wurde, mit diesem String weiterarbeiten. Es kann doch nicht 
sein, dass das so kompliziert ist. Die Interrupt Funktion habe ich nur 
verwendet weil ich nicht wusste wie ich sonst dafür sorge, dass nach 
einmaligem senden der Daten im main programm der empfangene String 
weiterverarbeitet werden kann. Es geht auch sicherlich dann noch einfach 
ohne, nur weiß ich nicht wie. Ich wäre für jeden Hinweis sehr dankbar. 
Es kann mir aber doch niemand erzählen das ich für so einen Generalfall 
eine eigene Funktion schreiben muss !

Autor: Marco D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es war auch mein Plan jetzt nur die reguläre FUnktion ohne Interrupt zu 
verwenden. Nur hab ich da keine Ahnung wie ich in der while(1)-Schleife 
dafür sorge, dass nur einmal etwas versendet wird. Packe ich da die 
Transmit-Funktion in die Callback-Funktion ? Da wiederum kann ich aber 
nicht auf meinen empfangenen String zugreifen weshalb das nicht geht.

Autor: Harry L. (mysth)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Fang mal hiermit an!
Sonst wird das nix....

STM32Cube basics MOOC with hands-on exercises:
https://www.youtube.com/playlist?list=PLnMKNibPkDnGtuIl5v0CvC81Am7SKpj02

Moving from 8 to 32 bits workshop - first steps in STM32:
https://www.youtube.com/playlist?list=PLnMKNibPkDnHXgWV0h36LQDGrEuT2r5I4


Danach gehts dann hier weiter:
https://www.st.com/content/st_com/en/support/learning/stm32-education/stm32-moocs.html

Autor: Marco D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also ich hab jetzt den Code auf folgendes reduziert:

int main(void){

char daten[10] = "Hallo\n";

  while(1){

      HAL_UART_Receive(&huart3,(uint8_t*)daten,strlen(daten),100);
      HAL_UART_Transmit(&huart3,(uint8_t*)daten,strlen(daten),100);
      HAL_Delay(500);
}

Ich bekomme auch mit dem "normalen" Receive nur 1 Bit geladen. D.h. wenn 
ich etwas einlese, dann ändert sich nur das 1. Bit. Ich benutze nur 
default settings und hab ein komplett neues Projekt erstellt. Ich habe 
einzig die Baudrate vom Uart3 auf 9600 gestellt damit ich etwas über 
meinen USB Port versenden kann.

Habe auch schon etliche Tutorials gesehn und die machens auch nicht 
anders.

Autor: Marco D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also ich brauche für meinen Zweck definitiv die Interrupt Funktion. Ich 
bekomme es mittlerweile hin Daten mit festgelegter Größe zu empfangen. 
Ich habe aber keine Ahnung wie ich eine Routine schreibe, die mir Daten 
variabler Länge einliest. Ich habe am Schluss der Nachricht einen 
Nullterminator womit ich es schaffen sollte variable Längen einzulesen. 
Hat jemand einen Tipp für mich wie ich da vorgehe ?

Autor: Marco D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich müsste meiner Meinung nach irgendwie auf den Pointer, der auf das 
eingelesene Zeichen zeigt zugreifen und abfragen ob dieser == "NULL" 
ist. Sehe ich das richtig ?

Autor: pegel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe eine Variante die Text von der seriellen Schnittstelle einliest 
und nach Eingabe von New Line (0x0a) den gleichen Text in 
Grossbuchstaben gewandelt wieder zurück sendet.

Interesse?

Autor: gdfgsdgsg (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
eine callback ist eine "rückruf-funktion " ..
sie wird irgendwo anders aufgerufen ...

frei nach dem motto: wenn was passiert sag mir bescheid!!!
bescheidgesagt wird hier über diese funktion ...


wenn du also deine :

volatile uint8_t IstWasPassiert  =  0;

char rxString[100];
uint8_t rxCount;


int main(){

   uint8_t byte ;
   HAL_UART_Receive_IT(&huart3, &byte , 1 );

   while(1)
   {
      if( IstWasPassiert == 1 )
      {
          IstWasPassiert  =  0;
          printf( rxString );
      }
   }
}



void HAL_UART_RxCpltCallback( uint8_t byte )
{
   // jedes zeichen was emfangen wird landet hier nacheinander drin !!! 
   rxSring[rxCount] = byte;
   rxCount++;

   if( byte == '\n' )
   {  
       rxCount = 0;
       IstWasPassiert  =  1;
   }

   if( rxCount > 100 )
       rxCount  = 0;

}


Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.