Forum: Compiler & IDEs Puffer mit Zeigern oder Index


von sep (Gast)


Lesenswert?

Hey,

irgendwie habe ich gerade ein Problem, welches ich nicht so ganz 
verstehe. Ich bin gerade dabei mich ein bisschen vertrauter mit der 
USART Schnittstelle des ATmega88 machen und wollte dies 
Interrupt-Basiert machen. Ich habe hier nun einen Puffer. Befülle ich 
den mittels Indexen, funktioniert alles.
1
#ifndef F_CPU
2
#define F_CPU 3686400UL
3
#endif // F_CPU
4
5
#ifndef BAUD
6
#define BAUD 115200UL
7
#endif // BAUD
8
9
#define UBRR_VAL ( ( F_CPU / ( 16 * BAUD ) ) - 1 )
10
11
volatile uint8_t buffer[ 256 ];
12
volatile uint16_t in;
13
volatile uint16_t out;
14
15
int main( void ) {
16
  
17
  UBRR0 = UBRR_VAL;
18
  
19
  UCSR0A = 0;
20
  UCSR0B = ( ( 1 << RXCIE0 ) | ( 1 << RXEN0 ) | ( 1 << TXEN0 ) );
21
  UCSR0C = ( ( 1 << UCSZ01 ) | ( 1 << UCSZ00 ) );
22
  
23
  in = 0;
24
  out = 0;
25
  
26
  sei();
27
  
28
    while( 1 ) {
29
    if( in != out ) {    
30
      while( !( UCSR0A & ( 1 << UDRE0 ) ) );
31
      UDR0 = buffer[ out++ ];
32
    }
33
    }
34
  
35
}
36
37
ISR( USART_RX_vect ) {
38
  buffer[ in++ ] = UDR0;
39
}
Also, einfach ein kleiner Echo. Zum testen erst einmal ohne Boundary 
Check & RingBuffer.
Realisiere ich dies mit Zeigern, dann bekomme ich nichts zurück auf der 
seriellen Schnittstelle.
1
#ifndef F_CPU
2
#define F_CPU 3686400UL
3
#endif // F_CPU
4
5
#ifndef BAUD
6
#define BAUD 115200UL
7
#endif // BAUD
8
9
#define UBRR_VAL ( ( F_CPU / ( 16 * BAUD ) ) - 1 )
10
11
volatile uint8_t buffer[ 256 ];
12
volatile uint8_t *in;  // <-- Zeiger für Interrupt
13
volatile uint8_t *out;  // <-- Zeiger für main-loop zum lesen
14
15
int main( void ) {
16
  
17
  UBRR0 = UBRR_VAL;
18
  
19
  UCSR0A = 0;
20
  UCSR0B = ( ( 1 << RXCIE0 ) | ( 1 << RXEN0 ) | ( 1 << TXEN0 ) );
21
  UCSR0C = ( ( 1 << UCSZ01 ) | ( 1 << UCSZ00 ) );
22
  
23
  in = buffer;  // Anfang natürlich auf Buffer-Anfang
24
  out = buffer;
25
  
26
  sei();
27
  
28
    while( 1 ) {
29
    if( in != out ) {    
30
      while( !( UCSR0A & ( 1 << UDRE0 ) ) );
31
      UDR0 = *( out++ );  // Stelle des Zeigers dereferenzieren und danach eins weiter
32
    }
33
    }
34
  
35
}
36
37
ISR( USART_RX_vect ) {
38
  *( in++ ) = UDR0;  // Dito wie in main-loop
39
}
Es ist keine große Änderung dabei. Wo Änderungen sind, sind Kommentare.
Also, entweder stehe ich gerade auf dem Schlauch, oder es ist schon 
spät.
Wäre für eure Hilfe echt dankbar.

Viele Grüße
sep

von Rolf Magnus (Gast)


Lesenswert?

sep schrieb:
> volatile uint8_t *in;  // <-- Zeiger für Interrupt
> volatile uint8_t *out;  // <-- Zeiger für main-loop zum lesen

Hier hast du zwei Zeiger auf volatile uint8_t. Aber die Zeiger selbst 
sind nicht volatile.

von sep (Gast)


Lesenswert?

Hmm, gut, das kannte ich noch nicht.
Aber das funktioniert und klingt auch recht logisch wenn man dies 
bedenkt: http://en.wikipedia.org/wiki/Const-correctness

Danke. Also mit dem folgenden funktioniert es nun.
1
volatile uint8_t * volatile in;
2
volatile uint8_t * volatile out;

Viele Grüße
sep

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

sep schrieb:
>     if( in != out ) {

Auf in muß atomar zugegriffen werden!

von Peter D. (peda)


Lesenswert?

Ich bevorzuge die Indexe.
Einen Index kann man leicht auf Bereichsüberschreitung testen, einen 
wild gewordenen Pointer nicht so leicht.

Und wenn der Puffer <= 256 Byte ist, kann der Index 8-bittig sein, d.h. 
man spart deutlich Code gegenüber Pointern.

Damit sich die Indexe/Pointer nicht überholen, kann der 256 Byte Puffer 
nur mit max 255 Byte befüllt werden. Aber da die HW-UART 3 Byte puffert, 
hat man effektiv 258 Byte Puffer.

von sep (Gast)


Lesenswert?

Hey Peter,

Ich wollte einfach mal schauen, wie es denn umgesetzt wird. Auch wenn 
die Pointer 16 Bittig sind, habe ich weniger Code als mit Indexen. In 
diesem Bsp waren das knapp 10%.
Das wird wohl daher kommen, dass die Pointer immer im 1 Byte weiter 
bewegt werden. Das kann mit dem st X+, r24 bsp passieren. Da hab ich 
zwei Takte und der Zeiger ist gleich weiter bewegt. An sich find ich das 
eigentlich ganz kompakt.
Weiterhin, ob mein Index wild geworden ist, oder mein Zeiger... Das ist 
meiner Meinung nach nur ne Geschmacksfrage.

Viele Grüße
sep

von Peter D. (peda)


Lesenswert?

sep schrieb:
> Auch wenn
> die Pointer 16 Bittig sind, habe ich weniger Code als mit Indexen.

Dein Code ist ja auch nicht vollständig. Du schreibst Dir ja den ganzen 
RAM zu. Es fehlt noch der Rücksprung zum Anfang.

sep schrieb:
> Weiterhin, ob mein Index wild geworden ist, oder mein Zeiger... Das ist
> meiner Meinung nach nur ne Geschmacksfrage.

Für mich nicht. Wenn ein Pointer überschrieben wurde, zeigt er 
irgendwohin im RAM und Du suchst den Fehler an einer völlig falschen 
Stelle.

Z.B. Funktion X will ein int16_t schreiben und Du übergibst aber 
versehentlich die Adresse eines int8_t. Dahinter steht zufällig Dein 
Pointer für Puffer Y, der nun zerstört wird. Er zeigt dadurch in den RAM 
der Funktion Z, wo sich der Fehler bemerbar macht.
Du suchst Dich also dumm und dämlich in Funktion Z, statt im Aufruf von 
X.

von Amateur (Gast)


Lesenswert?

Das: "Was wäre wenn" ist ziemlich sinnlos, wenn der Code unvollständig 
ist.
Spätestens, wenn die Zeiger verglichen werden müssen, neigt sich die 
Waage schnell in Richtung Indexvergleich.
Klar *Zeiger++= sieht nach fast nix aus schau Dir aber mal an was 
dahintersteht.
Es gibt natürlich eine Ausnahme: Bei einem Riesenpuffer (>255)geht's 
auch beim Index richtig zur Sache.

von Rolf Magnus (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Ich bevorzuge die Indexe.
> Einen Index kann man leicht auf Bereichsüberschreitung testen, einen
> wild gewordenen Pointer nicht so leicht.
>
> Und wenn der Puffer <= 256 Byte ist, kann der Index 8-bittig sein, d.h.
> man spart deutlich Code gegenüber Pointern.

Bei jedem Zugriff auf ein Elemen muß dafür aber der Index erweitert und 
zur Basisadresse dazuaddiert werden. Ob das wirklich weniger Code 
erzeugt?

Peter Dannegger schrieb:
> Z.B. Funktion X will ein int16_t schreiben und Du übergibst aber
> versehentlich die Adresse eines int8_t. Dahinter steht zufällig Dein
> Pointer für Puffer Y, der nun zerstört wird. Er zeigt dadurch in den RAM
> der Funktion Z, wo sich der Fehler bemerbar macht.
> Du suchst Dich also dumm und dämlich in Funktion Z, statt im Aufruf von
> X.

Und inwiefern wäre das nun anders, wenn an der besagten Adresse ein 
Index steht und eben dadurch an die falsche Adresse geschrieben wird?

von Amateur (Gast)


Lesenswert?

>Bei jedem Zugriff auf ein Elemen muß dafür aber der Index erweitert und
>zur Basisadresse dazuaddiert werden. Ob das wirklich weniger Code
>erzeugt?

Das stimmt zwar, aber diese Addition erfolgt nur bei einem "echten" 
Zugriff alles andere spielt sich im Bereich 0 ... 255 ab.

Leer- und Vollabfrage, Indexarithmetik usw. sind Einfachstrechnungen. 
Wie gesagt: nur beim "echten" Lesen und Schreiben wird's langwieriger.

In der Praxis wird wohl die Leerabfrage am häufigsten sein. Ein 
einfacher Indexvergleich (Anfang==Ende).

Übrigens die Frage ob ein wildgewordener Index oder Zeiger schlimmer 
ist, halte ich für unsinnig.

von sep (Gast)


Lesenswert?

Naja. Nen Puffer muss man ja nicht immer als Ringpuffer implementieren. 
Für meine Anwendung (auch wenn der eine Zugriff nicht atomar war), waren 
es 10% weniger Code mit Zeigern.

Amateur schrieb:
> Das: "Was wäre wenn" ist ziemlich sinnlos, wenn der Code unvollständig
> ist.
> Spätestens, wenn die Zeiger verglichen werden müssen, neigt sich die
> Waage schnell in Richtung Indexvergleich.
> Klar *Zeiger++= sieht nach fast nix aus schau Dir aber mal an was
> dahintersteht.
> Es gibt natürlich eine Ausnahme: Bei einem Riesenpuffer (>255)geht's
> auch beim Index richtig zur Sache.
Ich vergleiche keine Zeiger. Ich setze sie nur. Der Puffer ist groß 
genug sodass alle Zeichen platz finden. Wenn nicht, habe ich mit einem 
Ringpuffer auch Probleme, dass meine älteren Zeichen überschrieben 
werden, oder die neueren weg fliegen, oder oder oder... Auch alles 
Probleme welche sich nicht leicht finden lassen.

> *Zeiger++ =
st X+, r16 <-- Mehr steckt nicht dahinter... Wenn denn der Compiler 
ordentlich optimiert, oder ich das mit inline ASM mache.

Amateur schrieb:
>>Bei jedem Zugriff auf ein Elemen muß dafür aber der Index erweitert und
>>zur Basisadresse dazuaddiert werden. Ob das wirklich weniger Code
>>erzeugt?
>
> Das stimmt zwar, aber diese Addition erfolgt nur bei einem "echten"
> Zugriff alles andere spielt sich im Bereich 0 ... 255 ab.
Ich spiele nicht mit den Zeigern rum, ich brauche sie nur als Hinweis, 
wo ich mich gerade mit dem schreiben, oder lesen befinde...

Also, ich denke schon, dass das ordentliche Arbeiten mit Zeigern kleiner 
und schneller ist. Zumindest für meinen Anwendungszweck. Kommt halt 
immer drauf an was man machen will.

Aber seis drum. Ich habs nun noch ein bisschen geändert und hab halt den 
einen Zeiger in den Registern zu liegen (So läuft der RX Int in ca 30 
Takten durch). Das gesamte Ergebnis schaut nun gerade so aus.
Für mich läuft es ziemlich gut.
1
#ifndef UART_RX_BUFFER_H_
2
#define UART_RX_BUFFER_H_
3
4
#include <avr/common.h>
5
#include <avr/interrupt.h>
6
#include <avr/io.h>
7
8
#ifndef UART_RX_BUFSIZ
9
#error "Define UART_RX_BUFSIZ!"
10
#elif !defined( UART_RX_INREG )
11
#error "Define UART_RX_INREG. A register where in_buffer is stored."
12
#else
13
14
volatile char uart_rx_buffer[ UART_RX_BUFSIZ ];
15
register char * uart_rx_buffer_in asm( UART_RX_INREG );
16
char volatile * uart_rx_buffer_out;
17
18
inline void uart_rx_buffer_reset( void ) {
19
  asm volatile (  "movw %[in], %[buf]"  "\n\t"
20
      :
21
      : [buf] "r" ( uart_rx_buffer ),
22
        [in] "r" ( uart_rx_buffer_in ) );
23
  uart_rx_buffer_out = uart_rx_buffer;
24
}
25
26
inline uint8_t uart_rx_buffer_tryGet( char **c ) {
27
  uint8_t _res_;
28
  uint8_t _sreg_ = SREG;
29
  cli();
30
  
31
  // Be shurt in is actual
32
  asm volatile( "" : "+r" ( uart_rx_buffer_in ) );
33
  _res_ = ( uart_rx_buffer_in != uart_rx_buffer_out );
34
  
35
  if( _res_ ) {
36
    SREG = _sreg_;
37
    *c = ( char * ) uart_rx_buffer_out++;
38
  }
39
  else {
40
    SREG = _sreg_;
41
  }
42
  
43
  return _res_;
44
}
45
46
inline char * uart_rx_buffer_getOutPtr( void ) {
47
  return ( char * ) uart_rx_buffer_out;
48
}
49
50
inline void uart_rx_buffer_setOutPtr( char * ptr ) {
51
  uart_rx_buffer_out = ( char volatile * ) ptr;
52
}
53
54
#define ASSIGN_UART_RX_FAST( vect, UDR ) ISR( USART_RX_vect, ISR_NAKED ) {  \
55
  register void *_x_ptr_ asm( "r26" );          \
56
  asm volatile(  "push __tmp_reg__"        "\n\t"  \
57
      "lds __tmp_reg__, %[udr]"      "\n\t"  \
58
      "push %A[ptr]"          "\n\t"  \
59
      "push %B[ptr]"          "\n\t"  \
60
      "movw %[ptr], %[in]"        "\n\t"  \
61
      "st %a[ptr]+, __tmp_reg__"      "\n\t"  \
62
      "movw %[in], %[ptr]"        "\n\t"  \
63
      "pop %B[ptr]"          "\n\t"  \
64
      "pop %A[ptr]"          "\n\t"  \
65
      "pop __tmp_reg__"        "\n\t"  \
66
      "reti"            "\n\t"  \
67
      : [ptr] "+x" ( _x_ptr_ )        \
68
      : [udr] "M" ( _SFR_MEM_ADDR( UDR ) ),      \
69
        [in] "r" ( uart_rx_buffer_in ) );      \
70
}
71
  
72
#endif // UART_RX_BUFSIZ
73
74
#endif // UART_RX_BUFFER_H_

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

sep schrieb:
> Ich vergleiche keine Zeiger. Ich setze sie nur. Der Puffer ist groß
> genug sodass alle Zeichen platz finden. Wenn nicht, habe ich mit einem
> Ringpuffer auch Probleme, dass meine älteren Zeichen überschrieben
> werden, oder die neueren weg fliegen, oder oder oder...

Es ist ein Unterschied, ob ein FIFO überläuft (d.h. alte empfangene 
Zeichen überschrieben werden) oder ob das gesamte RAM überschrieben 
wird. Für letzteres sorgt Deine Vorgehensweise.

Klar, Datenverlust beim Empfang ist ein Fehler, aber der Rest des 
Programmes könnte ja störungsfrei weiterlaufen. Killt man aber das RAM, 
geht gar nichts mehr.

Setzt Du denn jemals Deine Zeiger wieder zurück? Oder gehst Du davon 
aus, daß Du während der Laufzeit Deines Programmes nie mehr als x Bytes 
an Daten empfangen möchtest?

von sep (Gast)


Lesenswert?

Oh, bevors kommt, das reset muss ja auch atomar sein! ;)

von sep (Gast)


Lesenswert?

Ich setze den zurück. Jeweils wenn ein Befehl, welcher über den USART 
rein gekommen ist abgearbeitet ist, oder wenn ein Timeout eingetreten 
ist. Durch das Protokoll ist sichergestellt, dass immer nur ein Befehl 
auf einmal gesendet wird.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

sep schrieb:

Und schon hat man einen Fehler im Code durch unnötige Inline Asm 
Kapriolen...

Viel Spaß beim Suchen :-P

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

sep schrieb:
> Durch das Protokoll ist sichergestellt, dass immer nur ein Befehl
> auf einmal gesendet wird.

Aha. Und was geschieht, wenn die Gegenseite das Protokoll nicht einhält? 
Was, wenn durch eine Störung die UART "Dreck" empfängt?

von sep (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> sep schrieb:
>> Durch das Protokoll ist sichergestellt, dass immer nur ein Befehl
>> auf einmal gesendet wird.
>
> Aha. Und was geschieht, wenn die Gegenseite das Protokoll nicht einhält?
> Was, wenn durch eine Störung die UART "Dreck" empfängt?

Dann werden die Zeichen sofort aus dem Puffer gelöscht.

Johann L. schrieb:
> Und schon hat man einen Fehler im Code durch unnötige Inline Asm
> Kapriolen...
>
> Viel Spaß beim Suchen :-P

Währe gut zu wissen wo.
Aber so viel inline Assembler ist es ja nicht. Und hier in dem Interrupt 
muss ich das SREG nicht sichern, da weder push, pop, lds, movw, noch st 
X+ Einfluss auf das SREG haben. höchstens reti. Aber das soll ja so! ;)

Wenn ich was übersehen habe, würde ich dir dankbar sein, wenn du mir 
sagst, was es ist. ^^

von Amateur (Gast)


Lesenswert?

Sep hat entschieden das Zeiger weniger Platz und Rechenzeit benötigen.
Vor allem dann, wenn unbegrenzt Speicher, wie bei den Meisten hier 
verwendeten Prozessoren (*Tiny, *Mega, PIC und Konsorten), vorhanden 
ist.

von Karl H. (kbuchegg)


Lesenswert?

Was genau ist der Grund für die Assembler-Origie.
Ich seh da noch keinen wirklichen Grund dafür?
So ineffizient kann doch der Compiler ein
1
ISR( USART_RX_vect ) {
2
  *( in++ ) = UDR0;  // Dito wie in main-loop
3
}
gar nicht auflösen.
Wenn der volatile Pointer stört, dann caste das volatile weg oder benutz 
eine Hilfsvariable, die keine volatiles hat (optimiert der COmpiler 
sowieso wieder raus)

von sep (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Was genau ist der Grund für die Assembler-Origie.

Erstens einmal eine Entscheidung von mir aus.
Zweitens wird so weder SREG noch r1 gepusht/pepoppt weswegen auch noch 
einmal ein bisschen was weg fällt. Wie ich auch gleich unten schreibe, 
ich brauche Platz auf dem Flash.

Amateur schrieb:
> Sep hat entschieden das Zeiger weniger Platz und Rechenzeit benötigen.
> Vor allem dann, wenn unbegrenzt Speicher, wie bei den Meisten hier
> verwendeten Prozessoren (*Tiny, *Mega, PIC und Konsorten), vorhanden
> ist.

Sry, ich weiß ja nicht, was los ist. Aber ich denke mal, dass dies jeder 
für sich entscheiden muss und sollte. In meinem Fall kann ich nun einmal 
in paar mehr Bytes im RAM ablegen. Kleiner und schneller ist es in 
meinem Fall auch noch. Da ich mehr auf den Flash achten muss, als auf 
den SRAM. Wenn ich dort was zur Verfügung habe, warum sollte ich das 
denn brach liegen lassen. Wenn ich im Flash Notstand habe, warum sollte 
ich den dann verschwenden.

Eigentlich hatte ich das µC-Forum einmal anders in Erinnerung. Geholfen
wurde mir ja nach meinem Anfangspost. Nun weiß ich aber nicht, was das
Problem ist.
Ich klink mich dann hier erst einmal aus.

Grüße
sep

von Peter D. (peda)


Lesenswert?

Rolf Magnus schrieb:
> Und inwiefern wäre das nun anders, wenn an der besagten Adresse ein
> Index steht und eben dadurch an die falsche Adresse geschrieben wird?

Ich muß ja den Index mit der Größe des Ringpuffers vergleichen, um ihn 
wieder auf Anfang zu setzen. Und wenn man das geschickter Weise vor dem 
Schreibzugriff macht, können keine Daten außerhalb des Puffers 
überschrieben werden.

Aber das habe ich auch erst lernen müssen, daß man sichere Konstrukte 
bevorzugen sollte.

Ist bestimmt jedem mal passiert, daß man tagelang den Fehler an der 
falschen Stelle sucht und dann am liebsten in die Tischkante beißen 
würde ob der eigenen Dämlichkeit.

von Peter D. (peda)


Lesenswert?

sep schrieb:
> asm volatile(

Ehe ich die gefährliche Asm-Axt ansetze, überlege ich mir 100-mal, ob 
das unbedingt nötig ist und die 3 gesparten Bytes/Zyklen wirklich über 
Wohl und Wehe des Programms entscheiden.

von Amateur (Gast)


Lesenswert?

Um Himmels Wilhelm.
Du sollst Dir doch keine andere Meinung aufzwingen lassen. Wenn in 
Deinem speziellen Fall, das mit den Zeigern besser ist... Warum nicht!

Allerdings würde mich der Algorithmus hinter der folgenden Aussage mal 
interessieren:

>> Aha. Und was geschieht, wenn die Gegenseite das Protokoll nicht einhält?
>> Was, wenn durch eine Störung die UART "Dreck" empfängt?

>Dann werden die Zeichen sofort aus dem Puffer gelöscht.

...

von sep (Gast)


Lesenswert?

Amateur schrieb:
> Allerdings würde mich der Algorithmus hinter der folgenden Aussage mal
> interessieren:
>
>>> Aha. Und was geschieht, wenn die Gegenseite das Protokoll nicht einhält?
>>> Was, wenn durch eine Störung die UART "Dreck" empfängt?
>
>>Dann werden die Zeichen sofort aus dem Puffer gelöscht.

Es steckt eine FSM dahinter.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

sep schrieb:
>> Aha. Und was geschieht, wenn die Gegenseite das Protokoll nicht einhält?
>> Was, wenn durch eine Störung die UART "Dreck" empfängt?
>
> Dann werden die Zeichen sofort aus dem Puffer gelöscht.

Deine Empfangsroutine --die Du hier gepostet hast-- macht das nicht. Wer 
setzt dann den Pointer wieder zurück?

von Rolf Magnus (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Rolf Magnus schrieb:
>> Und inwiefern wäre das nun anders, wenn an der besagten Adresse ein
>> Index steht und eben dadurch an die falsche Adresse geschrieben wird?
>
> Ich muß ja den Index mit der Größe des Ringpuffers vergleichen, um ihn
> wieder auf Anfang zu setzen.

Und wenn ich stattdessen einen Pointer nehme, muß ich ihn halt mit 
Puffer-Ende vergleichen.

> Und wenn man das geschickter Weise vor dem
> Schreibzugriff macht, können keine Daten außerhalb des Puffers
> überschrieben werden.
>
> Aber das habe ich auch erst lernen müssen, daß man sichere Konstrukte
> bevorzugen sollte.

Ich sehe das etwas anders. "Selbstheilender" Code kann einem die Sache 
auch erschweren. Lieber habe ich ein Programm, das definiert abstürzt, 
als eins, das zwar unauffällig weiterläuft, aber immer wieder 
zwischendrin mal ein falsches Ergebnis produziert, weil duch einen 
Fehler irgendwo ein falscher Index steht, der aber immer gleich wieder 
repariert wird. Da ist der Fehler dann nämlich noch schwerer zu finden.
Viele machen bei Prüfung auf Puffer-Ende lieber ein <= als ein !=, damit 
sich das Programm "regeneriert", falls der Index mal zu groß ist. Ich 
mache gerade das nicht, denn ich will lieber den Grund für den falschen 
Index finden, was meist leichter ist, wenn der Fehler nicht vertuscht 
wird.

Peter Dannegger schrieb:
> Ist bestimmt jedem mal passiert, daß man tagelang den Fehler an der
> falschen Stelle sucht und dann am liebsten in die Tischkante beißen
> würde ob der eigenen Dämlichkeit.

Ja. Aber mit deiner Methode hast du diesen Fehler nicht gefunden oder 
gar behoben, sondern einfach nur etwas besser versteckt. Da ist er immer 
noch.

von Peter D. (peda)


Lesenswert?

Rolf Magnus schrieb:
> Ja. Aber mit deiner Methode hast du diesen Fehler nicht gefunden oder
> gar behoben, sondern einfach nur etwas besser versteckt.

Nö, ich habe verhindert, daß er andere Routinen stört. Ich bin also mit 
der Fehlersuche ein Stufe näher an der Ursache dran.

Würde er z.B. die RTC stören, würde ich erstmal ewig im Timerinterrupt 
rumsuchen.

von sep (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> sep schrieb:
>>> Aha. Und was geschieht, wenn die Gegenseite das Protokoll nicht einhält?
>>> Was, wenn durch eine Störung die UART "Dreck" empfängt?
>>
>> Dann werden die Zeichen sofort aus dem Puffer gelöscht.
>
> Deine Empfangsroutine --die Du hier gepostet hast-- macht das nicht. Wer
> setzt dann den Pointer wieder zurück?

Aber sie bietet die Funktionalität. mittels reset kannst du nämlich den 
Puffer zurück setzen. Alles andere geschieht eine Schicht weiter oben.
Wie gesagt, steht dort eine FSM. Diese kennt das Protokoll und kann 
entsprechend reagieren. Der Puffer kann dies nicht. Weil dieser nicht 
weiß, was gesprochen wird. So bleibt alles schön modular.

von sep (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Peter Dannegger schrieb:
>> Ist bestimmt jedem mal passiert, daß man tagelang den Fehler an der
>> falschen Stelle sucht und dann am liebsten in die Tischkante beißen
>> würde ob der eigenen Dämlichkeit.
>
> Ja. Aber mit deiner Methode hast du diesen Fehler nicht gefunden oder
> gar behoben, sondern einfach nur etwas besser versteckt. Da ist er immer
> noch.

Sry, wenn ich nun doppelt poste. Ich halte das auch so wie Rolf. Denn es 
sollte ein Modul für genau eine Aufgabe geben. Sollten bei der Benutzung 
Fehler auftreten, dann kann man die nach verschiedenen Vorgehensweisen 
suchen. Das ist auch wieder Geschmackssache. Aber das Fehler durch 
untere Module ausgebügelt werden halte ich auch für nicht richtig. Denn 
der Fehler muss in der Benutzung gefunden und behoben werden.
Zur Nutzung von Modulen gibt es Dokumentationen. Daran sollte man sich 
dann auch halten. Diese ist in meinem Falle nun auch vorhanden. Dort 
stehen auch Hinweise darauf dass man halt den Puffer zurück setzen muss. 
Wer sich nicht daran hält, muss halt mit den Konsequenzen hier rechnen.
Aber so ist das halt. Wer das eine will muss das andere mögen.

Grüße
sep

von Bastler (Gast)


Lesenswert?

Ich glaube PeDa geht es einfach nur um robuste Routinen. Solche, die 
nicht gleich komplett ausrasten, wenn irgendwo ganz anders etwas falsch 
läuft oder gleich wie ein Kartenhaus zusammenfallen, wenn man wo ganz 
anders eine kleine Änderung macht.
Wenn ich mir aber die "C-Lösung" oben anschaue, dann frage ich mich: 
warum nicht gleich als S-File?

von Peter D. (peda)


Lesenswert?

sep schrieb:
> Aber das Fehler durch
> untere Module ausgebügelt werden halte ich auch für nicht richtig.

Wird er ja nicht, sie wird weiterhin falsche Daten liefern.
Aber sie wird nicht eine völlig unbeteiligte Routine stören und dadurch 
den Fehler verschleiern.

Es ist schon ein großer Unterschied, ob ein Fehler sich nur in der 
beteiligten Routine bemerkbar macht oder sich irgendwo ein Zufallsopfer 
aussucht. Und auch, wenn man den Verdacht hat, daß ein Pointer Unfug 
treibt, weiß man noch lange nicht, welcher.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

sep schrieb:
> Der Puffer kann dies nicht. Weil dieser nicht
> weiß, was gesprochen wird. So bleibt alles schön modular.

In meinen Augen ist ein Puffer ohne Überlaufsicherung ein Designfehler.

von Rolf Magnus (Gast)


Lesenswert?

Peter Dannegger schrieb:
> sep schrieb:
>> Aber das Fehler durch
>> untere Module ausgebügelt werden halte ich auch für nicht richtig.
>
> Wird er ja nicht, sie wird weiterhin falsche Daten liefern.
> Aber sie wird nicht eine völlig unbeteiligte Routine stören und dadurch
> den Fehler verschleiern.

Wie kommst du darauf? Dein Sendepuffer wird immer noch Mist machen. Du 
hast zwar keinen out-of-Bounds-Zugriff mehr, aber wenn der Index 
überschrieben wird, können zwei Dinge passieren: Der Wert ist "out of 
bounds", dann wird er hart auf 0 gesetzt, oder er ist zufällig im 
gültigen Bereich, dann wird halt an dieser zufälligen Stelle im Puffer 
weitergemacht. In beiden Fällen sind irgendwelche wilden Sprünge im 
Puffer das Resultat. Und damit hast du dann genau gar nichts gewonnen. 
Es gibt immer noch merkwürdige Fehler in der unbeteiligten Routine.

> Es ist schon ein großer Unterschied, ob ein Fehler sich nur in der
> beteiligten Routine bemerkbar macht oder sich irgendwo ein Zufallsopfer
> aussucht.

Da gebe ich dir recht.

von sep (Gast)


Lesenswert?

Bastler schrieb:
> Wenn ich mir aber die "C-Lösung" oben anschaue, dann frage ich mich:
> warum nicht gleich als S-File?

Naja. Hier steht nun alles in einer Header-File. Ich kann das Ding 
nacher in eine Lib auslagern und kann Projektspezifisch die Interrupts 
zentral "binden". (So hab ich alle Interrupts auf einen Blick)
Dies geschieht in diesem Falle durch
1
ASSIGN_UART_RX_FAST( USART_RX_vect, UDR0 )
Weiterhin sind die Funktionen alle geinlined. Da die ja doch recht 
kompakt sind, bzw in der Regel nur einmal aufgerufen werden.

Rufus Τ. Firefly schrieb:
> sep schrieb:
>> Der Puffer kann dies nicht. Weil dieser nicht
>> weiß, was gesprochen wird. So bleibt alles schön modular.
>
> In meinen Augen ist ein Puffer ohne Überlaufsicherung ein Designfehler.

Da ich euch nicht mein Programm zeigen möchte und meine

Rufus Τ. Firefly schrieb:
> sep schrieb:
>> Der Puffer kann dies nicht. Weil dieser nicht
>> weiß, was gesprochen wird. So bleibt alles schön modular.
>
> In meinen Augen ist ein Puffer ohne Überlaufsicherung ein Designfehler.

In meinen Augen sollte man die Anforderungen und das gesamte System 
abschätzen. Ansonsten ist es ein Fehler solch eine Designentscheidung 
als Fehler abzutun.
Die Ursprungsfrage drehte sich um etwas ganz anderes. Aber trotzdem muss 
jeder (auch wenn er keine Ahnung von den Randbedingungen hat), seinen 
Senf dazu geben. Wie gesagt ist es meine Entscheidung und die Größe und 
Performance zahlen sich bei mir hier sehr viel besser aus, als die 
Sicherung.
Weiterhin ist es ja nun nicht so dass es gänzlich nichts gibt was den 
Puffer zurück setzt. Es steht eine FSM dahinter. die das Protokoll kennt 
und sehr viel besser reagieren kann als der Puffer selbst.
Die Überlaufsicherung passiert halt nur eine Ebene weiter oben. Die 
Grundfunktionalität reicht für den Puffer aber vollkommen aus.

Weiter habe ich nun langsam wirklich keine Lust mehr mich zu 
rechtfertigen. Ich habe nun schon des öfteren beschrieben warum ich 
meine Designentscheidung so gewählt habe, wie ich es getan hab. Wenn ihr 
das nicht versteht, dann fragt ordentlich oder behaltet eure Meinung für 
euch.

Grüße
sep

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

sep schrieb:
> Wenn ihr das nicht versteht, dann fragt ordentlich oder
> behaltet eure Meinung für euch.

Bist Du Dir sicher, daß das der geeignete Umgangston für dieses Forum 
hier ist?

von W.S. (Gast)


Lesenswert?

sep schrieb:
> irgendwie habe ich gerade ein Problem, welches ich nicht so ganz
> verstehe.

Aber ich verstehe dein Problem: Du hast einfch drauflos programmiert, 
ohne vorher nachzudenken.

Ein Ringpuffer dient eigentlich IMMER dazu, zwei asynchron laufende 
Domänen voneinander zu trennen, hier z.B. deine Endlosschleife vom 
Interrupt-Geschehen. (Gilt auch für Hardware-FIFO).

Also soll jede Domäne NUR den Teil sehen, der ihr auch zusteht. Das wäre 
für die Schleife in main eine Funktion im UART-Treiber, die meinetwegen
char GetCharFromUART(void);
heißt. Und bei dieser Funktion MUSS !! man VORHER bedenken, wie sie sich 
verhalten soll, wenn grad nix anliegt. Genauso MUSS man vorher bedenken, 
wie sich die Interrupt-Routine benehmen soll, wenn der Puffer immer noch 
voll ist.

Ansonsten ist das Arbeiten mit Indizes bedeutend günstiger, denn man muß 
ja ne Rechnung aufmachen, welchen Wert man beim Beladen und Entladen des 
Ringpuffers in den jeweiligen Index zurückschreibt. EInen Index setzt 
man nach Überschreiten des letzten Elements im Array auf 0 zurück, das 
ist einfacher als das Beladen eines Zeigers mit ner Startadresse. Wenn 
man strikt 2er Potenzen für die Puffergröße nimmt, reicht dafür ein AND 
aus, bei krummen Puffergrößen muß man MOD benutzen. All das fehlt deinem 
Beispiel.

Johann L. schrieb:
> Auf in muß atomar zugegriffen werden!

Das ist fast ein Unfug, weil viel zu grob dargestellt. Richtig ist, daß 
man von jeder Seite aus auf den Index oder Pointer der Gegenseite nur 
lesend zugreifen darf und dieses sollte dann auch atomar geschehen. 
Wenn also ein µC einen Pointer nicht in einem Maschinenbefehl lesen 
kann, sondern dafür 2 oder 3 Zugriffe braucht, dann scheidet die 
Pointerversion schon deshalb aus - der Interrupt könnte ja 
dazwischenkommen. Indizes über übliche Puffer sind meist bloß 8 Bittig 
und das kann eigentlich jeder µC mit einem Befehl.

W.S.

von Peter D. (peda)


Lesenswert?

Rolf Magnus schrieb:
> Wie kommst du darauf? Dein Sendepuffer wird immer noch Mist machen.

Ganz genau!
Sag ich doch schon die ganze Zeit.

Nur der macht Mist und nicht irgendwas zufälliges anderes, wo ich erst 
unnötig Zeit bräuchte, um dahinter zu kommen, daß es eigentlich die 
UART-Routine ist, bzw. daß sie gestört wird.
Ich spare also einen aufwendigen Debugschritt ein.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Das was Peter und andere hier propagieren, nenne ich gerne "defensives 
programmieren".

Was wenn du an deiner FSM schraubst, und die halt mal zufällig den 
Überlauf nicht abfängt? oder wenn du irgendwann den Code entdeckst, und 
den low-level-teil ohne FSM als super geeignet für ein anderes Projekt 
erachtest?

ich würde die Aussage "In meinen Augen ist ein Puffer ohne 
Überlaufsicherung ein Designfehler." sogar noch verstärken: eine schwere 
Designsünde, und im Wiederholungsfall ein Kündigungsgrund.

von sep (Gast)


Lesenswert?

> Was wenn du an deiner FSM schraubst, und die halt mal zufällig den Überlauf 
nicht abfängt?

Dann werd ich wohl in der FSM nen Fehler haben und diesen beheben.

> oder wenn du irgendwann den Code entdeckst, und den low-level-teil ohne FSM als 
super geeignet für ein anderes Projekt erachtest?

Dann werd ich das wohl für dieses Projekt wieder verwenden.

von sep (Gast)


Lesenswert?

W.S. schrieb:
> Ein Ringpuffer dient eigentlich IMMER dazu,

Wer schreibt denn vor, dass ein IMMER ein Ringpuffer verwendet wird.
Natürlich hat der Ringpuffer seine Vorteile. Jedoch sollte man diese 
genauso abwägen. Dieser ist zwar sicherer als ein einfacher Puffer, aber 
zum einen auch komplexer (damit Fehleranfälliger) und auch um einiges 
langsamer und größer (da immer wieder die Grenzen geprüft werden 
müssen). Ein einfacher Puffer ist um einiges schneller, simpler und 
einfacher. Dafür fehlt aber einem die Sicherheit. Diese muss man dann 
wieder an anderen Stellen investieren. Es ist hier auch wieder eine 
Abwägung welche natürlich geprüft werden muss. In meinem Falle ist es 
nun aber einmal so dass ein Rücksetzen des Puffers alle Nachteile wieder 
wett macht.
Wer sagt, dass ich einen Puffer verwende der kleiner als 256 Bytes ist? 
Ich brauche dort etwas mehr und dies passt alles ordentlich in den RAM 
(der ist ja bei weitem größer als 256 Byte!) und nein, zufälligerweise 
ist das nun auch leider keine Zweierpotenz mehr. Somit würde ich dort 
auch wieder auf 16 Bit ausweichen müssen und dann fallen mir alle 
arithmetischen Operationen wieder zur Last.

W.S. schrieb:
> Wenn man strikt 2er Potenzen für die Puffergröße nimmt, reicht dafür ein
> AND aus, bei krummen Puffergrößen muß man MOD benutzen. All das fehlt
> deinem Beispiel.

Wie gesagt. keine 2er Potenzen. Mod würde ich so oder so nicht 
verwenden. Da dies viel zu aufwendig ist. lieber würde ich den Zeiger 
auf den Anfang setzen oder dann den Index auf 0. Da so oder so nur 
Byte-weise und nicht Blockweise geschrieben wird. Ist um einiges 
effizienter als ein MOD.

Grüße
sep

von Olga (Gast)


Lesenswert?

Mal eine Frage: Wieviel tausend Stück willst du produzieren, dass sich 
der ganze Aufwand lohnt, um jedes einzelne Byte zu kämpfen, anstatt nen 
größeren Controller zu nehmen?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

sep schrieb:
> Dieser ist zwar sicherer als ein einfacher Puffer, aber
> zum einen auch komplexer (damit Fehleranfälliger)

Du bist lustig. Fehleranfällig ist vor allem Dein Puffer ohne 
Grenzprüfung.

> und auch um einiges langsamer und größer (da immer wieder
> die Grenzen geprüft werden müssen).

Sofern die Puffergröße eine Zweierpotenz ist, und mit einem Index 
gearbeitet wird, ist eine einzige AND-Verknüpfung erforderlich. Auch 
eine simple Werteabfrage mit nachfolgender Zuweisung oder eine 
Modulo-Anweisung ist kein Beinbruch, und bei Pufferung von mit seriellen 
Schnittstellen empfangenen Daten ist Geschwindigkeit nicht so kritisch, 
daß es auf ein paar Taktzyklen mehr oder weniger ankommt.
Tut es das wider Erwarten doch, liegt möglicherweise ein Problem im 
Gesamtdesign vor.

Auch hier: Wenn die Verwendung einer anderen Adressierungsart durch den 
Controller bereits ein Geschwindigkeitsproblem darstellt, liegt ein 
Problem im Gesamtdesign vor.

sep schrieb:
> Somit würde ich dort auch wieder auf 16 Bit ausweichen
> müssen und dann fallen mir alle arithmetischen Operationen
> wieder zur Last.

Wenn simple 16-Bit-Operationen "zur Last fallen", liegt definitiv ein 
Problem im Gesamtdesign vor.
Mit welcher Datenrate bitte empfängst Du Daten auf Deiner seriellen 
Schnittstelle, und welche Wunderdinge stellt Deine "FSM" damit an?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

sep schrieb:
> Wenn ich was übersehen habe, würde ich dir dankbar sein, wenn du mir
> sagst, was es ist. ^^

Im ersten asm wird ein Operand gesetzt / verändert, obwohl er 
Inlput-Operand ist.

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.