Forum: Mikrocontroller und Digitale Elektronik Frage zu Peter Fleurys UART Lib


von Klaus (Gast)


Lesenswert?

Guten Morgen Forum,
ich hätte da ein paar Anfängerfrage zu Peter Fleurys UART Lib. Ich stehe 
da derzeitg auf dem Schlauch. Wäre nett, wenn mir hierbei jemand 
weiterhelfen könnte. Grüße

1) Meine erste Frage wäre, warum man eigentlich lastRxError verwendet 
bzw. einsetzt wird. FE ist ja ein Fehler des Empfangsrahmen und DOR der 
Empfängerüberlauf. Ist das wirklich nötig?


 /* read UART status register and UART data register */
    usr  = UART0_STATUS; // UCSR0A
    data = UART0_DATA;   // UDR0

    /* */
#if defined( AT90_UART )
    lastRxError = (usr & (_BV(FE)|_BV(DOR)) );
#elif defined( ATMEGA_USART )
    lastRxError = (usr & (_BV(FE)|_BV(DOR)) );
#elif defined( ATMEGA_USART0 )
    lastRxError = (usr & (_BV(FE0)|_BV(DOR0)) );
#elif defined ( ATMEGA_UART )
    lastRxError = (usr & (_BV(FE)|_BV(DOR)) );
#endif



2) Im Empfangs-Interrupt ist weder tmptail noch noch UART_TxTail 
initialisiert. Zudem verstehe ich nicht ganz warum noch ein Bit -And mit 
UART_RX_BUFFER_MASK (Buffer_Size -1) bei

    tmphead = ( UART1_RxHead + 1) & UART_RX_BUFFER_MASK;

verwendet wird. Ich verstehe hier nicht ganz die Vorgehensweise und 
warum die Buffergröße 2^x sein muss.



3) Last but not least wäre da noch die Sache mit dem Rechts-shift bei

    lastRxError = UART_BUFFER_OVERFLOW >> 8;

UART_BUUFER_OVERFLOW wird mit 0x0200 initialisiert, warum? Und weshalb 
der Rechts-shift?


   SIGNAL(UART0_RECEIVE_INTERRUPT)
/*********************************************************************** 
**
Function: UART Receive Complete interrupt
Purpose:  called when the UART has received a character
************************************************************************ 
**/
{
    unsigned char tmphead;
    unsigned char data;
    unsigned char usr;
    unsigned char lastRxError;

    /* read UART status register and UART data register */
    usr  = UART0_STATUS;
    data = UART0_DATA;

    /* */
#if defined( AT90_UART )
    lastRxError = (usr & (_BV(FE)|_BV(DOR)) );
#elif defined( ATMEGA_USART )
    lastRxError = (usr & (_BV(FE)|_BV(DOR)) );
#elif defined( ATMEGA_USART0 )
    lastRxError = (usr & (_BV(FE0)|_BV(DOR0)) );
#elif defined ( ATMEGA_UART )
    lastRxError = (usr & (_BV(FE)|_BV(DOR)) );
#endif

    /* calculate buffer index */
    tmphead = ( UART_RxHead + 1) & UART_RX_BUFFER_MASK;

    if ( tmphead == UART_RxTail ) {
        /* error: receive buffer overflow */
        lastRxError = UART_BUFFER_OVERFLOW >> 8;
    }else{
        /* store new index */
        UART_RxHead = tmphead;
        /* store received data in buffer */
        UART_RxBuf[tmphead] = data;
    }
    UART_LastRxError = lastRxError;
}

von Karl H. (kbuchegg)


Lesenswert?

Klaus schrieb:

> 1) Meine erste Frage wäre, warum man eigentlich lastRxError verwendet
> bzw. einsetzt wird.

Eigentlich sollte man die immer benutzen. Aber wie es eben im Leben so 
ist: Meistens tut man es nicht. Diese Fehler sind selten und da man 
meistens programmtechnisch eh nichts dagegen tun kann, lässt man es 
eben.

> FE ist ja ein Fehler des Empfangsrahmen und DOR der
> Empfängerüberlauf. Ist das wirklich nötig?

Frame Error deutet darauf hin, dass entweder
* bei den Übertragungsparametern keine Einigkeit zwischen Sender
  und Empfänger herrscht
* oder sich die UART beim ersten Zeichen nicht richtig synchronisieren
  konnte

Empfängerüberlauf bedeutet einfach nur, dass dein Programm zu lange 
getrödelt hat und zu lange in einem Interrupt steckte, so dass der 
Receive Interrupt die UART nicht schnell genug leeren konnte.

> 2) Im Empfangs-Interrupt ist weder tmptail noch noch UART_TxTail
> initialisiert.

Dazu müsste man jetzt den kompletten Code sehen.
Aber denk daran: Globale Variablen werden sowieso automatisch mit 0 
initialisiert.

> Zudem verstehe ich nicht ganz warum noch ein Bit -And mit
> UART_RX_BUFFER_MASK (Buffer_Size -1) bei
>
>     tmphead = ( UART1_RxHead + 1) & UART_RX_BUFFER_MASK;
>
> verwendet wird.

Weil er hier einen Ring bauen will:
tmphead soll beispielsweise nacheinander die Werte annehmen
0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, ....
Also im Kreis alle Wert von 0 bis 7 (das sind 8 Werte) und dann wieder 
bei 0 beginne

> Ich verstehe hier nicht ganz die Vorgehensweise und
> warum die Buffergröße 2^x sein muss.

Genau aus dem Grund: Damit er hier ein AND benutzen kann und nicht den 
Rest einer Division bestimmen muss.

> 3) Last but not least wäre da noch die Sache mit dem Rechts-shift bei
>
>     lastRxError = UART_BUFFER_OVERFLOW >> 8;
>
> UART_BUUFER_OVERFLOW wird mit 0x0200 initialisiert, warum?

Weil er das so haben will

> Und weshalb
> der Rechts-shift?

Weil das Ziel als unsigned char keine 16 Bit aufnehmen kann.

Wharscheinlich braucht er die Konstante UART_BUFFER_OVERFLOW nach an 
anderer Stelle im Code, dort aber als 16 Bit Wert.

von Klaus (Gast)


Lesenswert?

Hallo Karl-Heinz,
vielen Dank für deine Antwort!
Ahhh, ok. Bei einer Buffergröße von 8

tmphead = ( UART_RxHead + 1) & UART_RX_BUFFER_MASK;

wäre das dann also 0000 0111 & 0000 0001 = 0000 0001 und so weiter

Vielen Dank!
Gruß

von Klaus (Gast)


Lesenswert?

Hallo,
ich hab mir die Lib von Peter Fleury nochmals zu Gemüte geführt und 
dabei haben sich nochmal ein paar Fragen aufgetan. Es wäre nett, wenn 
mir hierbei jemand weiterhelfen könnte.

Ich habe immer noch Verständnisprobleme bei den Fehlermeldungen...

/*
** high byte error return code of uart_getc()
*/
#define UART_FRAME_ERROR      0x0800              /* Framing Error by 
UART       */
#define UART_OVERRUN_ERROR    0x0400              /* Overrun condition 
by UART   */
#define UART_BUFFER_OVERFLOW  0x0200              /* receive ringbuffer 
overflow */
#define UART_NO_DATA          0x0100              /* no receive data 
available   */


UART_FRAME_ERROR      0x0800        1000 0000 0000 (2048dec)
UART_OVERRUN_ERROR    0x0400        100 0000 0000 (1024dec)
UART_BUFFER_OVERFLOW  0x0200        10 0000 0000 (512dec)
UART_NO_DATA          0x0100        1 0000 0000 (256dec)

In der ISR
wir ja zunächst mal überprüft ob im 7 Bit großen UCSR0A Register ein 
Frame Error oder ein Empfängerüberlauf aufgetreten ist.

    lastRxError = (usr & (_BV(FE0)|_BV(DOR0)) );

Tritt ein Overflow auf...

    if ( tmphead == UART_RxTail ) {
        /* error: receive buffer overflow */
        lastRxError = UART_BUFFER_OVERFLOW >> 8;

wird 10 0000 0000 >> 8 in 00 0000 0010 umgewandelt.


Was in uart_getc vor sich geht verstehe ich nicht so ganz.

unsigned int uart_getc(void)
{
    unsigned char tmptail;
    unsigned char data;


    if ( UART_RxHead == UART_RxTail ) {
        return UART_NO_DATA;   /* no data available */
    }

    /* calculate /store buffer index */
    tmptail = (UART_RxTail + 1) & UART_RX_BUFFER_MASK;
    UART_RxTail = tmptail;

    /* get data from receive buffer */
    data = UART_RxBuf[tmptail];

    return (UART_LastRxError << 8) + data;

Zunächst wird data als unsigned char deklariert. Also 7 Bit groß 
(0..255).
Dann am Ende der Funktion, wird

        return (UART_LastRxError << 8) + data;

der Fehler und data zurückgegeben. Was wird hier eigentlich gemacht? ich 
versteh das nicht.

In der Test-Demo wird nun c als unsigned int deklariert. Also 32 Bit ( 0 
- 4.294.967.295 ). Ich versteh das noch nicht so ganz...


   if ( c & UART_NO_DATA )

...wenn UART_RxHead == UART_RxTail dann wird von uart_getc() 
UART_NO_DATA zurückgegeben, also 0x0100. 0x0100 & 0x0100 --> send Uart 
hat keine Daten!


Hier versteh ich nicht ganz, wie er das hier...

 if ( c & UART_FRAME_ERROR )


   if ( c & UART_OVERRUN_ERROR )


   if ( c & UART_BUFFER_OVERFLOW )

...vergleichen kann. Aber wahrscheinlich hängt das mit meinem 
Verständnisproblem von ...

            return (UART_LastRxError << 8) + data;

... zusammen.

Wenn der Rückgabewert von uart_getc() fehlerfrei ist, wird der unsigned 
in mit Hilfe eines Cast in...

        uart_putc( (unsigned char)c );


...unsigned char umgewandelt un ausgegeben.

Viel Grüße,
Klaus

von Karl H. (kbuchegg)


Lesenswert?

Klaus schrieb:

> Zunächst wird data als unsigned char deklariert. Also 7 Bit groß
> (0..255).
> Dann am Ende der Funktion, wird
>
>         return (UART_LastRxError << 8) + data;
>
> der Fehler und data zurückgegeben. Was wird hier eigentlich gemacht? ich
> versteh das nicht.

Ist doch ganz einfach:

Der Return Wert der Funktion ist unsigned int. Also 16 Bit
Das nächste Zeichen ist ein unsigned char, also 8 Bit

An dieser Stelle nimmt er den Fehlercode, so einer existiert (ansonsten 
eben 0), schiebt ihn um 8 Bits nch rechts und baut in die unteren 8 Bits 
noch das nächste Zeichen rein.

Der Return-Wert der Funktion hat also 16 Bit, also 2 Byte

Das höherwertige Byte davon ist ein Fehlercode
Das niederwertige Byte ist das nächste Zeichen

> In der Test-Demo wird nun c als unsigned int deklariert. Also 32 Bit

No.
16 Bit.

>    if ( c & UART_NO_DATA )
>
> ...wenn UART_RxHead == UART_RxTail dann wird von uart_getc()
> UART_NO_DATA zurückgegeben, also 0x0100. 0x0100 & 0x0100 --> send Uart
> hat keine Daten!

Genau.
Wenn die Funktion also UART_NO_DATA liefert, dann gab es keinen Fehler 
oder sonst was, sondern es ist einfach nichts da, was die Funktion hätte 
liefern können. Auf deutsch: Das Eingangs-Postfach ist leer.

> Hier versteh ich nicht ganz, wie er das hier...
>
>  if ( c & UART_FRAME_ERROR )
>
>
>    if ( c & UART_OVERRUN_ERROR )
>
>
>    if ( c & UART_BUFFER_OVERFLOW )
>
> ...vergleichen kann.

Warum soll er das nicht können?

UART_FRAME_ERROR und all die anderen #define sind so gebaut, dass man 
sie mit dem unsigned int, den die Funktion liefert, einfach nur verunden 
muss und dann das entsprechende Fehlerbit im höherwertigen Byte übrig 
bleibt. Ist es gesetzt (und damit das Verundungsergebnis ungleich 0) 
dann wird der entsprechende if-Zweig genommen


> Wenn der Rückgabewert von uart_getc() fehlerfrei ist, wird der unsigned
> in mit Hilfe eines Cast in...
>
>         uart_putc( (unsigned char)c );
>
>
> ...unsigned char umgewandelt un ausgegeben.

Genau.
Was er im Grunde macht: Er befreit das 'Konglomerat' aus Fehlercodes und 
emfangenen Zeichen vom Fehlercode, so dass nur das Zeichen übrig bleibt. 
Er 'strippt' das High-Byte vom c weg. Das High-Byte war aber genau der 
Fehlercode.


Hier wird der unsigned int nicht als Integer-Zahl im eigentlichen Sinne 
benutzt, sondern als Transport-'Container', mit dem er 2 Bytes aus der 
Funktion herausschafft. Er hätte sich auch eine Struktur
1
  struct get_Result
2
  {
3
    unsigned char ErrorCode;
4
    unsigned char nextCharacter;
5
  };
machen können. Aus Gründen, die nur Peter kennt, hat er sich dagegen 
entschieden und die beiden unsigned char in einem unsigned int händisch 
zusammengefasst.

von Klaus (Gast)


Lesenswert?

Hallo Karl-Heinz,

DANKE für deine Antwort. Es wird langsam etwas klarer.
Bei einem auftretenden Fehler wird in der Demo ja die Nachricht in den 
SRAM-Flash geschrieben. Wenn allerdings das Programm läuft, kann ich 
programmtechnisch ja eh nicht mehr eingreifen.

Ich selbst möchte auch ein kleines Programm schreiben, dass die 
empfangenen Daten vom UART empfängt und auswertet.

Ich glaube ich habe das langsam verstanden.

Der Ringpuffer, fängt die Daten die vom UART reingeschossen kommen auf 
und sagt: Nicht so schnell kommt erstmal zu mir und lasst das Programm 
mal seine restlichen Aufgaben erledigen! Der Puffer ist begrenzt groß 
und jedes Zeichen wird entsprechend ihrer Ankunft nacheinander in den 
FIFO abgelegt.

Den FIFO muss man etwas losgelöst vom eigentlichen Programm sehen. Erst 
durch die Funktion uart-getc() holt man sich die Daten zur Bearbeitung. 
Wenn man bspw. einen String "ABCDEFGHI\r" untersuchen will, schreibt man 
mit Hilfe der ISR die Zeichen in den Buffer.

Ist der Buffer voll, so kommen neue Daten und überschreiben die alten. 
Mit Hilfe von uart_get() kann man die Daten aus dem Ringbuffer holen und 
bearbeiten bzw. interpretieren. In meinem Programm kann ich die 
einzelnen Zeichen wieder in einen Puffer schreiben und auswerten. Bspw. 
kann ich A als Terminator festlegen und sagen, das wenn "A" detektiert 
wird die Zeichen bis '\r' ein den lokalen Puffer geschrieben werden. 
Dann kann ich sie weiterverarbeiten, etc.

Wichtig ist das der FIFO nicht zur Analyse oder Interpretation von Daten 
eingesetzt wird. Zudem muss das Programm so schnell wie möglich die 
Daten aus dem FIFO holen, so dass keine Daten überschrieben werden.

Passt das soweit?

In meiner Anwendung will ich die Daten permanent analysieren. Meine 
Strings sind 100 Zeichen lang. Ich habe zunächst einmal die FLeury Demo 
verwendet und meine Strings am Hyperterminal dargestellt. Klappt soweit 
ganz gut. Wenn ich allerdings...

             if(c & UART_NO_DATA)
             uart_puts("Keine Daten");

schreibe, erscheint am Hyperterminal eine ganze Reihe "Keine Daten" und 
dazwischen wieder einer meiner Strings. Dann wieder "Keine Daten", 
wieder einer meiner Strings, uns so weiter und so fort. Was geht da vor 
sich? Ist das die "Dauer" bis in meinem Puffer wieder Daten geschrieben 
werden?

von Karl H. (kbuchegg)


Lesenswert?

Klaus schrieb:

> ganz gut. Wenn ich allerdings...
>
>              if(c & UART_NO_DATA)
>              uart_puts("Keine Daten");
>
> schreibe, erscheint am Hyperterminal eine ganze Reihe "Keine Daten" und
> dazwischen wieder einer meiner Strings. Dann wieder "Keine Daten",
> wieder einer meiner Strings, uns so weiter und so fort. Was geht da vor
> sich?

Ganz einfach: Die Übertragung dauert viel länger als die Zeitspanne, in 
denen dein µC das 'Postfach' abfrägt.

Wenn du alle 10 Minuten zu deinem Postfach gehst, der Postler aber nur 
am Vormittag kommt und ab und zu auch mal am Nachmittag, dann wirst du 
auch die meiste Zeit mit einem 'keine Post da' in deine Wohnung 
zurückkehren.

Aber: WEnn du mal 1 oder 2 Tage keine Zeit hast, die Post aus dem 
Postfach zu holen, dann speichert dein Postfach die Briefe in der 
Zwischenzeit. Und genau das gleiche macht auch diese 
Ringbuffersteuerung. Wenn dein Programm kurzfristsig anderweitig 
beschäftigt ist, weil er zb einen String zusammenstellt um ihn zu 
versenden, dann werden die in der Zwischenzeit über die UART 
hereinkommenden Zeichen im Ringbuffer zwischengespeichert. Solange bis 
dein Programm wieder Zeit hat, sich erneut um das Postfach zu kümmern.

Und noch eine Analogie greift: Wenn du 2 Monate auf Weltreise bist, dann 
wird dein Postfach irgendwann voll, weil ja keiner mehr die Post 
rausholt. Und dann wird es spannend was dann passiert. Beim Ringbuffer 
werden dann halt alte, noch nicht verarbeitete Daten überschrieben. Das 
ist genauso gut oder genauso schlecht, als ob diese neuen Daten nicht 
mehr in den Ringbuffer geladen werden würden. Denn Fehlerfall ist es auf 
jeden Fall.

von Klaus (Gast)


Angehängte Dateien:

Lesenswert?

Die Vgl. sind klasse, dass kann ich mir auf jeden Fall so merken.

Eine Frage noch:

Da man programmtechnisch bei den Fehlern eh nichts mehr tun kann, hab 
ich in meinem Programmteil diese

#define UART_FRAME_ERROR
#define UART_OVERRUN_ERROR
#define UART_BUFFER_OVERFLOW
#define UART_NO_DATA

einfach weggelassen. Wenn mein Programm zu spät auf die im FIFO 
befindlichen Daten zugreift, werden sie ja eh überschrieben. Im Anhang 
hab ich nun den abgeänderten Code von Peter Fleury. Die Nullen, wenn 
keine Daten vorhanden, filtere ich zuvor raus und erhalte somit meine 
gewünschten Strings. Kann man das so lassen oder sollte ich noch was 
beachten/verbessern?
VG

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.