Forum: Mikrocontroller und Digitale Elektronik Atmega8 Timer1


von Florian K. (flo_ww)


Lesenswert?

Hallo Leute,

ich beschäftige mich gerade mit Timer1 des Atmega8. Hintergrund ist ein 
einfacher Rechteckgenerator mit einstellbarer Frequenz. Minimum ist 
eigentlich egal, liegt aktuell eh unter 1Hz. Maximum reichen 50kHz.

Der Timer läuft im CTC mit Output toggle bei match. Funktioniert auch 
gut. Prescaler von 64 bringt mir 62,5kHz bei OCR1A=0 und 0,9567Hz bei 
OCR1A=65535.
Nun hätte ich ja die 65535 Zähler um die Frequenz ein zu stellen. Das 
läuft aber nicht linear.
Zählt er bis 1 sind es 31,33kHz.
Zählt er bis 2 sind es nur noch 20,1kHz.
Kommt man dann in die Region 65535 lässt sich dafür die Frequenz auf 
0,0001Hz einstellen.
Rechnerisch ist das ja alles korrekt. Aber wie bekomme ich es nun hin 
das ganze linear zu verteilen? Ich steh gerade ein wenig auf der 
Leitung.

So sieht mein Versuch aus:
1
void port_init (void)
2
{
3
  DDRB=(1<<PB1);
4
}
5
6
void timer_init (void)
7
{
8
    TCCR1A=(1<<COM1A0);
9
    TCCR1B=(1<<WGM12)|(1<<CS10)|(1<<CS11);
10
    OCR1A=0;
11
}
12
13
  
14
int main(void)
15
{
16
    port_init();
17
    timer_init();
18
    while(1)
19
    {
20
    
21
    }
22
}

Grüße

Flo

von Rene K. (xdraconix)


Lesenswert?

Rechenaufgabe:

1
OCR1A = CLKio / Freq / 2 / Vorteiler - 1;

Oder halt ohne Vorteiler:

1
OCR1A = CLKio / Freq / 2 - 1;

Das heißt z.b. auf nem Mega8 mit 16Mhz und deinem Vorteiler von 64 und 
wie oben CTC sowie einer Ziel-Frequenz von 50 Herz:

1
OCR1A = 16000000 / 50 / 2 / 64-1;

Aaaaaber.... mach das über eine Variable, weil wie du siehst, es ist ein 
Uint32. Und durch die Division isses auch nicht sonderlich 
Resourcensparend. Ich mach das immer so:


1
#define F_CPU 16000000UL
2
3
void Tone(uint16_t Freq)
4
{
5
  uint32_t ocr = F_CPU / Freq / 2 / 64 - 1;
6
  OCR1A = ocr;
7
}

: Bearbeitet durch User
von Florian K. (flo_ww)


Lesenswert?

Hallo Rene,

danke für die schnelle Antwort.

Das Beispiel gefällt mir, werde ich sicherlich auch so übernehmen. Die 
Berechnung funktioniert gut für 50Hz.
Will ich aber als Zielfrequenz nun 25kHz haben ergibt es rechnerisch 
einen OCR1A von 1,5 (FCPU sind bei mir 8Mhz) Ich weiß nicht wie die 1,5 
umgesetzt werden, denke da wird dann auf oder abgerundet. Geht man von 1 
aus sind wir am unteren Ende der Skala und der Ausgang läuft mit 
31,33kHz anstelle der 25kHz. Würde man die zwei nehmen wären es nur noch 
20,1kHz. Wie treffe ich denn die Frequenz dazwischen dann?

Wenn ich mit Vorteiler auf 62,5kHz komme und bei dem 16 Bit Zähler bis 
65535 Zählen kann müsste man doch meinen das es eine Möglichkeit gibt 
das ganze auf ±1Hz genau zu verteilen.
1
#define F_CPU 8000000UL
2
3
void port_init (void)
4
{
5
  DDRB=(1<<PB1);
6
}
7
8
void timer_init (void)
9
{
10
    TCCR1A=(1<<COM1A0);
11
    TCCR1B=(1<<WGM12)|(1<<CS10)|(1<<CS11);
12
    //OCR1A=0;
13
}
14
15
void frq(uint16_t Freq)
16
{
17
  uint32_t ocr = F_CPU / Freq / 2 / 64 - 1;
18
  OCR1A = ocr;
19
}
20
  
21
int main(void)
22
{
23
  port_init();
24
  timer_init();
25
  frq(25000);
26
    while(1)
27
    {
28
    
29
    }
30
}

: Bearbeitet durch User
von Rene K. (xdraconix)


Lesenswert?

Dann nimm einen anderen Vorteiler. Oder besser, teile diesen auf:


1
uint32_t ocr = F_CPU / frequency / 2 - 1;
2
3
if (ocr > 65534)
4
{
5
   ocr = F_CPU / frequency / 2 / 64 - 1;
6
   TCCR1B = (1<<WGM12)|(1<<CS10)|(1<<CS11);
7
} 
8
else 
9
{
10
  TCCR1B = (1<<WGM12)|(1<<CS10);
11
}
12
13
OCR1A = ocr;


(Ich hab jetzt net geschaut ob da die Freq höher oder tiefer geht :D - 
je nachdem einen höheren oder tieferen Prescaler nehmen)

EDIT: Code von 16Mhz auf F_CPU geändert.

: Bearbeitet durch User
von Hannes (Gast)


Lesenswert?

Denkanstoß:

Mit den Timerwerten kannst Du nur die Periodendauer verstellen, dies 
erfolgt linear, also proportional zum Timerwert. Die Frequenz ist aber 
1/T und somit nicht linear. Vielleicht hilft Dir ja statt der Rechnerei 
ein Array mit Timerwerten und ein Index, der in Frequenz skaliert ist. 
Das lässt sich auch mit dem Vorteiler kombinieren, indem man Bereiche 
nutzt, die sich mit verschiedenen Vorteilern wiederholen.

...

von Rene K. (xdraconix)


Lesenswert?

Das wären ja dann ~62.000 Werte in diesem Array... schon fett. ;)

von Hannes (Gast)


Lesenswert?

Rene K. schrieb:
> Das wären ja dann ~62.000 Werte in diesem Array... schon fett. ;)

Nööö, das ist ja eine Frage der Auflösung. Siehe dieses Beispiel von 
2008:
Beitrag "Kleiner Funktionsgenerator mit Tiny2313"

Das kann man mit Sicherheit noch optimieren.

...

von c-hater (Gast)


Lesenswert?

Florian K. schrieb:

> Geht man von 1
> aus sind wir am unteren Ende der Skala und der Ausgang läuft mit
> 31,33kHz anstelle der 25kHz. Würde man die zwei nehmen wären es nur noch
> 20,1kHz. Wie treffe ich denn die Frequenz dazwischen dann?

Garnicht natürlich. Die Hardware kann nur Ganzzahlen für die 
Periodendauer. Und da der Zusammenhang zwischen Periodendauer und 
Frequenz halt 1/x ist, sollte jedem, dem sie nicht sehr heftig in's Hirn 
geschissen haben, klar sein, dass das mit der Frequenz unmöglich klappen 
kann...

> Wenn ich mit Vorteiler auf 62,5kHz komme und bei dem 16 Bit Zähler bis
> 65535 Zählen kann müsste man doch meinen das es eine Möglichkeit gibt
> das ganze auf ±1Hz genau zu verteilen.

Nein. Dir fehlen offensichtlich die elementarsten Grundlagen der 
Mathematik. Die nächstmögliche Lösung wurde im Thread bereits genannt: 
Das DDS-Prinzip.

Aber auch die kann natürlich letztlich nicht gegen die Mathematik 
anstinken. Bestenfalls kannst du damit kontinuierlich die 100% korrekte 
Frequenz für jede gewünschte Frequenschritt-Größe absondern. Das aber 
nur mit mehr oder weniger heftigen Phasenfehlern, die effektiv nichts 
anderes darstellen als eine zum gewünschtne Signal hinzugemischte 
Störfrequenz...

von Jakob (Gast)


Lesenswert?

Wenn man C hasst und wohl BASIC bevorzugt (?), muss man doch
bei Grundsatz-Fragen nicht gleich so aggressiv werden.

Besser wäre es, dem TO klarzumachen, dass man durch TEILEN,
wobei der Teiler eine GANZE ZAHL ist, nun mal nicht jede
Frequenz herleiten kann. Zumindest, wenn man beim ATmega8 auf
<= 20 MHz beschränkt ist.

Vielleicht genügt da ein Beispiel:

20 MHz : 400 = 50.000 Hz
20 MHz : 401 = 49.875 Hz

und wenn es ein symmetrisches Rechteck sein soll, (Output
toggle), wird es noch gröber:

(20 MHz : 200) / 2 = 50.000 Hz
(20 MHz : 201) / 2 = 49.751 Hz

Genauer geht es nicht, denn es kann keine Tabelle geben, die
eine Ganzzahl zwischen 200 und 201 liefert.

FERTIG.

von Hannes (Gast)


Lesenswert?

Jakob schrieb:
> Wenn man C hasst

Ich mag auch kein C, von Hass kann aber keine Rede sein.

> und wohl BASIC bevorzugt (?),

Es gibt auch noch Assembler. Das traue ich dem C-Hasser sogar zu, nach 
Allem, was ich so von ihm gelesen habe. Doof ist der nämlich nicht.

Übrigens kann man auch in Basic viele Probleme lösen. Man sollte aber 
nicht alle Funktionsangebote von Bascom nutzen, da viele davon 
blockierend arbeiten und somit kontraproduktiv wirken. So ist z.B. eine 
kleine Modellbahn-Digitalzentrale (DCC) mit ATMega8 in Bascom kein 
Problem. ;-)

> muss man doch
> bei Grundsatz-Fragen nicht gleich so aggressiv werden.

Da hast Du allerdings nicht ganz unrecht. Ich kann mich mit diesem Stil 
auch nicht so recht anfreunden. Bei diesem Tonfall verpufft meist die 
Wirkung einer ansonsten sachlich richtigen Aussage.

Trotzdem habe ich seinen Hinweisen (in anderen Threads) schon diesen und 
jenen wertvollen Tipp entnehmen können. Schräg angemacht fühlte ich mich 
dabei aber nicht, da es nicht meine Fragen sind, auf die er so reagiert. 
Ich stelle in diesem Forum hier schon sehr lange keine Fragen mehr.

>
> Besser wäre es, dem TO klarzumachen, dass man durch TEILEN,
> wobei der Teiler eine GANZE ZAHL ist, nun mal nicht jede
> Frequenz herleiten kann.

Genau deshalb empfahl ich ja eine Tabelle (Array) mit einer etwas 
groberen Auflösung und als Index die gerasterte Frequenz. Wenn man den 
Timer-Vorteiler so wählt, dass man mit Timer-Intervallen den unteren 
Bereich meidet, so bekommt man auch eine halbwegs lineare Skalierung 
hin. Exakt genau geht es aber nicht. Ich denke aber auch, dass das gar 
nicht nötig ist.

Wenn man den Timer-Vorteiler auch variabel macht, braucht man mit den 
Timerwerten (Intervalle, halbe Periodendauer für Pintoggeln) nur den 
Bereich von 1 zu 8 abdecken. Danach wird der Vorteiler verändert und das 
Spiel beginnt von vorn.

> Zumindest, wenn man beim ATmega8 auf
> <= 20 MHz beschränkt ist.

Bis 16 MHz, die 20 MHz gibt es erst bei den neueren AVRs. Und selbst da 
rudert Atmel ja inzwischen wieder zurück... Aber Controllertakt ist ja 
nicht Alles. Mit schlanker Programmierung erreicht man meist mehr.

...

von Florian K. (flo_ww)


Lesenswert?

Hallo Leute,

mir ist ja selbst schon klar geworden das es über diesen Weg nicht 
möglich ist. Der Taschenrechner (Ja, ein wenig Mathe kann ich schon) hat 
meine Ergebnisse auch immer Bestätigt. Die Hoffnung war das es sich über 
einen anderen Weg realisieren lässt. DDS halte für diese Anwendung für 
übertrieben. So 100% exakt muss das nicht laufen.
Das ganze läuft nun ohne Vorteiler. Da ich eh nur bis ~30kHz gehen 
möchte bin ich schon in einer Region wo sich die Frequenz halbwegs gut 
einstellen lässt. Gut genug für das was er tun soll.

Danke für Eure Antworten und auch für diese die vielleicht einen etwas 
härteren Ton bevorzugt :)

Grüße

Flo

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.