Forum: Compiler & IDEs Timerinterrupt und Pointer in gcc


von Klaus P. (marian)


Lesenswert?

Hallo Forum,

ich habe ein Problem mit der im Betreff stehende Sache. Ich möchte 
innerhalb der ISR des Timers1 des Atmega32 gern einen per SPI 
ausgelesenen Wert von einem Sensor mittels eines Pointers in ein Array 
schreiben. Zum Verständnis ein wenig Code:
Signal(SIG_OUTPUT_COMPARE1)
{
 getaccl();  //Werte aus dem Sensor auslesen
 *pdata= Werte; //Die ausgelesenen Werte mittels Pointer dem array 
zuweisen
}

Nun erstmal die Hauptfrage: Geht das einfach so? Ich habe es natürlich 
schon probiert und es geht bei mir nicht. Ich weiß aber nicht woran es 
liegt. Zeitkritisch ist es nicht, da der Interrupt nur alle 30ms 
aufgerufen wird. Wenn ich das Zuweisen der Werte weglasse und 
stattdessen noch aufwendige Funktionen in der ISR aufrufe, macht er das 
auch ohne Probleme. Also nochmal, weiß jemand, warum er bei dem 
Wertezuweisen Probleme hat?

Gruß,

Marian

von Karl H. (kbuchegg)


Lesenswert?

Klaus Peter wrote:
> schreiben. Zum Verständnis ein wenig Code:
> Signal(SIG_OUTPUT_COMPARE1)
> {
>  getaccl();  //Werte aus dem Sensor auslesen
>  *pdata= Werte; //Die ausgelesenen Werte mittels Pointer dem array
> zuweisen
> }
>
> Nun erstmal die Hauptfrage: Geht das einfach so?

Im Prinzip ja. Der Teufel steckt, wie immer, im Detail.
Zb. Darin, dass Arrays nicht als Ganzes zugewiesen werden
können etc.

> Ich habe es natürlich schon probiert und es geht bei mir nicht.

Dann hast du einen Fehler gemacht.

> Ich weiß aber nicht woran es liegt.

Wir auch nicht. Es ist nur so, dass du uns gegenüber einen
Vorteil hast: Du siehst deinen konkreten Code, wir nicht.
Wenn überhaupt, dann kannst also nur du deinen Fehler finden.
Oder aber du zeigst deinen richtigen Code her, dann können
auch wir den mal durchstöbern.

von Falk (Gast)


Lesenswert?

@Klaus Peter

>schreiben. Zum Verständnis ein wenig Code:
>Signal(SIG_OUTPUT_COMPARE1)
>{
> getaccl();  //Werte aus dem Sensor auslesen
> *pdata= Werte; //Die ausgelesenen Werte mittels Pointer dem array

Das ist so oder so Quark.

entweder

pdata[index] = Wert;  // EIN einzelner Wert

oder

*(pdata+index) = Wert;

>Nun erstmal die Hauptfrage: Geht das einfach so? Ich habe es natürlich

Ja, aber wie bereits von Karl Heinz gesagt, die Details sind der 
Knackpunkt.

1.) Dass Array muss global und volatile definiert sein

volatile uint8_t pdata[10];

2.) Du musst die einzelnen Elemente einzeln zuweisen, ggf. über ne 
Schleife.

>schon probiert und es geht bei mir nicht. Ich weiß aber nicht woran es
>liegt. Zeitkritisch ist es nicht, da der Interrupt nur alle 30ms
>aufgerufen wird. Wenn ich das Zuweisen der Werte weglasse und

Na dann sollte aber das Auslesen per SPI weniger als 30ms dauern.

MFG
Falk

von Klaus P. (marian)


Lesenswert?

Ok, dann hat sich meine Hauptfrage ja erledigt, es muss also gehen. Da 
ich nicht den vollständigen Code hier zeigen darf, werde ich versuchen 
alles nötige darzustellen.
1
#define SIZE 128  // Defines size of input array
2
3
volatile int32_t acclx=0,accly=0,acclz=0;  //Beschleunigungswerte
4
volatile double dataraw[SIZE]={0};
5
volatile double *pdata;
6
void getaccl(void)
7
{
8
  /*Beschleunigungswerte der x-Achse vom SMB360 holen*/
9
  smb360_read_register(0x9A,0x9B);                      // Register des Bese auslesen
10
  zweier_Komplement_umrechnen(data1,data2);        //Die Werte vom Bese in dezimal umrechen (-512 bis 511)
11
  acclx = accl;                      //Den aktuell ausgelesenen Beschleunigungswert der x-Achse zuweisen
12
  /*Beschleunigungswerte der y-Achse vom SMB360 holen*/
13
  smb360_read_register(0x9E,0x9F);            // Register des Bese auslesen
14
  zweier_Komplement_umrechnen(data1,data2);        //Die Werte vom Bese in dezimal umrechen (-512 bis 511)
15
  accly = accl;                      //Den aktuell ausgelesenen Beschleunigungswert der y-Achse zuweisen
16
  /*Beschleunigungswerte der z-Achse vom SMB360 holen*/
17
  smb360_read_register(0x9C,0x9D);            // Register des Bese auslesen
18
  zweier_Komplement_umrechnen(data1,data2);        //Die Werte vom Bese in dezimal umrechen (-512 bis 511)
19
  acclz = accl;                      //Den aktuell ausgelesenen Beschleunigungswert der z-Achse zuweisen  
20
}
21
SIGNAL(SIG_OUTPUT_COMPARE1)       
22
{
23
 if (zeitgeber==30)
24
 {
25
 getaccl();  //Auslesen der Beschleunigungswerte
26
 *pdata = (acclx*acclx) + (accly*accly) + (acclz*acclz); //Zuweisen der Beschleunigungswerte
27
 pdata++;  //Den Pointer auf das nächste Feld im Array zeigen lassen
28
 zeitgeber=0; 
29
 }else
30
 {
31
 zeitgeber++;
32
 }
33
}
So ich hoffe, dass dieser Ausschnitt schon reicht. Die verwendeten 
Funktionen funktionieren soweit alle, das alte Programm ansich läuft 
auch schon seit einiger Zeit. In diesem Programm habe ich die Werte 
immer in der main ausgelesen und dann zugewiesen, ohne Probleme. Ich 
will jetzt "nur" mein altes Programm umbauen, so wie ich es oben 
beschrieben habe. Leider klappt das nicht so ganz. Sobald ich die 
Wertezuweisung mittels *pdata einbaue, gibt mir mein Atmega32 seltsame 
Sonderzeichen per Uart aus. Wenn ich es rauskommentiere, funktioniert 
alles super. Kann mir, mit dem was ich bis jetzt beschrieb, schon jemand 
weiter helfen?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Falk wrote:

> 1.) Dass Array muss global und volatile definiert sein

global ja, volatile sehr wahrscheinlich nicht (aber das hängt vom
Rest ab).

von Stefan (Gast)


Lesenswert?

Wo/Wie wird der Speicher angelegt, auf den pdata zeigt? Liegt das Array 
in Data, Bss oder im Stack?

Besteht die Gefahr, dass die Endgrenze vom Array überschritten wird? 
Dein Code enthält kein Zurücksetzen von pdata.

Was passiert, wenn du die lange Berechnung durch eine kurze Zuweisung 
ersetzt z.B. *pdata = 42; ?

von Klaus P. (marian)


Lesenswert?

Achso, ich habe noch vergessen die main zu zeigen.
1
int main(void)
2
{
3
  SPI_MasterInit();          //SPI Initialisation
4
  UART_Init();            //Uartinitialisation 
5
  Timerinit1();            //Timerinitialisierung des Timers1
6
  stdout = &mystdout;         //Verlinken des Ausgabestreams für printf
7
        DDRC=0xFF;              //PortC auf Ausgang stellen, indem das gesamte DataDirectionRegister auf 1 gesetzt wird
8
  while(A<999)  PORTC &=~(1<<PC5);  //eine Sekunde lang die LED leuchten lassen
9
  zeitgeber=0;            //Variable für das Timing des Auslesen auf 0 setzen
10
  PORTC |=(1<<PC5);
11
  pdata=&dataraw[0];
12
  while(1)
13
  {if (pdata ==&dataraw[SIZE])
14
    {  PORTC &= ~(1<<PC5);
15
      anzahl=0;
16
                  pdata=&dataraw[0];
17
            PORTC |=(1<<PC5);
18
    }
19
        }
20
}

von Klaus P. (marian)


Lesenswert?

Also das Array liegt meines Wissens nach in data. Nein die Grenzen 
werden nicht überschritten, wie meine vergessene main zeigt :). Hab ich 
auch getestet. Das mit der einfachereren Zuweisung hatte ich nur so 
getestet: *pdata = acclx;
Ich probiere es mal mit der 42.

von Klaus P. (marian)


Lesenswert?

Hm sehr interessant. Ich habe gerade die Sache mit *pdata=42; getestet. 
Nun kann ich keine Verbindung mehr mit dem Controller herstellen, das 
Programm bleibt sofort hängen, die LED, die eigentlich nach einer 
Sekunde ausgeht, leuchtet ständig. Wenn ich *pdata=acclx; schreibe, kann 
ich mich wieder vebinden aber die Sonderzeichen sind anders als bei der 
oben genannten Zuweisung.

von Stefan K. (_sk_)


Lesenswert?

>Also das Array liegt meines Wissens nach in data. Nein die Grenzen
>werden nicht überschritten, wie meine vergessene main zeigt :). Hab ich
>auch getestet. Das mit der einfachereren Zuweisung hatte ich nur so
>getestet: *pdata = acclx;

Die Arraygrenzen werden sehr wohl überschritten, sobald aus Deinem 
Testprogramm ein "richtiges" main wird:
Wenn main mal länger beschäftigt ist als Deine 30ms UND während dieser 
Zeit der Pointer > &dataraw[SIZE] wird, überschreibst Du den Speicher 
hinter dem Array.

Und noch schlimmer:
weil Du in main auf pdata == &dataraw[SIZE] testest, wird Dein main() 
diesen Überlauf nichtmal bemerken, Dein koompletter Speicher wird ab 
diesem Zeitpunkt sukzessive zugemüllt.

Deshalb:
Den Pointer immer an der Stelle testen, wo er auch verändert wird, also 
in Deinem Fall in der ISR. Und: Nie auf ==, sondern auf >= testen.

Stefan

von Klaus P. (marian)


Lesenswert?

Ok danke für den Hinweis, daran hatte ich noch nicht gedacht. Ich habe 
es nun geändert, aber an meinem Problem hat sich nichts getan, er 
verhält sich immer noch genau so.

von Falk (Gast)


Lesenswert?

int index=0;          // global

SIGNAL(SIG_OUTPUT_COMPARE1)
{
 if (zeitgeber==30)
 {
    getaccl();  //Auslesen der Beschleunigungswerte
    dataraw[index]=(acclx*acclx) + (accly*accly) + (acclz*acclz); 
//Zuweisen der Beschleunigungswerte
    index++;
    if (index >= SIZE) index=0;
    zeitgeber=0;
 }
 else
 {
    zeitgeber++;
 }
}

von Falk (Gast)


Lesenswert?

Ähhh, du musst erst ALLE Variablen (und vor allem deinen Pointer) 
initialisieren, bevor du die Interrupts freigibst. Ich nehme an, dass

Timerinit1();            //Timerinitialisierung des Timers1

den Timer initialisiert und freigibt. Allerdings wird dein Pointer erst
nach einer Sekunde LED Blinken initialisiert!

 pdata=&dataraw[0];

Es muss definiv umgekehrt sein.

MFG
Falk

von Klaus P. (marian)


Lesenswert?

SUPER!

Genau das war der Knackpunkt, danke Falk. Ich habe jetzt den Pointer 
gleich als erstes initialisiert und nun macht er alles, wie ich es will. 
Endlich...nochmals vielen dank!

Gruß,

Marian

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.