stm32 HAL UART Receive_IT(.) sendet nur 1 Byte

Gast #5699639
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.
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
Gast
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.
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.
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.
Gast
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 ?
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?
Gast
1
char docklight[10];
2

3
int main(void){
4
    while(1){
5
        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?
1
#include <iostream>
2
#include <string.h>
3

4
using namespace std;
5

6
char docklight[10];
7

8
int main(void)
9
{
10
  cout << "strlen is: " <<  strlen(docklight) << endl;
11
  cout << "sizeof is: " <<  sizeof(docklight) << endl;
12

13
  return 0;
14

15
}
1
$ c++ -Wall -Wextra -o main main.cpp && ./main
2
strlen is: 0
3
sizeof is: 10
Gast
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.
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.
Gast
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.
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.
Gast
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 !
Gast
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.
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
Gast
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.
Gast
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 ?
Gast
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 ?
Gast
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?
Gast
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 :
1
volatile uint8_t IstWasPassiert  =  0;
2

3
char rxString[100];
4
uint8_t rxCount;
5

6

7
int main(){
8

9
   uint8_t byte ;
10
   HAL_UART_Receive_IT(&huart3, &byte , 1 );
11

12
   while(1)
13
   {
14
      if( IstWasPassiert == 1 )
15
      {
16
          IstWasPassiert  =  0;
17
          printf( rxString );
18
      }
19
   }
20
}
21

22

23

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

30
   if( byte == '\n' )
31
   {  
32
       rxCount = 0;
33
       IstWasPassiert  =  1;
34
   }
35

36
   if( rxCount > 100 )
37
       rxCount  = 0;
38

39
}

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 7 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.