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


von Toni (Gast)


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:
1
// für die ISR
2
volatile unsigned char  zaehler_timer2 = 0;
3
4
// MAIN
5
void main (void)
6
7
  {
8
    /*  lcd_init();
9
      // Startbildschirm
10
      lcd_string("Temperbox-Steuerung");
11
      lcd_cursor(0,2);
12
      lcd_string("by Toni Krulikowski");
13
      lcd_cursor(0,3);
14
      lcd_string("Software v1.0");
15
      lcd_cursor(0,4);
16
      lcd_string("21.09.2010");
17
      _delay_ms(4000);            // 4 Sekunden warten
18
    */
19
20
      uint8_t zeit_aus_timer;
21
      //tempsensor ansteuerung
22
      //Sensor starten und auf fallende flanke warten
23
      DDRB = ((1 << PB1) | (1 << PB0));    // PB0, PB1 = eingang
24
      PORTB |= (1 << PB1);          // Sensor einschalten
25
      sei();                  // interrupt frei
26
      while((PINB & (1 << PB2)));        // warte auf fallende Flanke
27
      TCCR2 |= (1 << CS00);          // dann Timer2 start
28
      while(!(PINB & (1 << PB2)));      // auf steigende Flanke warten
29
      TCCR2 &= ~(1 << CS00);          // Timer2 stopp    
30
      zeit_aus_timer = (TCNT2 - 10);      // gemessenen wert in Var schreiben
31
      zeit_aus_timer >>= 1;          // x:2
32
      OCR2 = zeit_aus_timer;          // und ins vergleichsregister schieben
33
      TCNT2 = 0;                // zählregister nullen      
34
      TCCR2 = ((1 << CS00)|(1 << WGM21));    // dann Timer2 start und CTC-mode
35
      TIMSK = (1 << OCIE2);          // Timer 2 interrupt
36
      
37
      
38
      while(1){};
39
40
  }
41
42
ISR (TIMER2_COMP_vect)
43
{
44
  zaehler_timer2++;
45
}

von Stefan B. (stefan) Benutzerseite


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.

von Rolf Magnus (Gast)


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.

von Bernhard R. (barnyhh)


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.

von Karl H. (kbuchegg)


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.

von Toni (Gast)


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().
1
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:
1
ISR (TIMER2_COMP_vect)
2
{
3
  zaehler_timer2++;
4
}

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.

von Hc Z. (mizch)


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:
1
#include <avr/io.h>
2
 .global __vector_4     ; ggfs. anpassen
3
__vector_4:
4
 push r24
5
 in r24,_SFR_IO_ADDR(SREG)
6
 push r24
7
 lds r24, zaehler
8
 subi r24, -1
9
 sts zaehler, r24
10
 pop r24
11
 out _SFR_IO_ADDR(SREG),r24
12
 pop r24
13
 reti
Das dürfte in etwa das Minimum für diese Aufgabe darstellen.  (Das 
Zyklen-Zählen überlasse ich aber Dir).

von Stefan B. (stefan) Benutzerseite


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.

von Toni (Gast)


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

von Hc Z. (mizch)


Lesenswert?

Das da oben ist aber kein Inline-Assembler.  Nur zur Sicherheit.

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


Lesenswert?

Toni schrieb:

>
1
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.
1
TIFR = (1 << OCF2);  /* hier darf _nie_ |= stehen */
2
TIMSK = (1 << OCIE2); /* hier darf es auch |= sein */

von Toni (Gast)


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.

von ... (Gast)


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.

von Toni (Gast)


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?!

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


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.

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.