Forum: Compiler & IDEs RS232 Schnittstelle außerhalb der main Datei


von RS232 (Gast)


Lesenswert?

Hallo,
wie gliedere ich die UART Schnittstelle am besten aus der Hauptdatei 
aus? Ich empfange einzelne Zeichen und arbeite mit Interrupt. Wäre das 
eine Lösung:
1
uint8_t rs232_rxcheck(void) {
2
  uint8_t rs232byte;
3
4
  if (UDR_new_byte) {
5
    cli();
6
    rs232byte = UDR_puffer;
7
    UDR_new_byte = 0;
8
    sei();
9
    return(rs232byte);
10
  } else {
11
    return(0);
12
  }
13
  
14
  
15
}

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Jein. Die Lösung hat Haken.

Lies dich in die Bedeutung von volatile ein. Möglicherweise brauchst 
du die für UDR_puffer und UDR_new_byte.

Deine Funktion kann nicht den Wert 0 zurückgeben, weil du den als 
Fehlerwert bzw. "nix da"-Wert zurückgibst. Das kannst du ändern, indem 
du einen uint16_t Wert zurückgibst und z.B. im unteren Byte das 
Datenbyte und im oberen Byte ein Fehlerbyte.

Wenn du mit der avr-libc arbeitest, schau dir das Makros ATOMIC an. Die 
kapseln den atomaren Zugriff, den du mit cli()/sei() kapselst ABER das 
Makro manipuliert das SREG. Deine Lösung schaltet mit dem sei() immer 
die Interrupts global ein, auch wenn sie vorher noch ausgeschaltet 
waren. Das ATOMIC Makro macht das nicht. In deinem Beispiel spielt das 
wahrscheinlich keine Rolle, in anderen Beispielen sucht man sich an 
sowas zur Laufzeit den Wolf!

von RS232 (Gast)


Lesenswert?

Hallo,
also die beiden Werte sind schon als voitaille definiert. Ich überlege 
nur gerade kommt UDR aus dem internen Puffer als char oder als int? Eher 
als char oder? Da wäre der Rückgabewert 0 irgendein Steuerzeichen. Ich 
überlege gerade, ob es überhaupt sinnvoll ist (für meine Verwendung) das 
Empfangene Byte in die Hauptdatei zurückzuführen. Weil ich möchte wenn 
bestimmte Bytes empfangen wurden Befehle ausführen das könnte ich ja 
gleich in der RS232 Datei.

ATOMIC werde ich mir mal anschauen.

von Justus S. (jussa)


Lesenswert?

RS232 schrieb:
> Hallo,
> also die beiden Werte sind schon als voitaille definiert. Ich überlege
> nur gerade kommt UDR aus dem internen Puffer als char oder als int?

Im UDR stehen einfach nur die acht Bits, die empfangen wurden, wie du 
sie interpretierst ist alleine deine Sache...

von RS232 (Gast)


Lesenswert?

hallo,
was haltet ihr von der Funktion:
1
void rs232_rxcheck(void) {
2
  
3
  if (UDR_new_byte) {
4
    cli();
5
    rs232_data[rs232_byte_count++] = UDR_puffer;
6
    UDR_new_byte = 0;
7
    sei();
8
    if (rs232_byte_count==5) {
9
      rs232_byte_count=0;
10
      //Auswertung von rs232_data
11
    }
12
  } 
13
14
}

dazu habe ich jetzt Global diese Variablen eingeführt:
1
unsigned int  rs232_byte_count = 0;
2
unsigned int  rs232_data [5];

von STK500-Besitzer (Gast)


Lesenswert?

1
void rs232_rxcheck(void) {
2
  
3
  if (UDR_new_byte) {
4
    cli();
5
    rs232_data[rs232_byte_count++] = UDR_puffer;
6
    UDR_new_byte = 0;
7
    sei();
8
    if (rs232_byte_count==5) {
9
      rs232_byte_count=0;
10
      //Auswertung von rs232_data
11
    }
12
  } 
13
14
}

Die Puffer-Behandlung kannst du direkt in die Empfangs-ISR stecken und 
dort ein Flag setzen, dass du 5 Bytes empfangen hast.
Irgendwo anders (z.B. in der main) wertest du die Daten dann aus.

1
unsigned int  rs232_byte_count = 0;
2
unsigned int  rs232_data [5];

Es dürfte sehr sinnfrei sein, int zu benutzen, wenn du nur char 
brauchst.
Das kostet unnötig Speicherplatz und Programmcode.
1
static unsigned char rs232_byte_count = 0;
gehört in die Empfangs-ISR, der Puffer muss natürlich volatile global 
vereinbart werden.

von RS232 (Gast)


Lesenswert?

mit der Interrupt Routine ist eig ne gute Idee. Da müsste ich halt nur 
während der Auswertung die Interrupts deaktivieren und danach wieder 
aktivieren.

von RS232 (Gast)


Lesenswert?

1
static unsigned char rs232_byte_count = 0;
kann ich ja auch nicht einfach in den Interrupt schreiben. Weil da wird 
ja bei jedem Interrupt der Zähle zurückgesetzt.

von STK500-Besitzer (Gast)


Lesenswert?

>kann ich ja auch nicht einfach in den Interrupt schreiben. Weil da wird
>ja bei jedem Interrupt der Zähle zurückgesetzt.

Hast du es ausprobiert? Solltest du mal. Funktioniert nämlich.


>mit der Interrupt Routine ist eig ne gute Idee. Da müsste ich halt nur
>während der Auswertung die Interrupts deaktivieren und danach wieder
>aktivieren.

Beim AVR können sich Interrupts nicht gegenseitig unterbrechen. Die 
werden nach der Reihenfolge der Interrupt-Vector-Tabelle abgearbeitet, 
nachdem eine ISR beendet wurde.

von RS232 (Gast)


Lesenswert?

ok und woran liegt das, dass es trotzdem funktioniert? Am static? Also 
da müsste ich ja aber am besten die Auswertung gleich in der Intterupt 
Routine machen oder? Weil das meinte ich mit deaktivieren, nicht das er 
während der Auswertung den Puffer umschreibt.

von STK500-Besitzer (Gast)


Lesenswert?

>ok und woran liegt das, dass es trotzdem funktioniert? Am static?
Ja.

>Also da müsste ich ja aber am besten die Auswertung gleich in der Intterupt
>Routine machen oder?

Das liest sich sehr wirr...
Was willst du überhaupt machen?
Es kann sein, dass deine Auswertung wunderbar in die ISR passt, es kann 
aber auch sein, dass das viel zu lange dauert, und andere Interrupts 
deswegen nicht pünktlich genug abgearbeitet werden können.

>Weil das meinte ich mit deaktivieren, nicht das er
>während der Auswertung den Puffer umschreibt.

Wenn während deiner Protokollbearbeitung mehr als ein Byte empfangen 
wird, geht dir eh irgendwas verloren.
Guck dir mal das Thema Ringpuffer an.

von RS232 (Gast)


Lesenswert?

naja ich möchte halt per RS232 befehle senden, und je nach Befehl andere 
Funktionen auslösen,

von RS232 (Gast)


Lesenswert?

und haben die ATMEL USARTS nicht schon generell einen Ringpuffer?

von STK500-Besitzer (Gast)


Lesenswert?

>und haben die ATMEL USARTS nicht schon generell einen Ringpuffer?

Nein. Naja, es werden 2 Bytes gepuffert. Das würde ich noch nicht als 
Ringpuffer bezeichnen.

von RS232 (Gast)


Lesenswert?

1
ISR(USART_RXC_vect) { 
2
  static unsigned char rs232_byte_count = 0;
3
  rs232_data[rs232_byte_count++] = UDR;
4
  if (rs232_byte_count==5) {
5
    UDR_new_bytes = 1;
6
  }
7
}

hier der Header Ausschnitt:
1
volatile unsigned char  rs232_data [5];
2
volatile unsigned char  UDR_new_bytes = 0;

verursacht erstmal Fehler.

rs232.c:13: multiple definition of `UDR_new_bytes'

von Frank K. (Gast)


Lesenswert?

der Header wird 2x included einmal in der main Datei und einmal in der 
RS232 Datei. Aber ich habe um den Header:
1
#ifndef RS232_H_INCLUDED
2
#define RS232_H_INCLUDED
3
4
#endif

von STK500-Besitzer (Gast)


Lesenswert?

>der Header wird 2x included einmal in der main Datei und einmal in der
>RS232 Datei.

Muss nicht sein. Es ist eher der Fall, dass zwei Variablen mit dem 
gleichen Namen definiert werden. Ob die sich in Header-Dateien befinden 
oder im Hauptprogramm, steht da nicht.

Vielleicht sollte sich RS232 mal ein C-Buch besorgen, in dem das alles 
beschrieben wird. Das sind nämlich C-Grundlagen.

von RS232 (Gast)


Lesenswert?

sie ist auf keinen Fall doppelt definiert. In der Main Datei steht gar 
nichts weiter groß. Und wenn ich das: #include "rs232.h" aus der Main 
entferne funktioniert es ja.

von STK500-Besitzer (Gast)


Lesenswert?

>Und wenn ich das: #include "rs232.h" aus der Main entferne funktioniert es >ja.

Dann liegt der Fehler wohl in deinen RS232-Dateien...

von RS232 (Gast)


Lesenswert?

ich kann sie ja mal posten:
c:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <inttypes.h>
4
5
#include "rs232.h"
6
7
ISR(USART_RXC_vect) { 
8
  static unsigned char rs232_byte_count = 0;
9
  rs232_data[rs232_byte_count++] = UDR;
10
  if (rs232_byte_count==5) {
11
    UDR_new_bytes = 1;
12
  }
13
}
14
15
void rs232_init(void) {
16
  UCSRB |= (1<<TXEN);                // UART TX einschalten
17
    UCSRC |= (1<<URSEL)|(3<<UCSZ0);    // Asynchron 8N1 
18
 
19
    UBRRH = UBRR_VAL >> 8;
20
    UBRRL = UBRR_VAL & 0xFF;
21
}

h:
1
#ifndef RS232_H_INCLUDED
2
#define RS232_H_INCLUDED
3
4
5
#define BAUD 9600L
6
7
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)  
8
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))   
9
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000)
10
11
volatile unsigned char  rs232_data [5];
12
volatile unsigned char  UDR_new_bytes = 0;
13
14
15
#endif

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> Aber ich habe um den Header:
1
#ifndef RS232_H_INCLUDED
2
#define RS232_H_INCLUDED
3
:
4
:
5
#endif
Überleg dir mal, warum das nicht hilft. Als kleine Hilfestellung: stell 
dir vor, der Header wird in beiden c-Dateien jeweils genau 1 mal 
eingebunden.

Fazit:
In einer Header-Datei darf keine Variable definiert werden.
Dort solltest du sie deklarieren (bekanntmachen):
1
extern volatile unsigned char  rs232_data [5];
2
extern volatile unsigned char  UDR_new_bytes = 0;
Und dann in einer der c-Dateien definieren:
1
volatile unsigned char  rs232_data [5];
2
volatile unsigned char  UDR_new_bytes = 0;
Siehe dazu 
http://www.mikrocontroller.net/search?query=variablendefinition+variablendeklaration&forums[]=1&forums[]=2&max_age=-&sort_by_date=0
und der Beitrag "Re: Doppelte Definitionen (Header Datei)"

von Frank K. (Gast)


Lesenswert?

ok das ist natürlich war. Da vielen Dank für den Hinweis.
Und was war hier heran jetzt falsch:
1
RS232_H_INCLUDED
2
.
3
.
4
.

von Frank K. (Gast)


Lesenswert?

in den Header muss doch dann aber das rein:
1
extern volatile unsigned char  rs232_data [5];
2
extern volatile unsigned char  UDR_new_bytes;


oder kann ich nicht auch die Variablen im header komplett weglassen?

von Frank K. (Gast)


Lesenswert?

weil auch wenn ich die Variable als extern einführe und ihr einen Wert 
zuweise bekomme ich einen Fehler.

von Karl H. (kbuchegg)


Lesenswert?

Wie schon geschrieben: Du brauchst Literatur!

In der Zwischenzeit, bis du dein Buch hast, liest du dir das mal durch:

http://www.mikrocontroller.net/articles/FAQ#Globale_Variablen_.C3.BCber_mehrere_Dateien

von Frank K. (Gast)


Lesenswert?

Also was ich jetzt so gelesen habe ist das in der Headerdatei verboten:
1
extern volatile unsigned char  UDR_new_bytes = 0;

von Karl H. (kbuchegg)


Lesenswert?

Frank K. schrieb:
> Also was ich jetzt so gelesen habe ist das in der Headerdatei verboten:

Verboten nicht.
Aber es macht nicht das, was man auf den ersten Blick vermuten würde :-)
Vor allen dann nicht, wenn man die Regeln nicht kennt. Und die sagen 
eindeutig: Wenn etwas eine Initialisierung hat, dann kann es keine 
Deklaration sein, sondern nur eine Definition. Und damit ist das extern 
wirkungslos.

von Frank K. (Gast)


Lesenswert?

Nur bekomme ich, wenn ich das = 0 lasse immer noch einen Redefinition 
Fehler:
h:
1
#ifndef RS232_H_
2
#define RS232_H_
3
4
5
#define BAUD 9600L
6
7
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1) 
8
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1))) 
9
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) 
10
11
extern volatile unsigned char  rs232_data [5];
12
extern volatile unsigned char  UDR_new_bytes = 0;
13
14
15
void rs232_init(void);
16
17
#endif

c:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <inttypes.h>
4
#include "motor.h"
5
6
#include "rs232.h"
7
8
9
volatile unsigned char  rs232_data [5];
10
volatile unsigned char  UDR_new_bytes = 0;
11
12
13
ISR(USART_RXC_vect) { 
14
  static unsigned char rs232_byte_count = 0;
15
  rs232_data[rs232_byte_count++] = UDR;
16
  if (rs232_byte_count==5) {
17
    UDR_new_bytes = 1;
18
  }
19
}
20
21
void rs232_init(void) {
22
  UCSRB |= (1<<TXEN);                // UART TX einschalten
23
    UCSRC |= (1<<URSEL)|(3<<UCSZ0);    // Asynchron 8N1 
24
 
25
    UBRRH = UBRR_VAL >> 8;
26
    UBRRL = UBRR_VAL & 0xFF;
27
}

von ... .. (docean) Benutzerseite


Lesenswert?

das gehört auch nicht in die .h, nur einmal mus du =0 machen das 
reicht...

Ach ja guck dir aml uint8_t Datentypen an...

von Frank K. (Gast)


Lesenswert?

ok ich habe mich nur auf den Beitrag von Lothar Miller bezogen, wo auch 
im Header ein = 0 war. Das war wahrscheinlich aber nur ein Tippfehler.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> Das war wahrscheinlich aber nur ein Tippfehler.
Das war ein Copy-Paste-Fehler   ;-)
Mir war der Unterschied zwischen Deklaration und Definition wichtig.

von Frank K. (Gast)


Lesenswert?

ok danke jetzt ist mir so einiges klar. Ich habe vorher noch nie mehr 
als eine C Datei benutzt, deswegen ist das Neuland für mich.

@docean was ist mit den Typen?

von Frank K. (Gast)


Lesenswert?

Und noch was. Wie löse ich jetzt am besten die Auswertung der 
Empfangenen Bytes. Soll ich die Auswertung in der ISR machen, oder 
lieber extern? Für den Anfang reicht es, wenn ich einzelne Bytes senden 
kann. Also nicht automatisiert, sondern per Hand vom PC.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> Soll ich die Auswertung in der ISR machen,
Nein. Eine ISR ist so lang wie unbedingt nötig, aber auf jeden Fall so 
kurz wie möglich. In der ISR schreibst du die Zeichen in einen 
Puffer....

> oder lieber extern?
...und wertest sie in der Hauptschleife aus.

von STK500-Besitzer (Gast)


Lesenswert?

>> oder lieber extern?
>...und wertest sie in der Hauptschleife aus.

In einer eigenen Header-Datei... SCNR

von Frank K. (Gast)


Lesenswert?

Ok. Aber wenn ich sie extern Auswerte muss ich ja aber auch die 
Interrupts für den Zeitraum deaktivieren, damit nicht der Puffer 
verändert werden kann.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Frank K. schrieb:
> Ok. Aber wenn ich sie extern Auswerte muss ich ja aber auch die
> Interrupts für den Zeitraum deaktivieren, damit nicht der Puffer
> verändert werden kann.
Nein, lies mal was zum Thema Ringpuffer bzw. Fifo.
Es gibt einen Schreib- und einen Lesepointer, so können keine Zeiger 
gegenseitig verbogen werden. Nur die ISR schreibt in den Puffer, und 
nur die main-Loop liest.
Sieh dir dazu mal den SIO-Code von Peter Fleury an, der funkt 
reibungslos.

von Frank K. (Gast)


Lesenswert?

So ich habe mich gerade mal ein wenig mit Ringpuffern beschäftigt. Das 
Prinzip beruht ja bei Peter Fleury auf der Bitweisen Verknüpfung. Das 
ganze ist eigentlich relativ genial. Ich werde jetzt mal versuchen mit 
der Teilen der Library mein Vorhaben zu realisieren.

von STK500-Besitzer (Gast)


Lesenswert?

>Das Prinzip beruht ja bei Peter Fleury auf der Bitweisen Verknüpfung.
Echt? Ich dachte, es beruht darauf, dass man einen Puffer hat, für den 
es eine Schreib- und einen Lesezeiger gibt.
Die bitweise Verknüpfung hängt eher mit dem beschränkten Platz des 
Puffers zusammen. Wenn man den Puffer 256 Bytes groß macht, und als 
Indizes welche vom Type unsigned char nimmt, dann kann man sich die 
bitweisen Verküpfungen sparen...

von Frank K. (Gast)


Lesenswert?

1
    /* calculate buffer index */ 
2
    tmphead = ( UART_RxHead + 1) & UART_RX_BUFFER_MASK;
3
4
  /* store new index */
5
  UART_RxHead = tmphead;
6
  /* store received data in buffer */
7
  UART_RxBuf[tmphead] = data;

also das ist der entsprechende Code Ausschnitt.

von Frank K. (Gast)


Lesenswert?

zur Überprüfung hätte ich auch nochmal ne Frage. Hier mal code 
Ausschnitte:
1
#define UART_NO_DATA          0x0100              /* no receive data available   */
2
3
unsigned int rs232_receive(void) {    
4
5
    return UART_NO_DATA;   /* no data available */
6
}
7
8
if ( c & UART_FRAME_ERROR )

wie Funktioniert die Bedingung in dem Fall. Wenn 0x0100 zurückgegeben 
wird wird das mit 0x0100 binär verknüpft. Es kommt also wieder 0x0100 
raus. Aber Sind nicht alle positiven Zahlen true?

von Frank K. (Gast)


Lesenswert?

1
if ( c & UART_NO_DATA

das meine ich natürlich.

von Naja (Gast)


Lesenswert?

1
if ( c & UART_FRAME_ERROR )

Das ist eine gewissermassen abkürzende Schreibweise von
1
if ((c & UART_FRAME_ERROR) != 0)

Als True im Sinne einer wahren Bedingung wird hier jeder Wert ungleich 0 
betrachtet. 0 ist False.

Siehe K&R ISBN: 3-446-15497-3, Seite 55

von Frank K. (Gast)


Lesenswert?

Ok ich verstehe. ALso alles, was nciht der Bitmaske entspricht wird 
durch die Verknüpfung 0. Aber knnte man da ncit gleich schreiben c==...?

von Naja (Gast)


Lesenswert?

>ALso alles, was nciht der Bitmaske entspricht wird durch die Verknüpfung 0.

So würde ich das nicht formulieren.
Was ist denn das was nicht der Bitmaske entspricht? Der andere Operand 
der Und-Verknüpfung.
Ist die Bitmaske selbst strukturiert? Ja. In Bits.
Was wird 0? Das Ergebnis der Und-Verknüpfung.
Ist das Ergebnis selbst strukturiert? Ja. Auch in Bits.
Was läßt sich über die Bits, des Ergebnisses sagen? Das jedes Bit des 
Ergebnisses 0 ist, bei dem das entsprechende Bit der Bitmaske gleich 0 
oder das entsprechende Bit des anderen Operanden gleich 0 ist.

>Aber knnte man da ncit gleich schreiben c==...?
In diesem Fall vielleicht schon. Aber der Code
1
unsigned int rs232_receive(void) {    
2
3
    return UART_NO_DATA;   /* no data available */
4
}

läßt mich bezweifeln ob es sich um ein reales bzw. vollständiges 
Beispiel handelt.

Im allgemeinen verwendet man diese Bitmasken mit Und, wenn noch weitere 
Information in anderen Bits stecken.
Daher kann man nicht mit == arbeiten. Man will ja die anderen Bits 
vielleicht garnicht betrachten.
Komplizierter wird es noch wenn man einige Bits auf 0 und andere auf 1 
testen will.

von Frank K. (Gast)


Lesenswert?

1
unsigned int uart_getc(void)
2
{    
3
    unsigned char tmptail;
4
    unsigned char data;
5
6
7
    if ( UART_RxHead == UART_RxTail ) {
8
        return UART_NO_DATA;   /* no data available */
9
    }
10
    
11
    /* calculate /store buffer index */
12
    tmptail = (UART_RxTail + 1) & UART_RX_BUFFER_MASK;
13
    UART_RxTail = tmptail; 
14
    
15
    /* get data from receive buffer */
16
    data = UART_RxBuf[tmptail];
17
    
18
    return (UART_LastRxError << 8) + data;
19
20
}/* uart_getc */

das War der vollständige Code ich habe ihn für meine Zwecke gekürzt. 
Also verwendet man die Bitmaske, weil man mehrere Informationen über 
return zurückgibt. Da ich da ja aber nicht mache kann ich auch mit == 
und != arbeiten.

von Karl H. (kbuchegg)


Lesenswert?

Frank K. schrieb:

> return zurückgibt. Da ich da ja aber nicht mache kann ich auch mit ==
> und != arbeiten.

Du musst noch lernen, dass man mit defensiven Programmieren auf lange 
Sicht immer besser fährt.
Heute ist es vielleicht so, dass das bei dir so ist. Aber in 2 Monaten 
brauchst du ev. ein neues Bit, welches dir einen Fehler anzeigt. Machst 
du die Abfrage gleich richtig, indem du nur dieses eine Bit, welches du 
abfragen willst, ausmaskierst, dann ändert sich beim verwendenden Code 
gar nichts. Es wird weiterhin nur dieses eine Bit abgefragt. Gehst du 
jetzt aber den verlockenden, vermeintlich einfacheren Weg, dann hast du 
in 2 Monaten erst mal eine Menge Arbeit, weil du den kompletten Code 
durchforsten musst, auf welche Abfrage sich dein neu hinzugefügtes Bit 
auswirkt und wo nicht. Das du bei dieser Änderung natütrlich ein paar 
Codestellen übersehen wirst und daher erst mal sporadische, seltsame 
Fehler auftauchen werden, braucht nicht extra betont zu werden.

Und das alles nur, weil du jetzt eine Und-Maskierung einsparen willst, 
die dir so gut wie keine Rechenzeit kostet.

von Naja (Gast)


Lesenswert?

Dein Code gibt auch ein wunderschönes Beispiel für einen Fall wo Du eben 
nicht einfach nur mit == arbeiten kannst. Es bezieht sich nur nicht auf 
UART_NO_DATA.

Aber mit
1
return (UART_LastRxError << 8) + data;

wird in den höherwertigen Bits noch der UART-Fehler zurückgegeben.
Um also nur die Fehlerbits zu berücksichtigen brauchst Du 0xFF00 als 
Maske. Um nur die Datenbits zu lesen hingegen 0x00FF.

Falls man etwa damit rechnen kann (das hängt von der 
UART-Implementierung ab) das sowohl das Datenbyte (zumdinest teilweise) 
gültig ist und auch die Fehlerbits so brauchst Du beide Masken und 
kannst nicht mit == arbeiten.

von Frank K. (Gast)


Lesenswert?

ok. Aber die Auswertung kann ich doch jetzt wie geplant machen in der 
Main Datei, oder eben wieder in einer extra Funktion.
1
  while(1) {
2
    rs232_byte=rs232_receive();
3
    if ( rs232_byte & UART_NO_DATA ) {
4
      //no data avaible
5
    } else {
6
      //new data avaible
7
      rs232_buffer[rs232_byte_count++];
8
      if (rs232_byte_count == 5) {
9
        rs232_byte_count=0;
10
        //analyses
11
      }
12
    
13
    }
14
  }

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

1
      rs232_buffer[rs232_byte_count++];
2
      if (rs232_byte_count == 5) {
3
        rs232_byte_count=0;
4
        //analyses
5
      }
Genau diese umständliche Grenz-Abfragerei kann durch eine geeignete Wahl 
der Puffergröße als 2er Potenz umgangen werden:
1
#define BUFSIZE 8 /* Zweierpotenz 4,8,16...  */
2
#define BUFMASK (BUFSIZE-1)
3
:
4
:
5
      rs232_buffer[rs232_byte_count++];
6
      rs232_byte_count &= BUFMASK;
Genauso macht das Peter Fleury.

Lustigerweise wird in deinem Code mit dem rs232_buffer[] gar nichts 
gemacht  :-/

EDIT:
du solltest deinem Lesezeiger einen eindeutigen Namen geben
z.B. rs232_byte_count_rd
im Gegensatz zum Schreibzeiger in der Interrupt-Routine, der demnach
z.B. rs232_byte_count_wr
heißen würde.
1
  // Im Interrupt in den Puffer schreiben
2
      rs232_buffer[rs232_byte_count_wr++] = UDR; // Zeichen einlesen
3
      rs232_byte_count_wr &= BUFMASK;
4
:
5
  // in main() aus dem Puffer lesen
6
      if (rs232_byte_count_wr!=rs232_byte_count_rd) {   // Zeichen da?
7
         zeichen = rs232_buffer[rs232_byte_count_rd++]; // Zeichen einlesen
8
         rs232_byte_count_rd &= BUFMASK;
9
      }

von Frank K. (Gast)


Lesenswert?

naja die Funktion rs232_receive liest ja schon aus dem Ringpuffer. Sie 
ist nur zur Kommunikation da. Die Zuweisung des Puffers habe ich aus 
versehen vergessen.
1
ISR(USART_RXC_vect) { 
2
  unsigned char tmphead;
3
    unsigned char data;
4
 
5
6
    data = UDR;
7
    tmphead = ( UART_RxHead + 1) & UART_RX_BUFFER_MASK;
8
  UART_RxHead = tmphead;
9
  UART_RxBuf[tmphead] = data;
10
11
}
12
13
14
15
unsigned int rs232_receive(void)
16
{    
17
    unsigned char tmptail;
18
    unsigned char data;
19
20
21
    if ( UART_RxHead == UART_RxTail ) {
22
        return UART_NO_DATA;
23
    }
24
    
25
    tmptail = (UART_RxTail + 1) & UART_RX_BUFFER_MASK;
26
    UART_RxTail = tmptail; 
27
28
    data = UART_RxBuf[tmptail];
29
    return (data);
30
31
}

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Deine Pointerbehandlung ist etwas unüblich.
Erst wird der Pointer aktualisiert, und dann mit dem alten Pointer 
geschrieben/ausgelesen. Üblicher ist es, erst die Daten zu manipulieren, 
dann den Pointer.
Aber das kann ja jeder machen, wie er will ;-)
1
:
2
  tmphead = ( UART_RxHead + 1) & UART_RX_BUFFER_MASK;
3
  UART_RxHead = tmphead;
4
  UART_RxBuf[tmphead] = data;
5
:
6
7
    tmptail = (UART_RxTail + 1) & UART_RX_BUFFER_MASK;
8
    UART_RxTail = tmptail; 
9
    data = UART_RxBuf[tmptail];
Das geht so kürzer und ohne explizite Definition von lokalen Variablen:
1
:
2
    UART_RxBuf[UART_RxHead] = data;
3
    UART_RxHead = ( UART_RxHead + 1) & UART_RX_BUFFER_MASK;
4
:
5
    data = UART_RxBuf[UART_RxTail];
6
    UART_RxTail = (UART_RxTail + 1) & UART_RX_BUFFER_MASK;

von Frank K. (Gast)


Lesenswert?

also das ist die Library von Fleury nicht von mir. Ziel ist es wie 
gesagt, das ich Befehle (5 Bytes) per RS232 sende und diese dann 
ausgewertet werden sollen. Also muss ja irgendwo eine Abfrage rein, ob 
die 5 Bytes fertig sind.

von Karl H. (kbuchegg)


Lesenswert?

Frank K. schrieb:
> also das ist die Library von Fleury nicht von mir. Ziel ist es wie
> gesagt, das ich Befehle (5 Bytes) per RS232 sende und diese dann
> ausgewertet werden sollen. Also muss ja irgendwo eine Abfrage rein, ob
> die 5 Bytes fertig sind.

Daran musst du sowieso noch arbeiten.
Die Idee 5 Bytes einfach so zu lesen und davon auszugehen dass diese 5 
Bytes tatsächlich irgendetwas miteinander zu tun haben, ist auf lange 
Sicht ein sicherer Garant für Desaster.

von Frank K. (Gast)


Lesenswert?

naja ich hätte einfach ein timeout eingebaut.

von Karl H. (kbuchegg)


Lesenswert?

Frank K. schrieb:
> naja ich hätte einfach ein timeout eingebaut.

Wer ist denn die Gegenstelle?
Wer sendet die Kommandos?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> naja ich hätte einfach ein timeout eingebaut.
Besser ist ein Protokoll, z.B. so:
1) Ein bestimmtes Startzeichen setzt den Schreibzeiger auf 0,
2) dann werden alle empfangenen Zeichen in den Puffer geschrieben,
bis entweder
3) der voll ist, das wäre ein Fehler  :-o
oder
4) das Endezeichen kommt, das dann auch die Auswertung anwirft.
Damit du die Steuerzeichen für Protokollstart und -ende von den anderen 
Zeichen unterscheiden kannst, erfolgt die Übertragung der eigentlichen 
Daten im ASCII-Klartext. Die Steuerzeichen sind dann z.B. STX (Start of 
Text 0x02) und ETX (End of Text 0x03) oder etwas, das du sicher von 
deinen Daten unterscheiden kannst (z.B. auch CR/LF).

Ein solches Protokoll ist weit verbreitet:
Beitrag "Re: [AVR] Serielle Protokolle: STX, ETX usw."
http://www.meinberg.de/german/info/leap-second.htm

Ein tiefergehendes Protokoll könnt auch das sein:
SOH (start of heading)
LÄNGE
STX (start of text)
DATEN
ETX (end of text)
CHECKSUM
EOT (end of transmission)

BTW:
Bitte ETX (End of Text) nicht mit EOT (End of Transmission) verwechseln

von Frank K. (Gast)


Lesenswert?

Also Gegenstelle soll ein PC sein indem ich erstmal die Daten per Hand 
sende und später auf dem PC ein Programm dafür schreiben werde.

von Frank K. (Gast)


Lesenswert?

Und selbst wenn ich mit Start und Stoppzeichen arbeite muss ich ja 
trotzdem einen Zähler mitlaufen lassen und bzw. auf das Endzeichen 
warten.

von Frank K. (Gast)


Lesenswert?

Oder ich könnte die Zeichen ab dem Startzeichen Zählen und prüfen, ob 
das 5. Zeichen z.B. das Endzeichen ist. eine andere Möglichkeit sehe ich 
eig nicht.

von Karl H. (kbuchegg)


Lesenswert?

Frank K. schrieb:
> Oder ich könnte die Zeichen ab dem Startzeichen Zählen und prüfen, ob
> das 5. Zeichen z.B. das Endzeichen ist. eine andere Möglichkeit sehe ich
> eig nicht.

Siehst du.
Jetzt hast du ein Protokoll!
Und dieses Protokoll leitet dein Programm!

von Frank K. (Gast)


Lesenswert?

Ok da würde ich das ganze jetzt aber zur Übersicht in eine Funktion 
auslagern, die das übernimmt und mit so einer art Satemachine arbeiten. 
Also je nach Status eine Globale Variable ändern oder eine static in der 
Funktion.

von Frank K. (Gast)


Lesenswert?

ein anderes Problem wäre noch, wenn ich Zahlen über 256 senden möchte. 
Da weis ich noch nicht, wie das gehen soll.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> ein anderes Problem wäre noch, wenn ich Zahlen über 256 senden möchte.
Das Problem hast du auch schon mit dem Wert 256   :-o

> Da weis ich noch nicht, wie das gehen soll.
Du sendest (basierend auf meinem Protokoll von oben) z.B. die Zeichen
ASCII  STX    1    2    3    4    5  ETX
Binär 0x02 0x31 0x32 0x33 0x34 0x35 0x03

Wenn du das ETX empfangen hast, ersetzt du das ETX (die 0x03) durch eine 
binäre 0x00 und lässt atio() ab Pufferadresse+1 darauf los. Heraus kommt 
ein bildschöner Integer der den Wert 12345 hat.

von Frank K. (Gast)


Lesenswert?

ok also muss ich das noch zusammenbasteln, dass es das 12345 dann wieder 
in ein 16bit int Feld reinschreibt.

von Frank K. (Gast)


Lesenswert?

achja das atio() direkt über den Rinpuffer laufen lassen? Das ist vlt. 
nicht so gut, weil er ja über das letzte Zeichen wieder bei 0 Anfängt. 
und wenn ich das ganze nochmal puffere weis ich ja nicht, wie lang das 
wird.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> achja das atio() direkt über den Rinpuffer laufen lassen?
Nein, der UART-Ringpuffer dient nur zur zeitlichen Entkopplung der 
seriellen Schnittstelle vom Hauptprogramm.

Hier eine exemplarische Auswertungsroutine:
Es wird hier kein Ringpuffer verwendet.
Das Zeichen STX setzt den Schreibpointer auf 0.
Das Zeichen ETX löst die Umwandlung aus.
Die ASCII-Zeichen dazwischen werden in den Puffer geschreiben.

Etwa so (from the scratch):
1
char buffer[10];
2
char index = 0;
3
char overrun = 0;
4
int  wert;
5
:
6
  for(;;) {
7
     :
8
     c = uart_getc();  // Zeichen aus Uart-Ringpuffer holen
9
     switch (c) {
10
        case ETX : buffer[index]=0;     // Endezeichen --> Auswertung
11
                   wert = atoi(buffer); // oder auch   strtol()
12
                   break;   // kann auch weggelassen werden  ;-)
13
        case STX : index=0; // STX wird nicht in den Puffer eingetragen 
14
                   break;
15
        default  : buffer[index]=c;     // evtl vorher prüfen, ob c eine ASCII-Zahl (0x30-0x39) ist
16
                   if index<sizeof(buffer)-2) index++;   // vor Überlauf schützen
17
                   else                       overrun=1; // Fehler: Überlauf
18
     }
19
     :
20
  }
Siehe zum Thema "Umwandlung" auch
Beitrag "Probleme mit der atoi-Funktion"
Beitrag "Umwandlung eines Char-Arrays in eine Zahl"

von Frank K. (Gast)


Lesenswert?

Hallo,
so hatte ih das auch schon probiert. Das Problem hierbei ist ja 
allerdings, das man maximal 8 Zahlen senden kann was ja aber ausreicht. 
Deshalb habe ich nach einer besseren Lösung gesucht.

von Karl H. (kbuchegg)


Lesenswert?

Frank K. schrieb:
> Hallo,
> so hatte ih das auch schon probiert. Das Problem hierbei ist ja
> allerdings, das man maximal 8 Zahlen senden kann

Wenn am anderen Ende ein menschlicher Benutzer sitzt, kann es natürlich 
passieren, dass er beim Einschlafen zb. auf die '5' Taste kommt und dir 
5555555555555555555555555555555555555555555 schickt.

(Nicht lachen. Ist ein beliebter Test unter Profis. Eingabreoutinen 
müssen damit klarkommen, dass ein Kleinkind auf der Tastatur rumhackt. 
Das Programm darf so etwas ablehnen, aber es darf nichts Unsinniges 
machen oder gar abschmieren)

> Deshalb habe ich nach einer besseren Lösung gesucht.

Und jetzt überleg mal, wofür wohl die Variable overrun in obigen 
Beispiel gut ist. Aus dem gleichen Grund ist auch atoi trotz seiner 
Einfachheit, keine glückliche Wahl. strtol hat einfach bessere 
Möglichkeiten, mir Fehlererkennung zu erlauben.

von Frank K. (Gast)


Lesenswert?

ok. Also ich würde das ganze jetzt so realisieren. Mit verschachtelten 
Zählern.

Start of Transmission
Start of text
1
2
3
End of Text
Start of text
4
5
6
End of Text
.
.
.
End of Transmission

Bei Start of Transmission geht der Zeiger des Auswertungspuffers auf 0, 
in dem letztendlich 5 uint16_t Zahlen gespeichert werden. Bei jedem 
Start of text geht der Zeiger des Zwischenpuffers auf 0. Nach jedem 
Textblock wir der Zeiger des Auswertungspuffer erhöht. Nach 5 Zahlen im 
Auswertungsarray wird geprüft, ob das letzte Zeichen ein End of 
Transmission ist. Dann wird das Array Ausgewertet.

von Frank K. (Gast)


Lesenswert?

so ich habe mal schnell was geschrieben:
1
    if ( data == END_TRANSMISSION ) {
2
      //Auswertung
3
    } else if ( data == START_TRANSMISSION ) {
4
      analysis_counter=0;
5
    } else {
6
      if ( data == END_TEXT ) {
7
        analysis_buffer[analysis_counter]=atoi(text_buffer);
8
        analysis_counter++;
9
      } else if (data==START_TEXT) {
10
        text_counter=0;
11
      } else {
12
        text_buffer[text_counter]=data;
13
        text_counter++;
14
      }
15
    }
16
    }

Den Pufferüberlauf muss ich noch einbauen. Es geht aber erstmal nur ums 
Prinzip, ob das so klappt.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Abgesehen von unglaublich langen Variablennamen und vielen Unterstrichen 
könnte ich damit leben... ;-)

> Den Pufferüberlauf muss ich noch einbauen.
"Die Pufferüberläufe" sollte das heissen.
Sonst läuft dir irgendwann u.U. z.B. dein analysis_counter Amok   :-o

von Frank K. (Gast)


Lesenswert?

ok das mit den Namen kann man noch ändern. Mir ist nur nichts besseres 
eingefallen auf die Schnelle. SO sollten die schütze eigentlich 
funktionieren:
1
if ( data == END_TRANSMISSION ) {
2
      //Auswertung
3
    } else if ( data == START_TRANSMISSION ) {
4
      analysis_counter=0;
5
    } else {
6
      if ( data == END_TEXT ) {
7
        analysis_buffer[analysis_counter]=atoi(text_buffer);
8
        if  (analysis_counter < 5) analysis_counter++;
9
      } else if ( data == START_TEXT ) {
10
        text_counter=0;
11
      } else {
12
        text_buffer[text_counter]=data;
13
        if (text_counter < 10) text_counter++;
14
      }
15
    }

von Karl H. (kbuchegg)


Lesenswert?

Wäre mir persönlich zu unübersichtlich und kryptisch.

Da Prozessorzeit hier keine Rolle spielt, würde ich das wie das 
Protokoll aufbauen (jede Protokollebene ist eine Funktion)

Dein Protokoll ist geschachtelt

Ein Record ist so aufgebaut
   "Start of Transmission"
   Parameter
   Parameter
   ...     (wieviele musst du wissen)
   "End of Transmission"

Ein Parameter ist so aufgebaut
    "Start of Text"
    Zahl in ASCII Form
    "End of Text"


Und genauso zerpflücke ich das auch in Funktionen
1
uint8_t ReceiveRecord( int Numbers[], int* NrNumbers)
2
{
3
  char c;
4
  int  i = 0;
5
6
  if( uart_getc() != SOTR )
7
    return FALSE;
8
9
  while( ( c = uart_getc() ) == SOT )  // solange ein SOT daherkommt,
10
                                       // kommt noch ein Parameter
11
    ReceiveParameter( &Numbers[i++] );
12
13
  if( c != EOTR )
14
    return FALSE;
15
16
  return TRUE;
17
}
18
19
uint8_t ReceiveParameter( int* Number )
20
{
21
  char c;
22
23
  // der einleitende SOT wurde schon empfangen und hat
24
  // dazugeführt, dass diese Funktion aufgerufen wurde
25
  //
26
  // Zahl einlesen und zusammensetzen.
27
  *Number = 0;
28
29
  while( isdigit( c = uart_getc() ) )
30
    *Number = 10 * *Number + ( c - '0' );
31
32
  return ( c == EOT );
33
}


SOTR, EOTR, SOT und EOT sind Makros hinter denen sich die Codes für 
Start Of TRansmission, End Of TRansmission, Start of Text, End Of Text 
verbergen.
Wie du die Number aus der Funktion rauskriegst (globale Variable, 
Parameter für die Funktion): entscheide dich für etwas.
Fehlerbehandlung bei Arrayoverflow muss auch noch rein.


Edit: Das ist ein blockierender Code. Soll heissen ein Aufruf von 
ReceiveRecord kommt erst dann zurück, wenn ein kompletter Datensatz 
eingelesen wurde. Das kann zu einem Problem werden oder auch nicht, je 
nachdem, wie der Rest des Programms aussieht und ob nebenher in der 
Hauptschleife noch andere Arbeiten zu erledigen sind.

von Frank K. (Gast)


Lesenswert?

Also das gefällt mir nicht so. Wie ich das sehe hängt das Porgramm ja an 
der Stelle:
1
while( ( c = uart_getc() ) == SOT )
2
    ReceiveParameter();

von Karl H. (kbuchegg)


Lesenswert?

Frank K. schrieb:
> Also das gefällt mir nicht so. Wie ich das sehe hängt das Porgramm ja an
> der Stelle:
>
1
> while( ( c = uart_getc() ) == SOT )
2
>     ReceiveParameter();
3
>

Was soll da hängen?
Solange SOT daherkommen beginnt ein neuer Parameter. Kommt kein SOT mehr 
wird abgebrochen (und wenn es dann auch kein SOTR war, ist was faul)

Oder meinst du 'Hängen' im Sinne von: Hauptschleife läuft nicht mehr. 
Also im Gegensatz zu: Eventgetriebens Auswerten.
Da hast du recht, hab ich aber auch im Nachsatz (siehe Edit) angemerkt.

von Frank K. (Gast)


Lesenswert?

Ja also es währe schon gut, wenn in der Hauptschleife weiter gearbeitet 
werden könnte. Also ich werde erstmal bei der obigen Lösung bleiben. Bei 
den paar Zeilen sieht man ja auch noch durch.

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.