Forum: Compiler & IDEs String mit Uart empfangen


von Steffen (Gast)


Lesenswert?

Hallo zusammen,

ich versuche gerade eine ATMega32 mit C zu programmieren. Mein Problem 
ist nun, dass ich vom Hyperterminal einen string an den Controller 
senden will. Die Eingabe sollte mit der Entertaste beendet werden.
Ich habe mir nun folgendes überlegt:

unsigned char empfangen()
{
  unsigned char receive;
  int i=0;
  while (uart_getc_wait()!=10)
  {
    receive[i]=UDR;
    i++;
  }
  receive[i]='\0';
return receive;

Diesen String möchte ich dann später wieder auf dem Hyperterminal 
ausgeben. Dafür habe ich schon eine Funktion geschrieben die mir beim 
Aufruf senden_string("test"); test auf dem Bildschirm ausgibt.
Leider funktioniert die Sache mit dem empfangen nicht richtig.
Sieht jemand meinen Fehler, oder kann mir jemand einen Code-Vorschlag 
schicken´, mit dem ich nicht nur einzelne Zeichen empfangen kann??
Danke schonmal für die Hilfe!

von remote1 (Gast)


Lesenswert?

hab so was auch schon mal gemacht:
1
#ifndef F_CPU
2
#define F_CPU 16000000           
3
#endif
4
5
#define BAUD 19200UL //Baudrate 
6
#define UBRR_BAUD   ((F_CPU/(16UL*BAUD))-1)
7
8
9
#define length 10  //Textlänge ab welcher Empfangen beendet wird
10
11
12
void uart_init(void)
13
{
14
  UCSRB = (1<<RXEN)|(1<<TXEN);      // UART TX und RX einschalten    
15
    UCSRC |= ( 1 << URSEL )|( 3<<UCSZ0 );  // Asynchron 8N1
16
  //nur für 19200 Baud bei 16MHz für Mega32
17
    UBRRH  = 0;                             // Highbyte ist 0
18
    UBRRL  = 51;                            // Lowbyte ist 51
19
  
20
  //falls anderer Mega bzw andere Frequenz
21
  //UBRRH = (uint8_t) (UBRR_BAUD>>8);
22
    //UBRRL = (uint8_t) (UBRR_BAUD & 0x0ff);
23
} 
24
25
void uart_putc(unsigned char c)
26
{
27
    while (!(UCSRA & (1<<UDRE))) {}  // warten bis Senden möglich 
28
    UDR = c;                         // Zeichen senden   
29
}
30
31
uint8_t uart_getc(void)
32
{
33
    while (!(UCSRA & (1<<RXC))) {} // warten bis Zeichen verfügbar
34
    return UDR;                    // Zeichen aus UDR zurueckgeben
35
}
36
37
38
int main (void)
39
{
40
  uint8_t i;
41
  char c;
42
  char* s;
43
  char string[length+1];
44
45
  uart_init();
46
  
47
while(1)
48
{    
49
  
50
  s=string;
51
  i=0;
52
53
  do
54
  {
55
    c=uart_getc();    
56
    if (c!='\r') 
57
     {
58
      *s=c;      
59
      uart_putc( c );    
60
    s++;
61
    i++;
62
     }       
63
  }
64
  while( i!=length && c!='\r');  
65
  *s=0;
66
67
  uart_puts( "\r\n" );
68
  uart_puts( "eingegebender Text: " );
69
  uart_puts( string );
70
  while (!(UCSRA & (1<<UDRE))) {}
71
    uart_puts( "\r\n" );
72
73
}
74
return (0);
75
}

von remote1 (Gast)


Lesenswert?

im prinzip empfange ich solange einen char vom PC, bis die maximallänge 
erreicht ist bzw. bis ein ENTER kommt
die einzelnen char werden dann noch in ein string zusammengepack

von Steffen (Gast)


Lesenswert?

Das trifft es schon fast! Danke!
Nur würde ich gern den Teil vom Empfangen in einer Funktion schreiben.
Habs gerade mal probiert, jetzt ergibt sich für mich allerdings das 
Problem mit der Rückgabe.... Was muss ich nun zurückgeben und wie lautet 
der Funktionsaufruf dafür dann?

von Steffen (Gast)


Lesenswert?

Hat sich erledigt. Ich habs hinbekommen!
Danke für die Hilfe!

von remote1 (Gast)


Lesenswert?

kannst ja mal deine Lösung noch mal posten, damit auch andere davon 
profitieren können

von Steffen (Gast)


Lesenswert?

Kein Problem! Hab noch eine Funktion zum empfangen von Integer Zahlen 
hinzugefügt.

//Empfangen

int input_int()
{
  int zahl;
  char zahl_c[100];

  zahl=atoi(input_string());
  return zahl;
}


 char input_string()
{
  char string[100];
  int i=0;
  char c;
  do
  {
    c=uart_getc_wait();
    if (c!='\r')
     {
         string[i]=c;
      i++;
     }
  }
  while(i!=99 && c!='\r');
  string[i]='\0';

  return string;
}

unsigned char uart_getc_wait()
{
    // Warten, bis etwas empfangen wird
    while (!(UCSRA & (1 << RXC)));
    // Das empfangene Zeichen zurückliefern
    return UDR;
}

von Karl H. (kbuchegg)


Lesenswert?

> char input_string()
> {
>   char string[100];
>   int i=0;
>   char c;
>   do
>   {
>     c=uart_getc_wait();
>     if (c!='\r')
>      {
>           string[i]=c;
>       i++;
>      }
>    }
>   while(i!=99 && c!='\r');
>   string[i]='\0';
>
>   return string;
> }

Da fehler erst einmal ein * in der Funktionsdefinition

char* input_string()
{
  ...
}

In input_int braucht wohl niemand das Array zahl_c.
Das verbraucht nur Speicherplatz bzw., wenn der Optimizer
es rausschmeist, verwirrt nur.

Aber, das ist alles Kleinkram. Das folgende aber nicht:
Deine input_string() Funktion ist fehlerhaft:
Du gibst einen Pointer auf eine lokale Variable zurück.
Das darfst du aber nicht. Lokale Variablen werden beim
verlassen einer Funktion zerstört. Damit gibst du aber
dem Aufrufer der Funktion einen Pointer zurück, der
auf Speicher zeigt, der schon längst freigegeben wurde ->
auf einem System, dass den Speicher etwas riguroser überwacht,
kann es durchaus passieren, dass sich dann das Betriebssystem
einschaltet und deinem Programm auf die Finger klopft.
Aber auch auf einem AVR ohne Betriebssystem ist das nach
wie vor fehlerhaft. In deiner Verwendung in input_int()
wird das zwar funktionieren, allerdings nur zufällig.
* Wenn atoi ein bischen mehr auf dem Stack allokiert, hast
  du ein Problem.
* Wenn zwischen dem Empfang des Pointers beim Aufrufer und
  der Verwendung der Daten auf die der Pointer zeigt noch
  weitere Funktionsaufrufe liegen, die lokale Variablen
  anlegen, hast du ein Problem.

Gib niemals die Adresse einer lokalen Variablen aus einer
Funktion zurück! Das ist in C-speak: undefined behaviour.
Alles Mögliche kann und wird passieren.

Du hast mehrere Möglichkeiten um das Problem zu umgehen:
* allokiere den 100-Zeichen Buffer als globale Variable
* allokiere den 100 Zeichen Buffer zwar innerhalb der
  Funktion, dort aber als static Variable
* Die Funktion input_string() kümmert sich überhaupt nicht
  mehr um den Buffer, sondern verlangt vom Aufrufer, dass
  er einen Buffer (ausreichender Größe) übergibt.
* Du allokierst den Buffer dynamisch innerhalb der Funktion
  (diese Variante ist nur der Vollständigkeit halber aufgeführt,
   sie ist in C selbst auf einem nicht µC System nur eingeschränkt
   zu empfehlen. Das Problem ist: Wer gibt den Speicher wieder
   frei?)

von Steffen (Gast)


Lesenswert?

Danke für die Verbesserung!
Das Programm lief bis jetzt auch so, aber vielleicht wäre es irgendwann 
zu Problemen gekommen und ich hätte wieder ewig nach dem Fehler suchen 
müssen.

>In input_int braucht wohl niemand das Array zahl_c.
>Das verbraucht nur Speicherplatz bzw., wenn der Optimizer
>es rausschmeist, verwirrt nur.

Der Array war nur ausversehen noch drin, hatte vorher eine andere Lösung 
und dafür habe ich ihn gebraucht....

von Karl H. (kbuchegg)


Lesenswert?

Steffen wrote:
> Danke für die Verbesserung!
> Das Programm lief bis jetzt auch so,

Wie gesagt: Das war ein glücklicher Zufall(*)
und funktioniert nur deswegen, weil input_int so aussieht
wie es aussieht.

Wenn ich zb mache:

int foo( char c )
{
  char tmp[20];
  int i;

  for( i = 0; i < 20; ++i )
    tmp[i] = c;

  return tmp[0] + tmp[9];
}

void bar()
{
   char* Input = input_string();
   foo( 'a' );

   // an dieser Stelle sollte eigentlich bereits der
   // Inhalt von *Input zerstört sein
}

dann wird das zu Problemen führen. Die tatsächliche
Funktionalität von foo() spielt keine Rolle, die Hauptsache
sie legt ein paar lokale Variablen an. Auch wenn das vom
C-Standard nirgends gefordert wird, so ist die Wahscheinlich-
keit sehr hoch, dass diese Variablen genau in dem Speicher
erzeugt werden, der einen Moment vorher noch die bewusste
lokale Variable 'string' in input_string() gehalten hat.
Und damit zerstört dir jeder Schreibzugriff die Daten, auf
die du einen Pointer von input_string() bekommen hast.


(*) glücklicher Zufall
eigentlich ist das kein glücklicher Zufall, sondern ein sehr
schlechter Zufall. Wenn du Glück gehabt hättest, dann wäre das
Programm sofort abgestürzt und du hättest mitbekommen, dass es
da ein Problem gibt. So hast du Pech und alles schaut so aus,
als ob es in Ordnung wäre.

von Phillip H. (philharmony)


Lesenswert?

Auch wenn der Post schon was älter ist, ich bin grade an etwas ählichem:
Ich wollte dazu einfach die Funktion aus dem Manual benutzen:

while(!(UCSRA&(1<<RXC)));
return UDR;

Was mir dabei nicht so ganz klar ist: Was genau macht "return UDR"?
Wenn ich das, was ich empfange jetzt zb auf den LEDs (meintewegen Port 
B) anzeigen lassen will, wie komme ich dann an die Daten ran?
"PORTB=UDR"?

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.