Forum: Mikrocontroller und Digitale Elektronik [AVR, C] Arrayzugriff aus Interrupt


von SD (Gast)


Lesenswert?

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?
1
#define SIZE 16
2
unsigned int array[SIZE];
3
unsigned int index = 0;
4
unsigned int* pointer = array;
5
6
...
7
8
ISR(){
9
  if(index < SIZE)
10
    array[index++] = value;
11
12
  // oder
13
14
  if( ? )
15
    *(pointer++) = value;
16
}

von SD (Gast)


Lesenswert?

Kommt wohl auf's selbe hinaus:
1
if (pointer-array < SIZE)

von Bastler_HV (Gast)


Lesenswert?

Nach meinen rüdimentären C-Kenntnissen sollte das so gehen:

1
   if( ++pointer < array[SIZE] )
2
     {
3
        *(pointer) = value;
4
     }

array[SIZE] ist der virtuelle Speicherplatz nach dem letzten 
Arrayelement.
Wenn der Pointer kleiner ist, kannst Du in das Array reinschreiben.

von Falk B. (falk)


Lesenswert?

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()

von Falk B. (falk)


Lesenswert?

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)

von Mitlesa (Gast)


Lesenswert?

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.

von Dirk B. (dirkb2)


Lesenswert?

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.

von SD (Gast)


Lesenswert?

Danke!

von Falk B. (falk)


Lesenswert?

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!

Beitrag #6331446 wurde von einem Moderator gelöscht.
von Oliver S. (oliverso)


Lesenswert?

Falk B. schrieb:
> if (pointer >= &array+sizeof(array)) mach_was()

C-Pointer sind anscheinend auch 50 Jahre nach ihrer Erfindung immer noch 
ein Mysterium.
1
if (pointer >= pointer+size) machwas();

Oliver

von Stefan F. (Gast)


Lesenswert?

SD schrieb:
> Was ist der Unterschied zwischen Zugriff per Index und Pointer?

Für den Prozessor ist beides identisch.

von Falk B. (falk)


Lesenswert?

Stefan ⛄ F. schrieb:
> Für den Prozessor ist beides identisch.

Ganz neue Erkenntnis!

Beitrag "Re: [AVR, C] Arrayzugriff aus Interrupt"

von Peter D. (peda)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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).

von kyrk.5 (Gast)


Lesenswert?

"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();
}

von Peter D. (peda)


Lesenswert?

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).

von SD (Gast)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

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.

von Einer K. (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von SD (Gast)


Lesenswert?

Besser gesagt:
isr und main schalten sich selbst ab und laufen nacheinander.
Ich sehe da keine Möglichkeit für eine Kollision:
1
ISR(){
2
  if(isr){
3
    if (index < SIZE){
4
      array[index++] = datum;
5
    }
6
    else{
7
      isr = false;
8
      index = 0;
9
      main = true;
10
    }
11
  }
12
}
13
14
main(){
15
  if (condition) {
16
    isr = true;
17
  }
18
19
  if(main){
20
    main = false;
21
    for (unsigned int i=0; i<SIZE; i++){
22
       // read = array[i];
23
    }
24
  }
25
}

von SD (Gast)


Lesenswert?

Der Abbruch in isr könnte natürlich direkt nach dem letzten Schreiben 
erfolgen...

von Einer K. (Gast)


Lesenswert?

SD schrieb:
> Ich sehe da keine Möglichkeit für eine Kollision:

Und ich sehe da ein unvollständiges/untestbares Programm.

von Andreas M. (amesser)


Lesenswert?

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.

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.