RS232 Schnittstelle außerhalb der main Datei

Gast #1410927
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
}
Persönliche Seite #1410934
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!
Gast #1411254
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.
#1411259
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...
Gast #1411327
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];
Gast #1411379
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.
Gast #1411394
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.
Gast #1411399
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.
Gast #1411419
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.
Gast #1411456
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'
Gast #1411509
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.
Gast #1411533
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
Moderator (Firma: Titel) Persönliche Seite #1411591
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)"
Gast #1411612
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?
#1411912
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.
Gast #1412001
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
}
Gast #1413946
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.
Moderator (Firma: Titel) Persönliche Seite #1414660
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.
Gast #1415393
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.
Gast #1415425
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...
Gast #1415522
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.
Gast #1415544
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?
Gast #1415553
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
Gast #1415601
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.
Gast #1415633
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.
#1415832
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.
Gast #1415848
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.
Gast #1416108
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
  }
Moderator (Firma: Titel) Persönliche Seite #1416200
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
      }
Gast #1416241
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
}
Moderator (Firma: Titel) Persönliche Seite #1416372
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;
Gast #1416405
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.
#1416414
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.
Moderator (Firma: Titel) Persönliche Seite #1416524
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
Gast #1420132
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.
Moderator (Firma: Titel) Persönliche Seite #1422355
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.
Gast #1422385
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.
Moderator (Firma: Titel) Persönliche Seite #1422660
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"
Gast #1422820
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.
#1422967
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.
Gast #1422979
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.
Gast #1423075
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.
Gast #1423152
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
    }
#1423234
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.
#1423281
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren