ATtiny13A, TCCR0B Prescaler für Timer in main() mehfach ändern?

OP #1580239
Lesenswert?

Hallo,

für das generieren von mehreren festen Testfrequenzen (25 Hz, 50 Hz, 100 
Hz...) über einen ATtiny13A muss ich den Prescaler bemühen, damit ich 
möglichst nah an der Zielfrequenz dran bin.

Leider funktioniert das neu setzen des Prescalers in TCCR0B  innerhalb 
von einer Schleife in main() nicht, wie erhofft; der erste Counter läuft 
wie erwartet, ab der nächsten Änderung (im Code unten setzen des 
Prescalers auf 64) sind die Ergebnisse nicht mehr determinierbar.
Ich vermute, das ich irgendeine Initialisierungsreihenfolge übersehen 
habe, habt Ihr dazu einen Tip? Prescaler Artefacts sind für eine 
Frequenzerzeugung wie unten kein echtes Problem, da sie nur den ersten 
Durchlauf von OCR0A betreffen, korrekt?

Danke,
Adrian
1
//
2
// *** ATTiny13A with 1.2 MHz as shipped ***
3
//
4
#define F_CPU 1200000UL 
5

6
#include <util/delay.h>
7
#include <avr/power.h>
8
#include <avr/interrupt.h>
9
#define SEQ_DELAY 5000  
10

11
int main (void)
12
{
13

14
// set PB0 as output port
15
DDRB |= (1 << PB0);
16

17
//set CTC Mode
18
TCCR0A |= (1 << WGM01); 
19
 
20
// set Timer/Counter0 Output Compare Match A Interrupt Enable
21
TIMSK0 |= (1 << OCIE0A); 
22

23
sei();
24
 
25
   for (;;)
26
   {
27
   //  set prescaler to 256
28
   TCCR0B |= (1 << CS02);   
29

30
   OCR0A   = 180;
31
   _delay_ms(SEQ_DELAY);
32

33

34
   //  set prescaler to 64
35
   TCCR0B |= (1 << CS00);   
36
   TCCR0B |= (1 << CS01); 
37

38
   OCR0A   = 93;  
39
   _delay_ms(SEQ_DELAY);
40
  }
41
}
42
 
43
ISR(TIM0_COMPA_vect)
44
{
45
  // create a single pulse with 1 ms duration
46
  PORTB |= (1<< PINB0);
47
  _delay_ms(1);
48
  PORTB &= ~(1<< PINB0);
49
  
50
  // Enable Interrupts again
51
  SREG |= (1 << 7);
52
}
Gast #1580265
Lesenswert?

Hallo Adrian,

und wer löscht CS2 ??

probier mal
1
  //  set prescaler to 256
2
   TCCR0B = (1 << CS02);   
3

4
   OCR0A   = 180;
5
   _delay_ms(SEQ_DELAY);
6

7

8
   //  set prescaler to 64
9
   TCCR0B =   (1 << CS00)
10
            | (1 << CS01);

Gruß,
Jürgen
Gast #1580305
Lesenswert?

>für das generieren von mehreren festen Testfrequenzen (25 Hz, 50 Hz, 100
>Hz...) über einen ATtiny13A muss ich den Prescaler bemühen, damit ich
>möglichst nah an der Zielfrequenz dran bin.

Versteh ich nicht. 1000 Hz kannst Du genau [*] generieren (Timer Mode = 
CTC, Prescaler = 8, OC-Wert = 150 bzw. 75) und aus 1000 Hz kannst Du 500 
Hz, 250 Hz, 200 Hz, 125 Hz, 100 Hz, 50 Hz, 40 Hz, 25 Hz, 20 Hz, 10 Hz 
und noch viele andere Frequenzen problemlos per Softwareteilung 
ableiten.

[*] Wobei "genau" bei Verwendung des internen RC-Oszillators als 
Taktquelle relativ ist.
OP #1580631
Lesenswert?

Jürgen, vielen Dank! Peinlicher Fehler, alle drei Bits gesetzt ist nicht 
dokumentiert, kein Wunder daß das nicht funktioniert.

Gastofatz, ich hab bisher der Einfachheit halber auf eine 
Softwareteilung verzichtet. Bis zu 2-3% Fehlerrate reicht bei meiner 
Anwendung, und das bekomme ich mit zwei Teilern (64 und 256) problemlos 
hin.  Mit dem internen RC Oszillator schwankt die Ausgabe bei 
Zimmertemperatur um 0.3-0.5%, da kann ich auch gut mit leben.

Vielen Dank für die Unterstützung,
Adrian
#1580655
Lesenswert?

Ähm
1
ISR(TIM0_COMPA_vect)
2
{
3
  // create a single pulse with 1 ms duration
4
  PORTB |= (1<< PINB0);
5
  _delay_ms(1);
6
  PORTB &= ~(1<< PINB0);
7
  
8
  // Enable Interrupts again
9
  SREG |= (1 << 7);
10
}
Der delay da drinnen ist jetzt aber nicht dein Ernst. Oder?
Was glaubst du, wie das die 'Genauigkeit' deines
   _delay_ms(SEQ_DELAY);
in der Hauptschleife beeinflussen wird :-)

Grrrr:
1
  // Enable Interrupts again
2
  SREG |= (1 << 7);
3
}
Lass das Interrupt Flag im SREG in Ruhe!
Interrupts sind in einer ISR automatisch ausgeschaltet und werden nach 
Beeindigung der ISR automatisch wieder eingeschaltet. Mit deiner Aktion 
da oben bettelst du praktisch um Schwierigkeiten.
#1580665
Lesenswert?

A. L. schrieb:
> Dann habe ich den falschen Ansatz im Kopf, erklär mal bitte kurz den
> Ansatz. Ich fand den CTC Timer schon recht einfach, aber wenn es noch
> einfacher geht... ;)
>

Dazu müsste man erst mal genau wissen, was eigentlich das Ziel ist. 
Willst du hintereinander an einem Pin 2 verschiedene Frequenzen jeweils 
eine bestimmte Zeitdauer ausgeben? (zumindest sieht es so aus)
OP #1580671
Lesenswert?

Nebenschauplatz, ist auch nicht mein Ernst, wie Du schon erraten hat :) 
Danke für den SREG Tip, ich war der Annahme, daß ich am Ende der ISR die 
Interrupts wieder aktivieren muss.

SEQ_DELAY soll die jeweils aktuelle Frequenz ein paar Sekunden für die 
Ansicht am Scope ausgeben. Das Programm oben ist nur ein schnell 
reingehackter Test der Idee, in ein Tachoadapter Projekt noch eine 
Kalibrierfunktion für den Tacho mit einzubauen.
Gast #1582212
Lesenswert?

Folgender Codeschnipsel möge das Prinzip der Softwareteilung 
verdeutlichen:
1
    t = t + 1;
2

3
    IF (t==n)
4
      {
5
      t = 0;
6
      a = ~a;
7
      <Portpin gemaess a auf L oder H setzen>
8
      }

Diese Routine rufst Du in Deinem Fall 2000 mal pro Sekunde auf. Um die 
Toggle-Frequenz f des Portpins zu ändern, musst Du nur den Wert von n 
passend setzen:

n = 1    -->  f = 1000 Hz
n = 2    -->  f = 500 Hz
n = 4    -->  f = 250 Hz
n = 5    -->  f = 200 Hz
n = 10   -->  f = 100 Hz
n = 20   -->  f = 50 Hz
n = 40   -->  f = 25 Hz
n = 50   -->  f = 20 Hz
n = 100  -->  f = 10 Hz
n = 200  -->  f = 5 Hz

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