www.mikrocontroller.net

Forum: Compiler & IDEs Problem mit globaler Variable und dem CTC-modus


Autor: Toni (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Guten Abend!!!

Ich bin gerade auf ein kleines Problem mit einer Globalen Variable für 
eine ISR gestoßen.
Und zwar speichert GCC diese auf dem flash. nun dauert ein einziger 
zugriff auf die variable geschlagene 10 zyklen. die isr sollte aber 
nicht länger als 20 zyklen dauern. wie kann ich dem compiler sagen, das 
er diese in einem register speichern soll? - das attribut "register" 
scheint ja nichtmehr zu funktionieren.

und mit dem timer 2 hab ich ein problem mit dem ctc-modus. und zwar 
springt das ding direkt nach dem einschreiben in "timsk" in die isr. 
nach einigen ähnlichen beiträgen mit vielen tipps zu reihenfolge etc. 
pp. komme ich einfach auf keinen grünen zweig. oder liegt hier ein bug 
des avr-studios vor? - das gleiche problem hatte ich allerdings auch 
schon unter assembler und habe mich damit unschön arrangiert.

dann danke ich euch schonmal und wünsche eine gute nacht.

hier noch ein kleiner codeschnipsel:
// für die ISR
volatile unsigned char  zaehler_timer2 = 0;

// MAIN
void main (void)

  {
    /*  lcd_init();
      // Startbildschirm
      lcd_string("Temperbox-Steuerung");
      lcd_cursor(0,2);
      lcd_string("by Toni Krulikowski");
      lcd_cursor(0,3);
      lcd_string("Software v1.0");
      lcd_cursor(0,4);
      lcd_string("21.09.2010");
      _delay_ms(4000);            // 4 Sekunden warten
    */

      uint8_t zeit_aus_timer;
      //tempsensor ansteuerung
      //Sensor starten und auf fallende flanke warten
      DDRB = ((1 << PB1) | (1 << PB0));    // PB0, PB1 = eingang
      PORTB |= (1 << PB1);          // Sensor einschalten
      sei();                  // interrupt frei
      while((PINB & (1 << PB2)));        // warte auf fallende Flanke
      TCCR2 |= (1 << CS00);          // dann Timer2 start
      while(!(PINB & (1 << PB2)));      // auf steigende Flanke warten
      TCCR2 &= ~(1 << CS00);          // Timer2 stopp    
      zeit_aus_timer = (TCNT2 - 10);      // gemessenen wert in Var schreiben
      zeit_aus_timer >>= 1;          // x:2
      OCR2 = zeit_aus_timer;          // und ins vergleichsregister schieben
      TCNT2 = 0;                // zählregister nullen      
      TCCR2 = ((1 << CS00)|(1 << WGM21));    // dann Timer2 start und CTC-mode
      TIMSK = (1 << OCIE2);          // Timer 2 interrupt
      
      
      while(1){};

  }

ISR (TIMER2_COMP_vect)
{
  zaehler_timer2++;
}

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Toni schrieb:

> Und zwar speichert GCC diese auf dem flash.

Die Variable wird vom Startupcode im RAM angelegt. Stichwort .DATA 
Variablenbereich. Im Flash ist lediglich der erste Wert (0) mit dem die 
Variable initialisiert wird.

Was soll dein Programm machen? Soweit ich sehe, benutzt du den Inhalt 
von zaehler_timer2 überhaupt nicht.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Toni schrieb:

> Und zwar speichert GCC diese auf dem flash.

Das glaube ich eher nicht. Die liegt im RAM.

> nun dauert ein einziger zugriff auf die variable geschlagene 10 zyklen.

Hast du evtl. vergessen, die Optimierung einzuschalten?
Bei mir braucht der Zugriff nur 5 Zyklen.

> und mit dem timer 2 hab ich ein problem mit dem ctc-modus. und zwar
> springt das ding direkt nach dem einschreiben in "timsk" in die isr.

In deinem Code sehe ich keinen Zugriff auf TIMSK oder irgendwelchen 
anderen Timer-Setup-Code. Das macht es etwas schwierig, zu ermitteln, 
was daran falsch sein könnte. Mein Wahrsager ist gerade im Urlaub ;-)

Übrigens: C ist case-sensitiv, und die meisten Leser hier auch. 
Angemessene Verwendung der Shift-Taste wird mit mehr und mit 
freundlicheren Antworten belohnt.


Stefan B. schrieb:
> Die Variable wird vom Startupcode im RAM angelegt. Stichwort .DATA
> Variablenbereich. Im Flash ist lediglich der erste Wert (0) mit dem die
> Variable initialisiert wird.

0-initialisierte Variablen landen eigentlich nicht dort, sondern in 
.BSS, das beim Starten einfach komplett genullt wird.

Autor: Bernhard R. (barnyhh)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die ISR soll 20 Zyklen dauern:

- Einsprung           4
- Rücksprung          4
- save / restore r1   4
- save / restore SR   6
- save / restore r0   4
-----------------------
Gesamt               22 Zyklen für "Housekeeping".

So geht das nicht.

Bernhard

BTW: Die Anzahl Zyklen pro Instruktion kommen aus meinem Gedächtnis, 
nicht aus dem Datenblatt. Sie können also etwas danebenliegen.

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

Bewertung
0 lesenswert
nicht lesenswert
Toni schrieb:

> und mit dem timer 2 hab ich ein problem mit dem ctc-modus. und zwar
> springt das ding direkt nach dem einschreiben in "timsk" in die isr.

Yep.

Was du ignorierst.
Die Vergleiche des OCR Registers mit dem Timer Register finden ständig 
statt und nicht nur, wenn du den Interrupt dazu freigibst.

Dein Timer ist davor ja schon gelaufen und das OCR Register wird dort 
noch den Wert 0 haben. Den hat aber TCNT2 auch schnell schon mal selber. 
Konsequenterweise wird daher das Flag gesetzt, welches den Match 
anzeigt, denn es liegt ja tatsächlich einer vor.

Mit dem Interrupt Flag im TIMSK erlaubst du nur, dass ein ISR Aufruf 
ausgelöst wird wenn ein Match auftritt. Aber auch dann wenn du das nicht 
tust (wie zb bei deiner ersten Timer Verwendung) wird der Match 
selbstverständlich registriert.

Tja. Bis du dann den zugehörigen Interrupt freigibst, der natürlich auf 
den bereits vorher aufgetretenen Match reagiert.

Autor: Toni (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Guten Morgen!

Ich werde mich bemühen, case-sensitive zu schreiben :)

Zurück zum Thema:

Das Problem mit dem Timer liegt in der vorletzten Zeile von main().
TIMSK = (1 << OCIE2);          // Timer 2 interrupt

Ab hier springt der debugger direkt in die ISR, weobei schon alle 
Register wie OCR usw. geladen sind.
Ich habe schon probiert, das TISMK-Register reltiv mittig zu laden, aber 
dann springt er in die ISR, sobald der Timer gestartet wird, blödes Ding 
:).

Die Variable zaehler_timer2 wird testweise in der ISR um eins erhöht:
ISR (TIMER2_COMP_vect)
{
  zaehler_timer2++;
}

Hier ist mir auch aufgefallen, das für diesen Schritt über 10 Zyklen 
benötigt werden. Aber wenn er die Var. ins EEPROM schreibt, ist das 
natürlich klar.
Wie kann ich nun verhindern, das er diese nichtmehr ins EEPROM schreibt 
und dafür ein festes Reigster bereit hält?

Danke nochmal und nen schönen Tag noch.

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich würde die Interruptroutine in Assembler schreiben, wenn sie so 
zeitkritisch ist.  Erfahrungsgemäß lässt sich gerade bei so kurzen 
Routinen noch eine beträchtliche Anzahl Zyklen sparen (so sichert und 
bereitet der Compiler r1 auch dann vor, wenn es gar nicht gebraucht wird 
und wenn ich's richtig in Erinnerung habe).

Die Assembler-Routine könnte so aussehen:
#include <avr/io.h>
 .global __vector_4     ; ggfs. anpassen
__vector_4:
 push r24
 in r24,_SFR_IO_ADDR(SREG)
 push r24
 lds r24, zaehler
 subi r24, -1
 sts zaehler, r24
 pop r24
 out _SFR_IO_ADDR(SREG),r24
 pop r24
 reti
Das dürfte in etwa das Minimum für diese Aufgabe darstellen.  (Das 
Zyklen-Zählen überlasse ich aber Dir).

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Toni schrieb:

> Hier ist mir auch aufgefallen, das für diesen Schritt über 10 Zyklen
> benötigt werden. Aber wenn er die Var. ins EEPROM schreibt, ist das
> natürlich klar.
> Wie kann ich nun verhindern, das er diese nichtmehr ins EEPROM schreibt
> und dafür ein festes Reigster bereit hält?

Troll, es wird nicht ins EEPROM geschrieben.

Autor: Toni (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ouh man, da hatte ich wohl einen kleinen hänger, mit dem Flash meinte 
ich natürlich den RAM :)
aber meinen und schreiben sind natürlich 2 paar Schuhe...

Ok, ich werds mal mit Inline-Assembler probieren. Die Timer-Geschichte 
werde ich auch noch irgendwie hinbiegen können :).

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das da oben ist aber kein Inline-Assembler.  Nur zur Sicherheit.

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

Bewertung
0 lesenswert
nicht lesenswert
Toni schrieb:

>
TIMSK = (1 << OCIE2);          // Timer 2 interrupt
>
> Ab hier springt der debugger direkt in die ISR, weobei schon alle
> Register wie OCR usw. geladen sind.

Klar, du musst ja vorher auch die Flags löschen, wie dir Karl Heinz
schon in Beitrag "Re: Problem mit globaler Variable und dem CTC-modus" schrieb.
TIFR = (1 << OCF2);  /* hier darf _nie_ |= stehen */
TIMSK = (1 << OCIE2); /* hier darf es auch |= sein */

Autor: Toni (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also entweder bin ich zu blöd, oder ich sehe einfach schlecht; aber den 
scheiß Timer bekomme ich nicht anständig zum laufen...
Obwohl das OCF2 - Flag im TIFR Register von mir gelöscht wurde, spingt 
er nach dem setzen des OCIE2 im TIMSK ständig in die ISR, ich könnte 
brechen. Egal welche Reihenfolge ich anwende, entweder der TIMSK oder 
das starten des Timers löst einen Interrupt aus.
Eigentlich dürfte er doch nur springen, sobald das Flag OCF2 im TIFR 
Register gesetzt ist, oder?
An der Hardware habe ich es leider noch nicht getestet.
Einen Code hab ich nun auch nichtmehr, da ich den Timer nun nichtmehr 
verwende :) - somit habe ich 2 Fliegen mit einer Klappe geschlagen.

Autor: ... (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Toni schrieb:
> Eigentlich dürfte er doch nur springen, sobald das Flag OCF2 im TIFR
> Register gesetzt ist, oder?

Genau deswegen sollst Du das blöde Flag ja auch zurücksetzen, bevor Du 
TIMSK anfasst!.
Jörg hat Dir sogar schon die passende Codezeile dafür gepostet.

Autor: Toni (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Toni schrieb:
> Obwohl das OCF2 - Flag im TIFR Register von mir gelöscht wurde, spingt
> er NACH dem setzen des OCIE2 im TIMSK ständig in die ISR.

Ich habe es doch gelöscht, bevor ich das TIMSK angefasst habe, Mensch. 
Laut debugger war/ist es ebenfalls gelöscht.
Obwohl dieses scheiß OCF2 = 0 war, wurde in die ISR gesprungen. Das kann 
doch nicht sein?!

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

Bewertung
0 lesenswert
nicht lesenswert
Toni schrieb:
> An der Hardware habe ich es leider noch nicht getestet.

Sag doch gleich, dass du nur so tust als ob.

Einen solchen "Fehler" in der Simulation nimmt doch niemand ernst.

> Einen Code hab ich nun auch nichtmehr, da ich den Timer nun nichtmehr
> verwende :)

Klar, wenn man das Loch für den Zündschlüssel nicht findet, wird das
Auto halt geschoben, auch eine Lösung.

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.