Forum: Compiler & IDEs UART empfang, immer ein zeichen "zu spät"


von steckdose (Gast)


Lesenswert?

Ich beginne gerade mit der C-Programmierung mit AVRs und habe mir aus 
einigen Beispielen etwas zusammengebastelt, was mir per UART ein Zeichen 
empfangen soll und dann wieder per UART und auf einem HD44780-Display 
ausgeben soll, mit einem mega16.
Mein Compiler ist der avr-gcc 4.2.2, als IDE hab' ich das AVR-Studio und 
den Code schieb ich mit Ponyprog auf den Controller.

Wenn ich jetzt z.B. im Terminal ABCDEFG eintippe, dann wird bei einem 
Tastendruck immer nur das letzte Zeichen angezeigt. Also, wenn ich noch 
gar nichts gedrückt habe und drücke A dann zeigt es nichts an (Display 
und Terminal), drücke ich dann B, dann zeigt es A an, drücke ich dann C, 
dann zeigt es B an uswuswusw....

Zum Ansteuern der Displays habe ich Peter Fleury's lcdlib verwendet. Die 
lcd.h stelle ich jetzt mal nicht rein, da das Display ja soweit 
funktioniert, falls ihr sie doch sehen wollt, kann ich sie ja mit 
reinstellen.

Hier der Code, ich hoffe ihr könnt mir da helfen, ich hab mir schon 'ne 
halbe ewigkeit den Kopf darüber zerbrochen:
1
#define F_CPU 8000000L
2
#define BAUD 9600L
3
#define UBRR_VAL ((F_CPU+BAUD * 8)/(BAUD*16)-1)      //clever runde
4
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))         //reale Baudrate
5
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000)      //Fehler in Promille
6
#if ((BAUD_ERROR>10)||(BAUD_ERROR<-10))
7
#error Systematischer Fehler in der Baudrate größer 1% und damit zu hoch!
8
#endif
9
10
#include "lcd.h"
11
#include <avr/io.h>
12
#include <stdlib.h>
13
#include <inttypes.h>
14
#include <util/delay.h>
15
#include <avr/pgmspace.h>
16
17
int uart_putc(unsigned char c) {
18
    while (!(UCSRA & (1<<UDRE))){}// warten bis Senden moeglich
19
  UDR = c;                      // sende Zeichen
20
  _delay_ms(10);
21
  return(0);
22
}
23
24
25
26
void uart_puts (char *s) {
27
    while (*s) {   
28
      uart_putc(*s);
29
        s++;
30
    }
31
}
32
33
int init_uart(void) {
34
  UBRRH = UBRR_VAL >> 8;
35
  UBRRL = UBRR_VAL & 0xFF;
36
  UCSRB = (1<<RXEN)|(1<<TXEN);      //UART TX einschalten
37
  UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0);      //Asynchron 8N1
38
  UCSRB |= ( 1 << RXEN ); // UART RX einschalten
39
  return(0);
40
}
41
42
43
int main(void) {
44
45
  lcd_init(LCD_DISP_ON);
46
    lcd_clrscr();
47
    
48
  init_uart();
49
    uart_puts("blubb\r\n\r\n");
50
  
51
  while(1) {
52
      while (!(UCSRA & (1<<RXC)))   // warten bis Zeichen verfuegbar
53
          ;
54
55
      if(UDR == 0x0D) {     // \r\n am uart senden, wenn zeichen = ENTER-Taste
56
        uart_puts("\r\n");
57
      }
58
      else {
59
        uart_putc(UDR);
60
        if( (UDR >= 0x61 && UDR <= 0x7A) || (UDR >= 0x41 && UDR <= 0x5A) || (UDR >= 0x30 && UDR <= 0x39) || (UDR == 0x20) ) {
61
          //    nur aufs display ausgeben wenn das zeichen eines von denen ist
62
          //       a      bis      z    ODER        A     bis     Z     ODER        0      bis     9   ODER   <SPACE>
63
          lcd_putc(UDR);
64
        }
65
    //  }  
66
    }
67
  }
68
69
70
   return 0;                     
71
}



Schonmal vielen Dank im Vorraus!

von Matthias L. (Gast)


Lesenswert?

1
      while (!(UCSRA & (1<<RXC)))   // warten bis Zeichen verfuegbar
2
          ;
3
4
     received = UDR;       // Zeichen abholen
5
      if(received == 0x0D) {     // \r\n am uart senden, wenn zeichen = ENTER-Taste
6
        uart_puts("\r\n");
7
      }
8
      else {
9
        uart_putc(received );
10
        if(   (received >= 0x61 && received <= 0x7A) 
11
           || (received >= 0x41 && received <= 0x5A) 
12
           || (received >= 0x30 && received <= 0x39) 
13
           || (received == 0x20)                       ) 
14
        {   
15
          lcd_putc(received );
16
        }
17
    //  }  
18
    }
19
  }

Versuchs mal so. DU solltest das UDR Register nur dann lesen, wenn ein 
Zeichen empfangen wurde. Ich weiß nicht, was passsiert,w enn du das 
immer wieder abholst

von Falk B. (falk)


Lesenswert?

@ steckdose (Gast)

>und Terminal), drücke ich dann B, dann zeigt es A an, drücke ich dann C,
>dann zeigt es B an uswuswusw....

>int uart_putc(unsigned char c) {
>    while (!(UCSRA & (1<<UDRE))){}// warten bis Senden moeglich
>  UDR = c;                      // sende Zeichen
>  _delay_ms(10);

Was soll das hier?

>  return(0);
>}

Die Funktion braucht keinen Rückgabewert.

Warum baust du dir nicht equivalent ein getc()?

Aber jetzt kommt das Problem.

>  while(1) {
>      while (!(UCSRA & (1<<RXC)))   // warten bis Zeichen verfuegbar
>          ;

Kann man machen, besser ist aber eine Funktion ala getc();

>      if(UDR == 0x0D) {     // \r\n am uart senden, wenn zeichen = ENTER-Taste

Ist hier prinzipielle auch noch richtig, ABER . . .

>        uart_puts("\r\n");
>      }
>      else {
>        uart_putc(UDR);

DAS geht mal fix schief! Auf UDR darf man nur EINMAL lesend zugreifen, 
sonst bringt man mal fix den UART durcheinander. Man muss UDR in eine 
Variable kopieren, damit kann man dan machen was man will. Ergo. getc 
ist dirngend notwendig.

1
char getc(void) {
2
   while (!(UCSRA & (1<<RXC)));
3
   return UDR;
4
}

Wenn man dann schreibt

1
 char tmp;
2
 while(1) {
3
      tmp = getc();
4
5
      if(tmp == 0x0D) {     // \r\n am uart senden, wenn zeichen = ENTER-Taste
6
        uart_puts("\r\n");
7
      }
8
      else {
9
        uart_putc(tmp);
10
        if( (tmp >= 0x61 && tmp <= 0x7A) || (tmp >= 0x41 && tmp <= 0x5A) || (tmp >= 0x30 && tmp <= 0x39) || (tmp == 0x20) ) {
11
          //    nur aufs display ausgeben wenn das zeichen eines von denen ist
12
          //       a      bis      z    ODER        A     bis     Z     ODER        0      bis     9   ODER   <SPACE>
13
          lcd_putc(tmp);
14
        }
15
    //  }  
16
    }
17
  }

sollte es passen.

MFG
Falk

von steckdose (Gast)


Lesenswert?

Ahhh vielen Dank :)

Nun hab' ich noch eine Frage, ein "Buchstabe" (der AVR hat das ja als 
Hex/Bin in seinem Speicher) hat ja kein Vorzeichen. Es funktioniert 
beide Male, wenn ich received als char oder als unsigned char definiere. 
Was macht da mehr Sinn?

von Sven P. (Gast)


Lesenswert?

unsigned char macht mehr Sinn. Ist aber oft egal, weil der Compiler 
automatisch annimmt, dass du mit "char" eigentlich ein vorzeichenloses 
Dingchen meinst.

Informier dich mal über die Optionen von GCC:

-fsigned-char
-funsigned-char

von Falk B. (falk)


Lesenswert?

@ steckdose (Gast)

>Nun hab' ich noch eine Frage, ein "Buchstabe" (der AVR hat das ja als
>Hex/Bin in seinem Speicher) hat ja kein Vorzeichen. Es funktioniert
>beide Male, wenn ich received als char oder als unsigned char definiere.
>Was macht da mehr Sinn?

Ist Jacke wie Hose. Eigentlich wäre unsigned logisch.

MfG
Falk

von Karl H. (kbuchegg)


Lesenswert?

Was du dir aber undebingt angewöhnen sollst: Schmeiss dir
nicht selbst Prügel zwischen die Beine.

Es gibt keinen Grund, da mit ASCII Codes um sich zu schmeissen.
Deine Abfrage kann man auch so schreiben
1
        if( (tmp >= 'a' && tmp <= 'z') ||
2
            (tmp >= 'A' && tmp <= 'Z') ||
3
            (tmp >= '0' && tmp <= '9') ||
4
            (tmp == ' ') ) {

Hier kann man ganz deutlich sehen, wie dein ursprünglicher
Kommentar sich in Code verwandelt hat und damit überflüssig
geworden ist (was an sich gut ist: Der beste Kommentar ist
einer den man weglassen kann)

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.