Port alle 200ms ein-/ausschalten

Gast #1458098
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
Persönliche Seite #1458123
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!
Gast #1458128
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.
Gast #1458238
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?
Persönliche Seite #1458314
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
Gast #1458335
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.
Gast #1458544
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
Persönliche Seite #1458565
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.
Persönliche Seite #1458568
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
Gast #1458587
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."
Persönliche Seite #1458615
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
Gast #1466943
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren