Assembler timer

1avr2

Neues Mitglied
21. Jan. 2010
28
0
0
Sprachen
guten abend,

wieder ein timer problem das ich nicht verstehe.
das programm funktioniert einmal nachdem es mit dem mkII downgeloadet wurde, beim zweitem druecken der taste
bleibt die led einfach brennen. hat jemand eine idee?
zum testen im simulator habe ich cs02=0 und cs00=1 gesetzt und auch hier haut's hin. bei cnt=70 bekomme ich ca
15 sec -> 0,83 X10-6 s x 1024 x 25 x 70 auch ca 15 sec.
aber mit vorteiler 1020 funktioniert das programm nur einmal korrekt.
schickt man das programm noch mal zum baustein funktioniert wieder einmal ???

die ratschlaege von old-max kann ich leider momentan noch nicht umsetzen, werde sie aber im hinterkopf behalten.


Code:
; zeitrelais mit timer0 interrupt, taster an pb0 soll ausgang
; pb4 fuer 30 sec setzen. 

.include "tn13def.inc"
.def rmp=r16 ;multipurpose
.def cnt=r18

; reset und isr vektoren

	rjmp init			; resetvektor
	reti
	reti
	rjmp timer_isr		; timeroverflow
	reti
	reti
	reti
	reti
	reti
	reti


;

init:
	ldi rmp,low(ramend)	; init stapel
	out spl,rmp
	ldi cnt,3			; zaehlersetzen
	cbi ddrb,0			; portb,0 eingang
	sbi portb,0			; pullup
	sbi ddrb,4			; portb,4 ausgang
	ldi rmp,1<<toie0	; enable overflow isr
	out timsk0,rmp

	sei					; enable isr

loop:
	sbic pinb,0			; ueberspringe naechste zeile wenn pinb,0 =1
	rjmp loop


	sbi portb,4			; ausgang pb4 setzen
	ldi rmp,(0<<cs02)|(1<<cs00)	; timer starten, fuer simu cs02=0
	out tccr0b,rmp

;	cpi cnt,0		; abfrage ob zaehlregister=0 
;	breq end

loop1:
	rjmp loop1

timer_isr:
	nop
	dec cnt
	cpi cnt,0
	breq end
	reti
end:
	nop
	cbi portb,4		; portb,4 aus
	rjmp loop
 
ich hab noch rausgefunden, das ab dem zweiten druchlauf kein timer0overflow mehr ausgeloest wird ??
beim simulieren habe ich jedoch nur cs00=1 gesetzt. verstehe leider nicht warum?
 
Hallo 1avr,

die Sache mit der Timerinitialisierung habe ich mir nicht genau angesehen oder überprüft.
Du hast vier grundsätzliche Fehler in deinem Programm, am besten versuchst du erst einmal diese
zu beseitigen.

Fehler 1:
Wird der Taster an PB0 betätigt, gehst du aus der ersten loop-Schleife raus und startest den Timer durch
setzen einiger Prescalerbits. Du solltest zuvor immer den Timer/Counter initialiseren, der sollte hier dann
ab 0 loslaufen.
Nach dem der Timer läuft, kommst du in die zweite loop-Schleife. Hier kommst du eigentlich nie mehr
raus. "Eigentlich", da du tatsächlich wieder rauskommst, das hängt aber mit Fehler 2 zusammen.

Fehler 2:
Du beendest die Timeroverflow-ISR mit "rjmp loop", wenn deine Hilfszählvariable 0 ist. Du musst die ISR immer
mit reti verlassen, nie mit jrmp o.ä.! Wenn der AVR in die ISR springt, merkt er sich die Adresse von wo aus er gesprungen ist
und die legt er sich auf den Stack. Der Stackpointer zeigt bei Programmstart auf das Ramende von SRAM. Bei einem
rjmp wird die Adresse nicht mehr vom Stack geholt. Der Stack kann irgendwann vollaufen.
Wenn du im Hauptprogramm bedingte Sprünge (breq ...) ausführst, solltest du in einer ISR am Anfang das Statusregister SREG auf dem Stack
ablegen (push) und am Ende vor reti wieder in SREG zurückkopieren (pop). Die ISR verändert bei dir SREG, das kann bei dir im Hauptprogramm
dann zu Problemen führen.

Fehler 3:
Wenn die TimerISR den PB4 auf low schaltet, läuft der Timer weiter, du schaltest diesen nicht ab, deine Variable cnt läuft das nächste mal über (0 -> 255).

Fehler 4:
Beim Starten des Timers ist cnt nur nach dem Reset das erste mal initialisiert. Danach nicht mehr, läuft über, siehe Fehler 3.

Falls dein Programm nichts anderes machen soll, könntest du einfach auch eine Pauseschleife verwenden. Im Moment wird bei
deinem Programm in der zweiten Schleife kein wichtiger Code ausgeführt. Das wäre einfacher zu programmieren, eleganter ist es
natürlich mit TimerISR.

Schau dir also erst mal deine Programmstruktur an.

Grüße,
Dirk
 
hallo dirk,
habe jetzt eine aufgabe aus dem tutorial modifiziert und erfolgreich getestet.
danke fuer deine analyse.

gruss bernhard.


Code:
; Loesung Aufgabe 9: Drei Mal blinken pro Tastendruck, interrupt-gesteuert
;
.nolist
.include "tn13def.inc"
.list
;
; Timer: Takt 1,2 MHz, Vorteiler 1024, Overflow bei 256
; Dauer: 1024*256/1,2 = 218.453 us = 218 ms
;
; Registerdefinitionen
.def rmp = R16 ; Multipurpose-Register
.def rCnt = R17 ; Blinkzaehler-Register
;
; Reset- und Int-Vektoren
	rjmp start ; Reset-Vektor
	reti ; INT0-Vektor
	rjmp pcint ; PCINT0-Vektor
	rjmp tc0ovf ; TC0-Overflow-Vektor
	reti ; EE_RDY-Vektor
	reti ; ANA_COMP-Vektor
	reti ; TIM0_COMPA-Vektor
	reti ; TIM0_COMPB-Vektor
	reti ; WDT-Vektor
	reti ; ADC-Vektor
pcint:
	sbis PINB,PB4 ; Ueberspringe wenn Taste nicht gedrueckt
	ldi rCnt,140; Zaehler setzen
	reti ; fertig mit Interrupt
; TC0 Overflow interrupt service routine 
tc0ovf:
	;sbic PORTB,PB0 ; Ueberspringe, wenn LED-Ausgang = 0
	cbi portb,0		; ausgang setzen
	rjmp tc0ovf1 ; LED-Ausgang = 1
	;sbi PORTB,PB0 ; schalte LED-Ausgang auf 1
	reti ; fertig, warte auf naechsten Interrupt
tc0ovf1:
	tst rCnt ; Blinkzaehler = 0 ?
	brne tc0ovf2 ; nicht Null, noch einmal blinken
	sbi portb,0		; led aus
	reti ; Null, nicht mehr blinken
tc0ovf2:
	dec rCnt ; Zaehler um Eins niedriger setzen
	;cbi PORTB,PB0 ; LED anschalten
	reti ; fertig
;
; Hauptprogramm-Start
Start:
	ldi rmp,LOW(RAMEND) ; init Stapel
	out SPL,rmp
	sbi DDRB,PB0 ; LED-Ausgang einschalten
	cbi PORTB,PB0 ; LED ausschalten ***
	sbi PORTB,PB4 ; Pullup-Widerstand am Tasteneingang einschalten
	ldi rmp,1<<PCINT4 ; externe Int-Maske an Tasteneingang einschalten
	out PCMSK,rmp
	ldi rmp,1<<PCIE ; externer Int einschalten
	out GIMSK,rmp
	ldi rCnt,140 ; am Anfang drei mal blinken
	ldi rmp,0 ; Timer-Mode auf Normal
	out TCCR0A,rmp
	ldi rmp,(1<<CS02)|(1<<CS00) ; Timer Prescaler auf 1024
	out TCCR0B,rmp
	ldi rmp,1<<TOIE0 ; Timer-Interrupts ermoeglichen
	out TIMSK0,rmp
	sei ; Interrupts generell einschalten
Schleife:
	rjmp Schleife ; alles weitere interrupt-gesteuert
 
Hi Bernmhard
Auch wenn du deinen Fehler beseitigt hast.. da ist noch was ganz dringendes.
Wer soll deine Listings lesen ? Bitte gewöhn dir diesen Stil nicht an. Sicherlich ist die folgende Schreibweise nicht die Entdeckung des Weltalls, aber doch wesentlich besser lesbar:

Code:
;******************************************************
;*                            Loesung Aufgabe 9:                                     *
;*    Drei Mal blinken pro Tastendruck, interrupt-gesteuert             *
;******************************************************
 .nolist
 .include "tn13def.inc"
 .list
 
;******************************************************
;*    Timer: Takt 1,2 MHz, Vorteiler 1024, Overflow bei 256            *
;*   Dauer: 1024*256/1,2 = 218.453 us = 218 ms                        *
;******************************************************

 ;***************** Registerdefinitionen********************
 .def rmp = R16                        ; Multipurpose-Register
 .def rCnt = R17                        ; Blinkzaehler-Register
 
;**************** Reset- und Int-Vektoren ******************
 rjmp start                               ; Überspringe Interrut-Vektor Tabelle

 reti                                         ; INT0-Vektor
 rjmp pcint                               ; PCINT0-Vektor
 rjmp tc0ovf                             ; TC0-Overflow-Vektor
 reti                                         ; EE_RDY-Vektor
 reti                                         ; ANA_COMP-Vektor
 reti                                         ; TIM0_COMPA-Vektor
 reti                                         ; TIM0_COMPB-Vektor
 reti                                         ; WDT-Vektor
 reti                                         ; ADC-Vektor

 pcint:                                     ; Taster- ISR
     sbis PINB,PB4                     ; Ueberspringe wenn Taste nicht 
;*** gedrueckt ***
     ldi rCnt,140                        ; Zaehler setzen
 reti                                        ; fertig mit Interrupt

;********** TC0 Overflow interrupt service routine *************
tc0ovf:
   ;sbic PORTB,PB0                   ; Ueberspringe, wenn LED-Ausgang = 0
   cbi portb,0                           ; ausgang setzen
   rjmp tc0ovf1                        ; LED-Ausgang = 1
   ;sbi PORTB,PB0                    ; schalte LED-Ausgang auf 1
reti                                        ; fertig, warte auf naechsten Interrupt

tc0ovf1:
   tst rCnt                               ; Blinkzaehler = 0 ?
   brne tc0ovf2                        ; nicht Null, noch einmal blinken
   sbi portb,0                          ; led aus
reti                                        ; Null, nicht mehr blinken

tc0ovf2:
    dec rCnt                             ; Zaehler um Eins niedriger setzen
    ;cbi PORTB,PB0                   ; LED anschalten
reti                                         ; fertig
;***************** Hauptprogramm-Start *******************
Start:
     ldi rmp,LOW(RAMEND)       ; init Stapel
     out SPL,rmp
??????????????????? wo ist die High-Adresse von RamEnd ???????????????
     sbi DDRB,PB0                       ; LED-Ausgang einschalten
     cbi PORTB,PB0                      ; LED ausschalten 
     sbi PORTB,PB4                      ; Pullup-Widerstand am Tasteneingang einschalten
     ldi rmp,1<<PCINT4               ; externe Int-Maske an 

;Tasteneingang einschalten
     out PCMSK,rmp
     ldi rmp,1<<PCIE                  ; externer Int einschalten
     out GIMSK,rmp
     ldi rCnt,140                         ; am Anfang drei mal blinken
     ldi rmp,0                             ; Timer-Mode auf Normal
     out TCCR0A,rmp
     ldi rmp,(1<<CS02)|(1<<CS00) ; Timer Prescaler auf 1024
     out TCCR0B,rmp
     ldi rmp,1<<TOIE0                ; Timer-Interrupts ermoeglichen
     out TIMSK0,rmp
     sei                                      ; Interrupts generell einschalten

Schleife:

rjmp Schleife ; alles weitere interrupt-gesteuert 
.

Ich muss hier mit Leerzeichen arbeiten, aber im Assemblerlisting nimmt man Tab's. Damit wird es dann wesentlich übersichtlicher. Auch eine Rahmengestaltung mit Kommentarzeilen vor Programmblöcken macht sich im Listing ganz gut. Leere Zeilen kannst du ohne Semikolon einfügen. Das Semikolon verwirrt da nur.
Und nochwas: geh früher schlafen.....:wink:
Gruß oldmax
 
Hi,

.. da ist noch was ganz dringendes.
Wer soll deine Listings lesen ? Bitte gewöhn dir diesen Stil nicht an. Sicherlich ist die folgende Schreibweise nicht die Entdeckung des Weltalls, aber doch wesentlich besser lesbar:
...
...
Ich muss hier mit Leerzeichen arbeiten, aber im Assemblerlisting nimmt man Tab's. Damit wird es dann wesentlich übersichtlicher. Auch eine Rahmengestaltung mit Kommentarzeilen vor Programmblöcken macht sich im Listing ganz gut. Leere Zeilen kannst du ohne Semikolon einfügen. Das Semikolon verwirrt da nur.
genau. Ne saubere Struktur wirkt bei der Fehlersuche Wunder. Außerdem kann man dann auch noch nach mehreren Wochen was damit anfangen und schmeißt den Code nicht einfach weg weil man nicht mehr durchsteigt ;)

Und nochwas: geh früher schlafen.....:wink:
dafür bist du aber recht früh an der Tastatur gewesen (5:00 morgens ... uah ... gähn ...:rolleyes:)

Gruß
Dino
 
vielen dank fuer ratschlaege,
werde versuchen sie zu beherzigen. anmerkung zu oldmax: beim tiny 13 glaube ich, gibt es keinen sph da das sram nur bis 009f geht.
muesste es dann heissen: sph 00 ? spl 9f
soweit ich das aus dem tutorial verstanden habe. erst ab ram > 256 byte zb atmega8 gibt es sph/spl.
falls nicht, bitte verbessere mich.

gruss bernhard.
 
Hi
Bezüglich der Adresse vom Stack hast du vermutlich recht.. ich hab nicht nach dem Controllertyp geschaut.
@Dino
Du weißt doch.... der frühe Vogel fängt den Wurm....
(Wer immer auch diesen blöden Spruch erfunden hat... als wenn ich Würmer fangen wollte....)
Bei mir ist die Nacht um 04:00 Uhr zu Ende. ( ich brauch so gefühlte 90 min. zum wach werden.... was sich leider auch in den Tippfehlern bemerkbar macht) ) Dafür hab ich in der Regel 14:30 Uhr Feierabend
Gruß oldmax
 
Hallo Freunde
habe ein ähnliche Problem mit den Timern. Habe mal ein Teil vom Programm reingestellt. Es läuft ganz gut, doch sind mir einige Teilr unklar.

Code:
void nibo_timer2(){                    
    TCNT2 = 0;                        
    TCCR2 &=~ (1<<CS22);                
    TCCR2 |= (1<<CS21);
    TCCR2 |= (1<<CS20);
    TCCR2 |= (1<<WGM21);
    TCCR2 &=~ (1<<WGM20);                  
    OCR2 = 250;
    TIMSK |= (1<<OCIE2);                    
    }                            


ISR (TIMER2_COMP_vect){
    zaehler1_t2++;
    if(zaehler1_t2>=65530)zaehler1_t2=0;


Ich verwende einen Atmega 128 mit einem 16 MHz Quarz. So wie das Programm oben ist, läuft es. Nach dem Datenblatt vom 128 arbeite ich mit PWM?. Ich wollte auf CTC umstellen. Doch dann bekomme ich Fehlermeldungen bzw der Timer läuft nicht. Wen ich in den Zeilen das | und ~ rauslösche, geht der Timer ebenfalls nicht. Auch in der Schreibweise (1<<CS21)|(1<<CS22| usw. bekomme ich Fehlermeldungen. Wozu sind diese Zeichen da. Im Datenblatt sind sie nicht angegeben. Auch wenn ich die entsprechenden Zeile wie TIMSK oder ISR in OVL ändere bekomme ich Fehlermeldungen. Im Netz finde ich einiges an Erklärungen. Diese sind aber immer anhängig vom Prozessortyp. Daher kann ich einige Sachen nicht einfach übernehmen.
Kann jemand es mir kurz erläutern. Danke für eure Hilfe
Achim
 
Hallo Achim,

der Timer2 läuft nach deinem Programm bereits im CTC Modus.

Der CTC Modus wird mit WGM21=1 und WGM20=0 eingestellt. Erreicht der Timer den Wert <ORC2>, wird der Timer/Counter2 auf 0x00 zurückgesetzt und es wird ein OutputCompareInterrupt ausgelöst, falls dieser freigegeben wurde und die Interrupts global freigegeben wurden.

Ich weiß nun nicht, was du an deinem Programm geändert hast, was zur Fehlermeldung geführt hat.

Bezüglich der Schreibweise in C, wie man in Registern einzelne Bits setzt oder löscht, schaue dir vielleicht mal den FAQ-Bereich an oder das GCC Turtorial bei mikrocontroller.net.

Noch einen Hinweis:

Dein Programm ...
Code:
void nibo_timer2(){                    
    TCNT2 = 0;                        
    TCCR2 &=~ (1<<CS22);                
    TCCR2 |= (1<<CS21);  <-- [B]ACHTUNG: Ab hier läuft der Timer mit einer nicht nicht korrekten fclk! OCR2 ist noch nicht initialisiert![/B]
    TCCR2 |= (1<<CS20);
    TCCR2 |= (1<<WGM21);
    TCCR2 &=~ (1<<WGM20);                  
    OCR2 = 250;
    TIMSK |= (1<<OCIE2);                    
}

Schreibe lieber in folgender Reichenfolge:
Code:
void nibo_timer2(){                    
   TCNT2 = 0;                           // zuerst Timer und Compare-Wert initialisieren!
   OCR2 = 250;
     
   TCCR2 = (1<<WGM21) | (1<<CS21) | (1<<CS20); // CTC, Prescaler 64, ab hier läuft der Timer

   TIMSK |= (1<<OCIE2);                    
}




Grüße,
Dirk
 
Hallo Dirk
Danke für deine Antwort. Es dämmert langsam. Das Licht am Horizont ist schon sichtbar.
Diese Tutorial habe ich durchgearbeitet. Verstehe aber was von der schreibweise nicht so richtig. Bei einigen Beispioelen im Netz TCCR2|=~ ... , bei dir steht es nicht drin. Was bedeiten diese Zeichen im Timer? Habe einen Artikel gefunden, in dem steht das dadurch der Befehl ausgeschaltet wird bzw. der Timer. Was ist den nun richtig?
Wenn mein Timer bereits im CTC steht, muss es den im nicht IST (Timer_OVL ... ) heissen? Im Datenblat vom 128 steht zu TIMSK drin das es TIMSK=(1<<OCIE0); sein soll. Damit wird doch das Bit 0 auf eins gesetzt. Bei zwei ist es doch was anderes. Da ich WGM20 und 21 gesetzt habe ist doch PWM an oder ? Bei dir nimmst du nur WGM 21 und damit ist CTC drin. Danke dir für deine Hilfe.
Achim
 
Hallo Achim,


Bits setzen und löschen:

Code:
TCCR2 = (1<<WGM21) | (1<<CS21) | (1<<CS20); // setzt nur die Bits WGM21, CS21 und CS20. Alles andere ist "0", [B]also auch WGM20[/B]!.


TCCR2 = (1<<WGM21); // setzt nur das Bit WGM21, alles andere ist "0"

TCCR2 |= (1<<CS21) | (1<<CS20); // setzt die Bits CS21 und CS20, alles andere wird nicht verändert (hier startet der Timer, da mindestens ein Prescalerbit gesetzt ist)
andere Schreibweise hierfür:
TCCR2 = TCCR2 | (1<<CS21) | (1<<CS20);

TCCR2 &= ~((1<<CS22) | (1<<CS21) | (1<<CS20));  // löscht die Bits CS21 und CS20, alles andere bleibt unberührt (hier stoppt der Timer, da kein Prescalerbit mehr gesetzt ist)
andere Schreibweise hierfür:
TCCR2 = TCCR2 & ~((1<<CS22) | (1<<CS21) | (1<<CS20));

| bedeutet ODER
& bedeutet UND
~ bedeutet bitweise invertiert

CTC heißt Clear Timer on Comparematch (setze den Timer auf 0, wenn ein Vergleichsereignis eintritt). Im CTC Modus wird der Timer/Counter-Wert mit dem Inhalt des Registers OCR2 verglichen. Sind beide Werte gleich, dann wird der Timer/Counter auf 0 zurückgesetzt und der OutputCompareInterrupt ausgelöst (durch setzen vom Bit OCIE2 im Register TIMSK freigegeben). Der OverflowInterrupt tritt dann auf, wenn der Timer überläuft. Im CTC Modus möchtest du ja den OutputCompareInterrupt, das ist dann in deinem Programm so schon richtig.

Dirk
 
Hallo Dirk
nochmal Danke für deine Hilfe. Habe es gestern eingetippt und Ausprobiert. Es läuft hervorragend. Die Auswertung beider Teile ist gleich. Es reicht dieser kurze Text dazu. Habe zum weiteren testen noch ein paar Zähler eingebunden und die Zeiten mit einander verglichen. Nach meinen Messungen werden alle korekt erreicht. Bei diesen Einstellungen und einem Quarz von 16MHz erreiche ich 1ms.
Eigentlich bin ich durch einen andere Sache auf diese Timer gekommen. Es gibt doch keinen wait Befehl. Bei verwendung von delay wird doch der Prozessor gestoppt und macht während der Wartezeit auch nichts anderes. In anderen Systemen gibt es für diesen Fall den wait Befehl. Wie kann ich das vernünftig umgehen. Es gibt die Möglichkeit über entsprechend viele Zähler zu gehen. Dazu muss ich die Zähler jedesmal aufrufen usw. Man kann doch über einen Timer einen Impuls bringen der für jeden Zähler egal wo er sitz genommen wird. Oder hast du eine bessere Idee zu wait?
Achim
 
Hallo Achim,

du könntest in der Timer-Interruptroutine einen Zähler laufen lassen. Wenn der Zähler einen bestimmten Wert erreicht, wird ein Flag gesetzt, welches du im Hauptprogramm auswerten kannst. So kannst du dir Ereignisse erzeugen, die bestimmte Sachen in deinem Programm in Zeitintervallen oder nach Pausen auslösen.

Hier mal ein Beispiel mit zwei Countern in der ISR. Druch den einen Counter wird ein Ereignis alle 500ms ausgelöst (kannst du zB verwenden um eine LED blinken zu lassen), der andere Counter läßt sich vom Hauptprogramm starten und erzeugt nach vorgegebner Zeit einmalig ein Ereignis.

Der Beispielsource soll nur das Prinzip zeigen, es gibt auch noch etliche weitere Lösungen, compiliert habe ich den Beispielcode nicht.

Grüße,
Dirk


Code:
volatile uint16_t CounterA;
volatile uint16_t CounterB;
volatile uint8_t SystemFlags;
#define Flag_Event       0
#define Flag_EnableEvent 1
#define Flag_500ms       2

TimerInit
{

  SystemFlags = 0 ;
  CounterA = 0;
  CounterB = 0;
  // hier den Timer und die ISR initialisieren

}



TimerISR:

  // 1ms Event

  if (0 == CounterB)
  {
    CounterB = 500;
    SystemFlags |= (1<<Flag_500ms);
  } else {
    CounterB--;
  }
  
  if ((SystemFlags & (1<<Flag_EnableEvent)) == (1<<Flag_EnableEvent))
  {
    if (0 == CounterA)
    {
      SystemFlags |= (1<<Flag_Event);
      SystemFlags &= ~(1<<Flag_EnableEvent);
    } else {
      CounterA--;
    }
  
}


in main(void)
{

  // Initialisierungen

  while (1)
  {

    if ((SystemFlags & (1<<Flag_500ms)) == (1<<Flag_500ms))
    {
      cli();
      SystemFlags &= ~(1<<Flag_500ms); // Flag wieder löschen
      sei();
      // hier irgendwas alle 500ms machen, zum Beispiel LED togglen

    }


    if (Taste_gdrueckt) event_start(20000);  // Diese Routine wartet nicht 20 Sekunden, sondern startet den Timer für das Ereignis Flag_Event
 
    if ((SystemFlags & (1<<Flag_Event)) == (1<<Flag_Event))
    {
      cli();
      SystemFlags &= ~(1<<Flag_Event); // Flag wieder löschen
      sei();
      // Das Ereignis tritt 20 Sekunden nachdem die Taste gedrückt wurde ein

    }


  }
  return 0;
}

void event_start(uint16_t time)
{

  cli();
  CounterA = time;
  SystemFlags |= (1<<Flag_EnableEvent);
  SystemFlags &= ~(1<<Flag_Event);
  sei();

}
 
Danke dir für deine Antwort. Auf den ersten Blick .. toll. Mein Licht am Horizont ist beim Untergehen. Stelle gerade fest, ich weiss das ich nichts weiss. Danke, probiere und melde mich wieder
Achim
 
Hallo Dirk
habe angefangen deinen Code zu studieren. Im Moment versteh ich nur Bahnhof. Dabei kommt gleich eine Frage. Habe immer gelesen, das Interrupts so kurz wie möglich sein sollen. Wenn ich 2 Zähler laufen lasse braucht das doch Zeit. Verändert sich dadurch auch die Zeit für dir Timer und damit die Genauigkeit? Der Aufbau des Zählers innerhalb der ISR ist mir noch vollkommen unklar. Eigentlich dachte ich mehr an einen z.B. Impulsgeber, der mir regelmässige Impulse bringt, die ich mit dem Hauptprogramm auswerte und z.B. in Schleifen verwenden kann. Ich möchte im Hauptprogramm nicht 10 oder mehr Zähler aufmachen.
Achim
 
Hallo Achim,
Habe immer gelesen, das Interrupts so kurz wie möglich sein sollen. Wenn ich 2 Zähler laufen lasse braucht das doch Zeit. Verändert sich dadurch auch die Zeit für dir Timer und damit die Genauigkeit?

ja das ist auch vollkommen richtig. In eine ISR gehören nur Sachen, die zeitkritisch sind. Die Interruptzeit oder die "Genauigkeit" verändert sich nur dann, wenn die Ausführungszeit des Codes innerhalb der ISR 1ms erreicht, das ist hier nicht der Fall. Grob geschätzt benötigt der Code innerhalb der ISR etwa 20 Maschinenzyklen, das sind bei 16MHz Systemfrequenz etwa 1,2us. Wenn die TimerISR alle 1ms aufgerufen wird, ist das eine Systemauslastung von gut 0,1%. Das ist also nicht so viel. ... und irgendwas muss die Timer ISR ja machen, noch weniger wäre einen IO-Pin toggeln o.ä ;)

Der Aufbau des Zählers innerhalb der ISR ist mir noch vollkommen unklar. Eigentlich dachte ich mehr an einen z.B. Impulsgeber, der mir regelmässige Impulse bringt, die ich mit dem Hauptprogramm auswerte und z.B. in Schleifen verwenden kann.

Genau das macht die ISR. Der Bereich mit dem CounterB erzeugt dir zum Beispiel alle 500ms ein Signal (das Bit Flag_500ms in SystemFlags wird gesetzt). Das Signal kannst du im Hauptprogramm abfragen. Bei dem CounterA kannst du die Ereigniszeit im Hauptprogramm festlegen und erhältst einmalig ein Signal, danach "schaltet" sich der Counter wieder ab. Am besten schau dir erst mal nur den Bereich mit CounterB an, einfacher kann man das fast nicht machen. Falls du Verständnisprobleme mit der Bitabfrage hast, verwende dann einfach erst mal ganze Bytes anstelle von Flags (Bits), die du abfragen kannst.

Dirk
 
Hallo Dirk
habe die schreibweise deines Timers nicht ganz geschnallt. Benutze die Sache mit 1<< nicht. Bin noch bei der "alten Methode". Wenn ich es richtig verstanden habe setzt du Counter B auf 0, später fragst du ob if(0==CounterB) dann setzt du CounterB auf 500, setzt SystemFlags|=( .. alles in den Zeilen 22 bis 28.
Damit bringst du einen Takt von 500ms z.B. für LED oder so.

ISR (TIMER2_COMP_vect)
{
wait++;
if(wait>=65530)wait=0;
}
Hatte das ausprobiert. Es läuft, muss nur noch die richtigen Zeiten bekommen. Bin dabei von einer Zeit von 1ms ausgegangen und will einen Takt von 10ms erzeugen. Dieser soll in wait stehen und durch die nächsten Zähler verarbeitet werden können. ie Grösse mit 65530 und auch nichz gerade gut. Habe auch mit dem Timer 0 probiert. Da dieser aber nur 255 hat, stimmt die Zeit nicht. Welcher Timer ist den besser der mit 255 oder 65530?
Achim
 
Hallo Achim,

so ganz verstehe ich nicht, was du gerne machen möchtest.

Zunächst ist es mal so, dass der Counter innerhalb der ISR nichts mit dem Timer/Counter0 oder 2 des AVR zu tun hat. Wenn du einen Systemtakt von 16MHz und eine Interruptzeit von 1ms ereichen möchtest, dann reicht ein 8Bit Timer (Timer0 oder Timer2), du kannst natürlich auch einen 16Bit Timer (Timer1 oder Timer3) verwenden.

  1. Möchtest du nun einen Zähler für das Hauptprogramm haben, der durch die TimerISR automatisch alle 10ms inkrementiert wird,
  2. oder möchtest du ein Signal für das Hauptprogramm haben, dass alle 10ms erzeugt wird?
Bei deiner Version ...
Code:
ISR (TIMER2_COMP_vect)
    {
    wait++;
    if(wait>=65530)wait=0;
    }
wird ja wait alle 1ms um 1 erhöht, nach 65530ms wird der Zähler auf 0 zurückgesetzt und wird dann wieder alle 1ms erhöht. Im Hauptprogramm kannst du dadurch zwar Zeiten messen, allerdings ist der Überlauf kritisch.
Wenn du im Hauptprogramm das Ereignis wait==0 auswerten möchtest, das wird eventuell nicht sicher funktionieren, je nach Struktur des Hauptprogramms könnte es sein, dass das Hauptprogramm wait==0 nicht mitbekommt.

Aber wie schon geschrieben, so ganz weiß ich nicht, was du machen möchtest.

Noch ein Hinweis zur Timerinitialisierung:
Der Comparewert stimmt nicht ganz, dieser muss den Wert 249 haben. (fck = 16MHz, Prescaler = 64)
Schau dir auch vielleicht mal unser kleines Programm zur Timerberechnung an: AVR Timer Calculator

Grüße,
Dirk
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)