Im Interrupt soll sequentiell in ein Array geschrieben werden.
Später wird es in main() ausgelesen (ebenfalls sequentiell).
Was ist der Unterschied zwischen Zugriff per Index und Pointer?
Wie müssen array, index/pointer deklariert werden (volatile)?
Wie lässt sich bei der Variante mit Pointer das Ende des Arrays
erkennen?
SD schrieb:> Was ist der Unterschied zwischen Zugriff per Index und Pointer?
Für den Compiler praktisch nix.
> Wie müssen array, index/pointer deklariert werden (volatile)?
Alles Variablen oder Arrays, welche sowohl im Hauptprogramm und in der
ISR gelesen doer geschrieben werden. D.h. hier das Array, in welches
geschrieben werden soll. ggf. der Index, wenn der auch im Hauptprogramm
benutzt wird.
> Wie lässt sich bei der Variante mit Pointer das Ende des Arrays> erkennen?
if (pointer >= &array+sizeof(array)) mach_was()
Bastler_HV schrieb:> Nach meinen rüdimentären C-Kenntnissen sollte das so gehen:
Geht es nicht.
>> if( ++pointer < array[SIZE] )
Wenn man die Adresse haben will, dann eher
&array[SIZE] oder array+sizeof(array)
Falk B. schrieb:> if (pointer >= &array+sizeof(array)) mach_was()
Es ist zeitraubend und umständlich bei jedem Durchlauf das
Ende des Arrays neu zu berechnen da es sich in seiner Grösse
und Lage im Speicher nicht ändert.
Es genügt also einmal das Ende des Arrays zu berechnen
und bei jedem Durchlauf mit einer Ende-Variablen zu vergleichen.
Kann eine Menge Rechenzeit sparen.
Ein Compiler wird nicht so vorausschauend handeln und diese
Vorausberechnung in Registern oder eigenständig angelegten
Variablen vorhalten.
Falk B. schrieb:> Wenn man die Adresse haben will, dann eher>> ... oder array+sizeof(array)
Nein, weil sizeof Bytes liefert, die Arrayarithmetik aber die Größe
eines Elements berücksichtigt.
Nebenbei macht der Compiler aus array[index] erstmal *(array+index)
(daran sieht man dann, das man array und index vertauschen kann)
Daher
&array[SIZE] oder array+SIZE
oder
array+sizeof(array)/sizeof(*array)
was aber nur im Scope funktioniert, in dem auch array definiert ist.
Mitlesa schrieb:> Falk B. schrieb:>> if (pointer >= &array+sizeof(array)) mach_was()>> Es ist zeitraubend und umständlich bei jedem Durchlauf das> Ende des Arrays neu zu berechnen da es sich in seiner Grösse> und Lage im Speicher nicht ändert.
Sicher, aber denkst du, ein halbwegs aktueller Compiler kann das nicht
erkennen und mach da keinen Konstantenvergleich draus?
> Es genügt also einmal das Ende des Arrays zu berechnen> und bei jedem Durchlauf mit einer Ende-Variablen zu vergleichen.> Kann eine Menge Rechenzeit sparen.
Siehe oben.
> Ein Compiler wird nicht so vorausschauend handeln und diese> Vorausberechnung in Registern oder eigenständig angelegten> Variablen vorhalten.
Viel besser! Mit einer KONSTANTE!
Falk B. schrieb:> if (pointer >= &array+sizeof(array)) mach_was()
C-Pointer sind anscheinend auch 50 Jahre nach ihrer Erfindung immer noch
ein Mysterium.
Bastler_HV schrieb:> Wenn der Pointer kleiner ist, kannst Du in das Array reinschreiben.
Vorsicht, ein wild gewordener Pointer kann auch unter das Array zeigen.
Das kracht dann auch.
Stefan ⛄ F. schrieb:> Für den Prozessor ist beides identisch.
Nicht ganz.
Auf einem 8Bitter (AVR) ergibt ein Index vom Typ uint8_t schnelleren und
kürzeren Code (nur ein Byte im RAM). Auch ist der Zugriff darauf atomar.
Ein Index läßt sich auch einfacher testen, ob außerhalb des Arrays (nur
ein Vergleich nötig).
"array[index++] = value;
*(pointer++) = value;"
Sollte identisch sein. Hangt bisschen davon ab wo die Variablen sich
befinden. Ist der pointer ein function parameter der auf den Stack liegt
oder ist es global und liegt in der small data array. Davon hangt ab ob
der Controller etwas noch laden muss, sprich pointer lesen in irgend
eine Register oder nicht.
"Im Interrupt soll sequentiell in ein Array geschrieben werden.
Später wird es in main() ausgelesen (ebenfalls sequentiell)."
Prinzipiell hast du 2 Kontexte: Main und ISR. Wo der ISR zwischen jede
beliebige ASM instruktion reinkommen kann. Einfach dein C Code in
disassembly angucken und vorstelen was könnte passieren wenn hier oder
da der ISR kommt und etwas da zwischen macht.
Beispiel:
int testVar = 0;
void main (void) {
if (testVar) {
testVar = 0;
}
}
void ISR(void) {
testVar = 1;
}
Das sollte so ablaufen:
ISR testVar = 1;
MAIN if (testVar) {
MAIN testVar = 0;
könnte aber auch so ablaufen:
ISR testVar = 1;
MAIN if (testVar) {
ISR testVar = 1; <=2te interrupt
MAIN testVar = 0; <=hups da werde die Daten aus den 1sten und 2ten
Interrupt gleichzeitig gelöscht.
Basierend auf diese Beispiel kann man auch komplexere
Datenmanipulationen sich vorstellen, wo der eine Kontext den anderen
Kontext in die Quere kommt.
Lösung ist ganz einfach. Mann muss entweder Buffers verwenden die
Thread-Safe sind (oder halt Kontext-Sicher) oder atomare Befehle.
Thread-Safe Buffer:
- Ein Ringbuffer soll in sich selbst Thread safe sein, ohne dabei den
Interrupt/Prio zu sperren müssen. Ich meine atomare Zugriffe werden aber
doch gebraucht.
- Ein Fifo braucht schon interruptsperre.
Atomare Befehle:
Das sind Befehle die nicht unterbrochen werden können. Zum Beispiel
könnte das auf bestimmte Architekturen atomar sein:
volatile int testVar = 0;
void main(void) {
volatile int testVarTemp = 0;
testVarTemp = testVar; <= Könnte auf ein 32bit System atomar
ausgelesen werden, weil es nur 1 enzigen ASM befehl braucht. Oder 2 weil
addressenoffset vorbereiten, und dann damit offset lesen, aber das Lesen
ist auch in dem Fall atomar.
Lesen ist oft zu wenig. Man braucht meist einen Test und Löschen. Also
sowas:
if (testVar) {//ist aber so in sich selbst nicht atomar
testVar = 0;
...
}
Wenn es nicht durch ASM geht (manche Compilers bieten atomare librarys
an), dann kann man immernoch interruptsperren nehmen. Ist zwar in
manchen Systemen langasam (wo ein OS existier und über SystemCalls
geht), sonst eigentlich sehr schnell.
lock_isr();
testVarTemp = testVar;
testVar = 0;
unlock_isr();
if (testVarTemp) {
...
}
Selbe gilt auch für den Speicher:
unsigned char tempBuffer[2];
void ISR(void) {
tempBuffer[0] = 0xde;
tempBuffer[1] = 0xad;
}
void main(void) {
lock_isr();//Nicht zu lange sperren, sonst tut das system nicht mehr
if (tempBuffer[0] != 0x00) {
//process buffer
//clear buffer
tempBuffer[0] = 0x00;
tempBuffer[1] = 0x00;
}
unlock_isr();
}
kyrk.5 schrieb:> könnte aber auch so ablaufen:> ISR testVar = 1;> MAIN if (testVar) {> ISR testVar = 1; <=2te interrupt> MAIN testVar = 0; <=hups da werde die Daten aus den 1sten und 2ten> Interrupt gleichzeitig gelöscht.
Wenn die Interrupts generell schneller eintreffen, als das Main sie
verarbeiten kann, dann hast Du immer ein unlösbares Problem.
Wenn sie nur als Burst schneller sind, aber im Mittel nicht, dann mußt
Du die Daten im Interrupt zwischen speichern (FIFO).
kyrk.5 schrieb:> könnte aber auch so ablaufen:> ISR testVar = 1;> MAIN if (testVar) {> ISR testVar = 1; <=2te interrupt> MAIN testVar = 0; <=hups da werde die Daten aus den 1sten und 2ten> Interrupt gleichzeitig gelöscht.
Danke für die Hinweise.
Es gibt noch ein flag, das in main() gesetzt wird, und damit die ISR
steuert.
Damit sollte das "Dazwischenfunken" ausgeschlossen sein.
Guten Tag.
SD schrieb:> Danke für die Hinweise.> Es gibt noch ein flag, das in main() gesetzt wird, und damit die ISR> steuert.> Damit sollte das "Dazwischenfunken" ausgeschlossen sein.
Das dachten schon Viele. Sowas wird gern vermurkst. Der Teufels steckt
im Detail.
SD schrieb:> Es gibt noch ein flag, das in main() gesetzt wird, und damit die ISR> steuert.> Damit sollte das "Dazwischenfunken" ausgeschlossen sein.
Das nennt man dann wohl Mutex, oder Semaphore.
Auch ein solches muss als volatile deklariert werden, und atomar
verarbeitet/gelesen/geschrieben werden.
SD schrieb:> Es gibt noch ein flag, das in main() gesetzt wird, und damit die ISR> steuert.
Und was soll das nutzen?
Der Interrupt kann doch nur das Ereignis melden oder wegschmeißen.
Eine 3. Option hat er nicht.
Wozu braucht man eine ISR, wenn man sowieso entweder "Main" oder die
"ISR" was machen lassen will? Da kann ich auch einfach im Main auf das
ensprechende Interrupt Bit pollen und wenn alles da ist zur Verarbeitung
weiter gehen.
Was genau willst Du denn erreichen?
Wie oben schon korrekt bemerkt ist beim AVR8 eine Indizierung mit
uint8_t effizienter (Code-Größe, Ram-Speicher, Geschwindigkeit) als
Pointerarithmetik.