Forum: Mikrocontroller und Digitale Elektronik UART Rx Bits


von Bernd S. (mms)


Lesenswert?

Hallo,

ich hab gerade ein Problem mit dem Xmodem Protokoll und der usart 
schnittstelle vom AT91Rm9200 controller.

Wenn ich versuche Daten vom Hyperterminal via Xmodem an den Controller 
zu schicken, erhalte ich das SOH Bit allerdings keine weiteren Bits 
mehr.

Die einzelnen Bits / Charakter lese ich folgendermaßen aus:
1
char cn; 
2
3
cn = AT91F_Rx_Char (); 
4
// if(cn == SOH) -> dann nächstes zeichen auslesen
5
6
//entspricht paket-number
7
cn = AT91F_Rx_Char (); 
8
9
//usw

1
char AT91F_Rx_Char (void)
2
{
3
  if (AT91F_US_RxReady((AT91PS_USART)AT91C_BASE_DBGU)) 
4
  {
5
    return((char)AT91F_US_GetChar((AT91PS_USART)AT91C_BASE_DBGU));
6
  }
7
8
  return(0); 
9
}


AT91F_US_RxReady gibt als Rückgabewerte 1 zurück, wenn ein Charakter 
ausgelesen werden kann aus dem US_RHR register. Muss man nach dem ersten 
Lesen, irgendwelche Register zurücksetzen?

Gruß
Bernd

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Du solltest näher auf den ersten Codeteil eingehen. Die drei Zeilen sind 
für eine Diagnose zu wenig.

Generell finde ich die Funktion AT91F_Rx_Char() unglücklich geschrieben, 
weil man nicht zwischen einem empfangenen <NUL> und "kein Zeichen 
ausgelesen" unterscheiden kann.

Man könnte das verbessern, indem man den Rückgabewert auf int erweitert 
und z.B. -1 für "kein Zeichen ausgelesen" verwendet (andere negative 
Zahlen entsprechend für andere Fehler) und legale Zeichen 0-255 im 
Lowbyte zurück gibt.

Weitere Probleme können auftreten, weil AT91F_Rx_Char() keine wartende 
Funktion ist. um darauf genauer einzugehen, bräuchte man mehr Einblick 
in den Auswertecode (s.o.)

von Bernd S. (mms)


Lesenswert?

>Generell finde ich die Funktion AT91F_Rx_Char() unglücklich geschrieben,
>weil man nicht zwischen einem empfangenen <NUL> und "kein Zeichen
>ausgelesen" unterscheiden kann.

das ist genau das problem. Hab mir grad mal eine for-schleife 
geschrieben, die solange nicht verlassen wird bis ein Zeichen ungleich 
0x00 ankommt. Und es handelt sich tatsächlich um das 0x01, welches ich 
auch erwarte.

>Weitere Probleme können auftreten, weil AT91F_Rx_Char() keine wartende
>Funktion ist

hab mich hier zu sehr vom datenblatt verleiten lassen, indem steht:

"RXRDY - receiver ready: -> wenn 1 => At least one complete character 
has been received and US_RHR has not yet been read"

-> ich bräuchte ein Register, welches wirklich nur dann seinen Wert 
ändert, wenn neue Daten / ungleich NULL angekommen sind.


>Weitere Probleme können auftreten, weil AT91F_Rx_Char() keine wartende
>Funktion ist. um darauf genauer einzugehen, bräuchte man mehr Einblick
>in den Auswertecode (s.o.)

Im Prinzip ist mein kleiner Code-Abriss oben genau das was ich mache.
1
void Xmodem_Start(unsigned char *ptr)
2
{
3
  unsigned char cn;
4
 
5
  for(;;)
6
  {
7
     cn = AT91F_Rx_Char ();
8
9
     if(cn == SOH)
10
        break;       //SOH angekommen 
11
  }
12
13
  //nächstes zeichen: paket number
14
  cn = AT91F_Rx_Char ();
15
16
  //paket number inverse auslesen
17
  cn = AT91F_Rx_Char ();
18
19
  //.... usw 
20
 
21
  //anschließend daten auslesen von xmodem 
22
  for(i=0; i<128; i++)
23
  {
24
     *ptr++ = AT91F_Rx_Char ();
25
 
26
  } 
27
28
29
  //checksum überprüfen 
30
}

Im Prinzip suche ich jetzt nach einer Möglichkeit um erst die Funktion 
AT91F_Rx_Char() aufzurufen, wenn wirklich neue Daten angekommen sind und 
keine NULL-Bytes.

Gruß
Bernd

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Du bekommst keine weiteren Bytes nach dem SOH, weil deine 
Einlesefunktion nicht auf Bytes wartet und im Verhältnix zu den 
reinkommenden Daten viel zu schnell ist. Du bekommst quasi immer ein 
Nullbyte als Resultat dafür, dass noch oder gerade keine Zeichen 
empfangen wurden.

Du hast mehrere sich gegenseitig ausschliessende Möglichkeiten...

1/ Forme AT91F_Rx_Char() generell in eine wartende Funktion um.

char AT91F_Rx_Char (void)
{
  while(1)
    if (AT91F_US_RxReady((AT91PS_USART)AT91C_BASE_DBGU))
    {
      return((char)AT91F_US_GetChar((AT91PS_USART)AT91C_BASE_DBGU));
    }
}

Diese Lösung kann ich nicht wirklich empfehlen, weil es komplett 
blockiert, wenn erwartete Zeichen ausbleiben.

2/ Behalte AT91F_Rx_Char() als nicht wartende Funktion, aber ändere den 
Rückgabewert, so dass du Fehler (kein Zeichen) erkennen kannst.

Damit kannst du je nach Programmablauf eine wartende Funktion oder eine 
nicht-wartende Funktion daraus machen.

Die Funktion kann dafür einen Datentyp zurückgeben, der char (oder 
unsigned char je nach Geschmack) zurückgeben kann plus den Fehlercode 
(s.o), also z.B. int.

Es geht auch, wenn man mit weiteren Argumenten arbeitet.

char AT91F_Rx_Char (char *err)
{
  if (AT91F_US_RxReady((AT91PS_USART)AT91C_BASE_DBGU))
  {
    *err = 0;
    return (char) AT91F_US_GetChar((AT91PS_USART)AT91C_BASE_DBGU);
  }

  *err = 1;
  return 0 ;
}

Im Programm selbst prüfst du die Fehlerbedingung ab und reagierst 
darauf.

  char err;
  do
  {
    //nächstes zeichen: paket number
    cn = AT91F_Rx_Char (&err);
  } while (err); // AUF ZEICHEN WARTEN!

Man kann auch Rückgabewert und Argument tauschen. Man findet das üfters 
in Bibliotheken:

char AT91F_Rx_Char (char *c)
{
  if (c && AT91F_US_RxReady((AT91PS_USART)AT91C_BASE_DBGU))
  {
    *c = AT91F_US_GetChar((AT91PS_USART)AT91C_BASE_DBGU);
    return ZEICHEN_DA;
  }

  // *c unverändert lassen
  return KEIN_ZEICHEN_DA;
}

  char cn;
  while ( AT91F_Rx_Char (&cn) == KEIN_ZEICHEN_DA )
    ; // AUF ZEICHEN WARTEN!

  // hier liegt ein gültiges cn vor.

Das blockiert aber auch, wenn den Sender mitten im Protokoll nichts mehr 
schickt.

Günstiger finde ich es daher, das Protokoll in einer state machine zu 
implementieren, die Zustände für Warten und Timeout kennt.

von Bernd S. (mms)


Lesenswert?

vielen dank für deine erklärungen. Polling ist für mich hier völlig 
ausreichend, da der Processor währenddessen eh nichts zu tun hat. 
Ansonsten ist natürlich eine Interrupt-Routine besser.

werd meine funktion entsprechend umformen.

Gruß
Bernd

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.