Forum: Compiler & IDEs Port alle 200ms ein-/ausschalten


von C. Schönauer (Gast)


Lesenswert?

Hallo zusammen!

Ich versuche nun schon seit etwa 3 Stunden den Timer1 meines ATtiny45 (@ 
1Mhz) dazu zu bringen alle 200ms PB0 ein-/auszuschalten. 
Compiler-Fehermeldungen habe ich keine.

1
#include <stdlib.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <avr/sleep.h>
5
6
volatile uint8_t i=0;
7
8
ISR(TIMER1_OVF_vect) {
9
  i++;
10
  if(i>200){
11
  i=0;
12
  PORTB ^= (1<<PB0);
13
  }
14
}
15
16
int main() {
17
DDRB |= (1<<PB0);
18
19
TCCR1 = (1<<CS13)|(1<<CS11)|(1<<CS10);
20
TIMSK |= (1<<TOIE1);
21
22
sei();
23
24
while(1);
25
return 0;
26
}

Da mein Controller bei 1Mhz werkelt müsste doch ein Prescaler von 1024 
etwa alle 1ms den Interrupt ausführen? Was hab ich faksch gemacht?

Grüsse C. Schönauer

von Stefan B. (stefan) Benutzerseite


Lesenswert?

C. Schönauer schrieb:

> Da mein Controller bei 1Mhz werkelt müsste doch ein Prescaler von 1024
> etwa alle 1ms den Interrupt ausführen? Was hab ich faksch gemacht?

Rechne nochmal nach. Ich komme auf 13421,7728 Sekunden statt 200ms. Ich 
denke du hast nicht berücksichtigt, dass Timer1 16-Bit breit ist und der 
Interrupt beim Overflow kommt.

> Ich versuche nun schon seit etwa 3 Stunden

Mit etwas mehr Geduld (~3,7h) hätte das Programm etwas ausgegeben!

von Gast (Gast)


Lesenswert?

>Da mein Controller bei 1Mhz werkelt müsste doch ein Prescaler von 1024
>etwa alle 1ms den Interrupt ausführen?

Timer1 läuft mit 1 MHz/1024 = 976 Hz, d. h. er zählt alle 1.024 ms eins 
rauf. Da er das 256 mal machen muss bis es zu einem Overflow Interrupt 
kommt, tritt dieser Interrupt alle 0.262144 s auf. Wegen des 
Softwareteilers (Variable i) von 200 wird Dein Pin alle 52.4288 s seinen 
Zustand ändern.

Fazit: Du bist nur zu ungeduldig.

>Was hab ich faksch gemacht?

Das "k"! :-D Außerdem hast Du auf gut Glück herumprobiert anstatt im 
Datenblatt nachzulesen, wie die Timer funktionieren.

@Stefan: Timer1 beim ATtiny45 ist nur 8 Bit breit.

von gast (Gast)


Lesenswert?

am besten gleich CTC modus
und einen timercompareinterrupt bei 200ms  :)

von Gast (Gast)


Lesenswert?

Und das Nonplusultra: CTC-Modus mit Compare-Interrupt bei 10 ms und 
diese 10 ms softwaremäßig auf 200 ms runterteilen. Ein Basistakt von 10 
ms ist noch für viele andere Sachen prima geeignet, z. B. Tasten 
entprellen (200 ms wäre dafür schon zu langsam).

von C. Schönauer (Gast)


Lesenswert?

Hallo zusammen,

vielen Dank für die vielen Antworten!
Hab jetzt probiert einen 10ms Interrupt zu programmieren. (Prescaler=64)
OCR1A = 10/(1/1000000*64*1000) => 156

Das gibt mir doch dann folgenden Code:
1
#include <stdlib.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <avr/sleep.h>
5
6
volatile uint8_t i=0;
7
8
ISR(TIMER1_OVF_vect) {
9
  i++;
10
  if(i>20){
11
  i=0;
12
  PORTB ^= (1<<PB0);
13
  }
14
}
15
16
int main() {
17
DDRB |= (1<<PB0);
18
19
TCCR1 = (1<<CS12)|(1<<CS11)|(1<<CS10);
20
OCR1A = 156;
21
TIMSK |= (1<<OCIE1A);
22
23
sei();
24
25
while(1);
26
return 0;
27
}

Ist das so korrekt?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

C. Schönauer schrieb:

> Ist das so korrekt?

Nein

== 1 ==
Lies nochmals im Handbuch über den CTC-Mode nach
-- Das CTC-Flag in TCCR1 muss gesetzt werden
-- Der CTC-Wert steht in OCR1C
OCR1A resp. OCR1B kann also immer 0 sein

== 2 ==
Du verwendest den falschen IRQ-Vektor.

== 3 ==
Anstatt der magischen Zahl 156 tut's auch die Rechnung
1
#define F_CPU 1000000
2
#define IRQS_PER_SECOND 100
3
#define PRESCALE 64
4
5
OCR1C = (uint32_t) F_CPU / IRQS_PER_SECOND / PRESCALE -1;

Beachte, daß F_CPU modulo (IRQS_PER_SECOND*PRESCALE) nicht 0 ist, d.h. 
die anvisierte Frequenz wird nicht exakt getroffen.

Johann

von Gast (Gast)


Lesenswert?

>Ist das so korrekt?

Ich würde zum Testen in den i-Teiler einfach noch einen weiteren 
Software-Teiler schalten, der durch 5 teilt. Dann Proggi laufen lassen 
und gucken ob ne LED an dem Pin brav mit 1 Hz blinkt (genaugenommen 
ändert sie ihren Zustand jede Sekunde, blinkt also mit der Frequenz 0.5 
Hz). Alternativ: Großer Teilfaktor und mit der Stoppuhr messen.

Dein Interrupt-Intervall ist übrigens nicht genau 10 ms, sondern 9.984 
ms.  Bei einer Uhrenanwendung würdest Du den Fehler schnell spüren. 
Leider lässt sich 10 ms bei 1 Mhz Systemclock mit einem 8-Bit-Timer 
schlecht erzeugen. Aber 8 ms ginge exakt, nämlich mit Prescaler 64 und 
OCR-Wert 125. Aus 8 ms kannst Du ja auch 200 ms problemlos ableiten.

von gast (Gast)


Lesenswert?

der richtige irq sollte dann gewählt werden ...

ISR(TIMER1_OVF_vect) {


stimmt dann nicht
es heißt dann

ISR(TIMER1_COMP_vect)

von C. Schönauer (Gast)


Lesenswert?

So, ich glaube wir kommen dem Ziel näher ;)
Leider blinkt die LED immernoch nicht. Die oben erwähnten Punkte hab ich 
(hoffentlich richtig) korrigiert.
1
#include <stdlib.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <avr/sleep.h>
5
6
#define IRQS_PER_SECOND 100
7
#define PRESCALER 64
8
9
volatile uint8_t i=0;
10
11
ISR(TIMER1_COMPA_vect) {
12
  i++;
13
  if(i>20){
14
  i=0;
15
  PORTB ^= (1<<PB0);
16
  }
17
}
18
19
int main() {
20
DDRB |= (1<<PB0);
21
22
TCCR1 = (1<<CTC1)|(1<<CS12)|(1<<CS11)|(1<<CS10);
23
OCR1A = 0;
24
OCR1C = (uint32_t)(F_CPU/IRQS_PER_SECOND/PRESCALER-1);
25
TIMSK |= (1<<OCIE1A);
26
27
sei();
28
29
while(1);
30
return 0;
31
}

* CTC-Flag ist gesetzt
* Prescaler 64 gewählt
* OCR1C berechnet (~156)
* Compare Interrupt aktiviert
* Interrupts allgemein aktiviert
* Auf TIMER1_COMPA_vect wird reagiert

Grüsse
C. Schönauer

von Stefan E. (sternst)


Lesenswert?

1
OCR1A = 0;
2
OCR1C = (uint32_t)(F_CPU/IRQS_PER_SECOND/PRESCALER-1);
> * OCR1C berechnet (~156)

Welche Überlegung steckt denn dahinter, den berechneten Compare-Wert in 
OCR1C zu schreiben?

Edit: Sehe gerade, dass das von Johann L. kommt. Keine Ahnung, was ihn 
da geritten hat.

von C. Schönauer (Gast)


Lesenswert?

Zum CTC1-Bit steht im Datenblatt:

"When the CTC1 control bit is set (one), Timer/Counter1 is reset to $00 
in the CPU clock cycle
after a compare match with OCR1C register value. If the control bit is 
cleared, Timer/Counter1
continues counting and is unaffected by a compare match."

von Stefan E. (sternst)


Lesenswert?

Also in meinem Datenblatt steht da OCR1A.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Stefan Ernst schrieb:

> Welche Überlegung steckt denn dahinter, den berechneten Compare-Wert in
> OCR1C zu schreiben?
>
> Edit: Sehe gerade, dass das von Johann L. kommt. Keine Ahnung, was ihn
> da geritten hat.

Es ist die Überlegung, das Handbuch zu lesen (ATtiny45, pp. 92):
1
 12.3.1 TCCR1 – Timer/Counter1 Control Register
2
 Bit 7- CTC1 : Clear Timer/Counter on Compare Match
3
 When the CTC1 control bit is set (one), Timer/Counter1 is reset to $00
4
 in the CPU clock cycle after a compare match with OCR1C register value.
5
 If the control bit is cleared, Timer/Counter1 continues counting and is
6
 unaffected by a compare match.

von Stefan E. (sternst)


Lesenswert?

Wie gesagt, in meinem Datenblatt (2586K–AVR–01/08) steht da OCR1 A.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Das da?

http://atmel.com/dyn/resources/prod_documents/doc2586.pdf

Auch da steht OCR1C in 12.3.1, hab eben nochmal extra in die 
Onine-Version geschaut.

Der CTC-Reset geschieht bei == OCR1C.
Der IRQ-Trigger geschieht bei == OCR1A bzw. == OCR1B. Und da beim Reset 
Timer1 auf 0 gesetzt wird, kann man hier einfach OCR1A=0 nehmen.

Hab den Timer auch so am laufen, allerdings mit OCR1B.

Johann

von Stefan E. (sternst)


Lesenswert?

Ja, ja, ich bin der Idiot hier. :-(

Ich hatte das hier offen:
13. 8-bit Timer/Counter1 in ATtiny15 Mode

@ C. Schönauer:
Dann musst du aber auch TIMER1_COMPC_vect nehmen.

von C. Schönauer (Gast)


Lesenswert?

Hab grad getestet:
Ob OCR1A = 156 und OCR1C = 156, nur OCR1C = 156 oder nur OCR1A = 156, 
macht keinen Unterschied...Muss also was anderes falsch sein.

Die Schaltung stimmt: Wenn ich den uC rausnehme und VCC mit PB0 verbinde 
leuchtet die LED.

von Stefan E. (sternst)


Lesenswert?

Stefan Ernst schrieb:

> @ C. Schönauer:
> Dann musst du aber auch TIMER1_COMPC_vect nehmen.

Ach, den gib es ja anscheinend gar nicht. Am besten halte ich mich aus 
diesem Thread einfach raus. ;-)

von C. Schönauer (Gast)


Lesenswert?

@ Stefan Ernst
TIMER1_COMPC_vect gibts leider nicht bzw. mein AVR-GCC kennt's nicht.

Laut der Header-Datei /usr/avr/include/avr/iotnx5.h gibts nur 
TIMER1_COMPA_vect (Makro auf "_VECTOR(3)")und TIMER1_COMPB_vect.

_VECTOR(3) würde wieder mit dem Datenblatt (zu TIMSK) übereinstimmen:

"OCIE1A: Timer/Counter1 Output Compare Interrupt Enable
[...] The corresponding interrupt at vector $003 is executed if a 
compare matchA occurs. The Compare Flag in Timer/Counter1 is set (one) 
in the Timer/Counter Interrupt Flag Register."

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

C. Schönauer schrieb:
> [...] Muss also was anderes falsch sein.

Möglicherweise hängts auch an den Fuses, zB ein aktivierter WDT der 
immer wieder zu einem Reset führt oder ein falsch eingestellter 
Prescaler per CLKPR.

Den Port kann man übrigens toggeln via
1
PINB |= (1 << PB0);

was gegenüber dem ^= den Vorteil hat daß es kürzer und vor allem atomar 
ist.

Johann

von Gast (Gast)


Lesenswert?

ATtiny25/45/85 + Timer1 + CTC-Modus ist in der Tat ein Kapitel für sich.

Such mal über die Suchfunktion nach dem Thread

"ATtiny25, TimerCounter1, CTC-Mode: Bug?"

von 12/2008 und lies ihn genau durch. Da hatte jemand dieselben Probs.
Die schließlich gefundene Lösung steht auch drin.

von Gast (Gast)


Lesenswert?

PS: Dasselbe mit Timer0 ist übrigens ein Klacks.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Beitrag "ATtiny25, TimerCounter1, CTC-Mode: Bug?"

Da geht's allerdings um PWM-Betrieb, also ne was andere Baustelle.

von Gast (Gast)


Lesenswert?

Ich dachte, es läuft darauf hinaus, das Ding im PWM-Mode betreiben zu 
müssen, um das gewünschte CTC sozusagen als Nebeneffekt zu bekommen (d. 
h. die PWM wird zwar erzeugt, aber nicht auf einen Pin geschaltet).

Oder hab ich das falsch verstanden?

von C. Schönauer (Gast)


Lesenswert?

Das Problem konnte behoben werden in dem der Code anstatt auf einer 
Linux-Maschine auf einer Windows-Maschine mit AVR Studio kompiliert 
wurde. Vielleicht funktionierts auch unter Linux wenn ich die 
AVR-Studio-Compiler-Flags mal identisch übernehme.

Grüsse
C. Schönauer

PS: Sorry das ich den Thread nochmals aufrolle, dachte es sei gut wenn 
später jemand hierrauf zurück kommt

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.