www.mikrocontroller.net

Forum: Compiler & IDEs Timerinterrupt und Pointer in gcc


Autor: Klaus Peter (marian)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Falk (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Klaus Peter (marian)
Datum:

Bewertung
0 lesenswert
nicht 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.
#define SIZE 128  // Defines size of input array

volatile int32_t acclx=0,accly=0,acclz=0;  //Beschleunigungswerte
volatile double dataraw[SIZE]={0};
volatile double *pdata;
void getaccl(void)
{
  /*Beschleunigungswerte der x-Achse vom SMB360 holen*/
  smb360_read_register(0x9A,0x9B);                      // Register des Bese auslesen
  zweier_Komplement_umrechnen(data1,data2);        //Die Werte vom Bese in dezimal umrechen (-512 bis 511)
  acclx = accl;                      //Den aktuell ausgelesenen Beschleunigungswert der x-Achse zuweisen
  /*Beschleunigungswerte der y-Achse vom SMB360 holen*/
  smb360_read_register(0x9E,0x9F);            // Register des Bese auslesen
  zweier_Komplement_umrechnen(data1,data2);        //Die Werte vom Bese in dezimal umrechen (-512 bis 511)
  accly = accl;                      //Den aktuell ausgelesenen Beschleunigungswert der y-Achse zuweisen
  /*Beschleunigungswerte der z-Achse vom SMB360 holen*/
  smb360_read_register(0x9C,0x9D);            // Register des Bese auslesen
  zweier_Komplement_umrechnen(data1,data2);        //Die Werte vom Bese in dezimal umrechen (-512 bis 511)
  acclz = accl;                      //Den aktuell ausgelesenen Beschleunigungswert der z-Achse zuweisen  
}
SIGNAL(SIG_OUTPUT_COMPARE1)       
{
 if (zeitgeber==30)
 {
 getaccl();  //Auslesen der Beschleunigungswerte
 *pdata = (acclx*acclx) + (accly*accly) + (acclz*acclz); //Zuweisen der Beschleunigungswerte
 pdata++;  //Den Pointer auf das nächste Feld im Array zeigen lassen
 zeitgeber=0; 
 }else
 {
 zeitgeber++;
 }
}
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?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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; ?

Autor: Klaus Peter (marian)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Achso, ich habe noch vergessen die main zu zeigen.
int main(void)
{
  SPI_MasterInit();          //SPI Initialisation
  UART_Init();            //Uartinitialisation 
  Timerinit1();            //Timerinitialisierung des Timers1
  stdout = &mystdout;         //Verlinken des Ausgabestreams für printf
        DDRC=0xFF;              //PortC auf Ausgang stellen, indem das gesamte DataDirectionRegister auf 1 gesetzt wird
  while(A<999)  PORTC &=~(1<<PC5);  //eine Sekunde lang die LED leuchten lassen
  zeitgeber=0;            //Variable für das Timing des Auslesen auf 0 setzen
  PORTC |=(1<<PC5);
  pdata=&dataraw[0];
  while(1)
  {if (pdata ==&dataraw[SIZE])
    {  PORTC &= ~(1<<PC5);
      anzahl=0;
                  pdata=&dataraw[0];
            PORTC |=(1<<PC5);
    }
        }
}

Autor: Klaus Peter (marian)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Klaus Peter (marian)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan Kleinwort (_sk_)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Klaus Peter (marian)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Falk (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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++;
 }
}

Autor: Falk (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Klaus Peter (marian)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.