Forum: Mikrocontroller und Digitale Elektronik Timerprogrammierung AVR Butterfly


von Tom H. (dastoem)


Lesenswert?

Hallo Leute,
ich bin grade dabei den Timer1 vom ATMega169 zu programmieren. Doch 
irgendwie gelingt mir die Sache nicht so richtig.

Aber vielleicht fang ich erstmal von vorn an: Ziel meiner Arbeit ist es 
einen kleinen Funktionsgenerator zu programmieren, der an einem 
Ausgangspin eine Rechteckspannung mit variabler Frequenz (10Hz-20KHz) 
erzeugt.

Mein bisheriges Testprogramm:
1
void init_timer3 (unsigned int wert)
2
{
3
  timer_preload(wert); //Wert vorladen
4
  cli();
5
  TIMSK1 |= (1 << TOIE1); //Timer1 mit Preloading starten; Interrupt bei Überlauf
6
  TCCR1B = (0 << CS11)|(1 << CS10); //Prescaler auf 1 stellen
7
  sei();    //Interrupts erlauben
8
  
9
}
10
11
void timer_preload (unsigned int wert)
12
{
13
14
  TCNT1 = wert;
15
}
16
17
int main (void)
18
{
19
  DDRD |= (1<<PD0);  //Port D für Output konfigurieren
20
  PORTD=( 1 << PD0);
21
  init_timer3 (0xFFFE); //Timer initialisieren
22
  
23
  while (1) //main loop
24
  {
25
    
26
  }
27
  
28
  return 0;
29
  
30
}
31
32
ISR (SIG_OVERFLOW1)
33
{
34
  cli();
35
  PORTD ^= (1 << PD0); //Wert an PortD_Pin0 invertieren
36
  timer_preload(0xFFFE); //Timer1 vorladen
37
  sei();
38
}

Dazu benötige ich einen Timer, der in bestimmten zeitlichen Abständen 
mit Hilfe einer ISR meinen Ausgangspin toggelt. Ich hab gerade versucht, 
diesen Timer mit oben stehendem Programm zu realisieren. Doch irgendwie 
bekomme ich am Ausgang nur eine Frequenz von 10KHz. Nach meinem 
Dafürhalten, sollte diese doch wesentlich höher sein oder?
Da der Systemtakt ja 1MHz beträgt, sollte sich die Frequenz am Ausgang 
doch in diesem Bereich bewegen oder liege ich da falsch?

Hat jemand von euch Erfahrungen mit diesem uC und könnte mir helfen?

Danke schonmal,
Tom

von Karl H. (kbuchegg)


Lesenswert?

Tom Heinrich wrote:

> Da der Systemtakt ja 1MHz beträgt, sollte sich die Frequenz am Ausgang
> doch in diesem Bereich bewegen oder liege ich da falsch?

Deine ISR verbraucht ja auch Zeit.
Bei einem preload Wert von 0xFFFE bewegt sich der µC praktisch nur
noch in der ISR. Sobald eine ISR fertig ist, ist der Timer schon
wieder im Überlauf und die ISR startet sofort wieder.
Aber: Wie gesagt vom Auslösen des Interrupt bis dann endlich der
Pin getoggelt wird, vergeht ja auch Zeit, da ja einige Takte
dafür benötigt werden.

Allerdings: 10kHz kommt mir schon etwas wenig vor.
Hast du den Optimizer eingeschaltet?

Was im Programm auffällt:
das cli() und sei() gibst du mal ganz schnell wieder aus der ISR
heraus. Das passiert erstens sowieso automatisch und ist zweitens
kontraproduktiv. Die Interrupts sollen erst dann wieder freigegeben
werden, wenn die ISR tatsächlich zu Ende ist. Also erst mit dem
abschliessenden Return. Und darum kümmert sich der Compiler.

ISR (SIG_OVERFLOW1)

Der Name SIG_OVERFLOW1 ist der alte Name, der mit SIGNAL benutzt
wurde. Die für ISR vorgesehenen Namen enden alle auf *_vect

Und zu guter letzt. Sowas macht man nicht mit Overflow und Timer-
reload. Dafür gibt es den CTC Modus des Timers.
Vorteil: Das ist erstens taktgenau und man muss sich nicht um den
Reload kümmern

von Tom H. (dastoem)


Lesenswert?

Erstmal danke für deine Hilfe.

Mit Compare hatte ich schon herumexperimentiert. Leider mit mäßigem 
Erfolg. Welche Register müssen gesetzt werden, um zuverlässig im 
CTC-Modus zu arbeiten?

Den Preload-Wert hatte ich auf 0XFFFE gesetzt, um herauszufinden mit 
welcher maximalen Frequenz ich den Pin am Ausgang toggeln kann. 
Jedenfalls kommen mir Frequenzen im kHz-Bereich sehr sehr spanisch vor. 
Von welchem Optimizer sprichst du?

Kannst jemand die Taktfrequenz des ATMega169 auf dem Butterfly mit 1MHz 
bestätigen oder hab ich mich da vertan?

von Karl H. (kbuchegg)


Lesenswert?

Tom Heinrich wrote:
> Erstmal danke für deine Hilfe.
>
> Mit Compare hatte ich schon herumexperimentiert. Leider mit mäßigem
> Erfolg. Welche Register müssen gesetzt werden, um zuverlässig im
> CTC-Modus zu arbeiten?

Steht alles im Datenblatt.

>
> Den Preload-Wert hatte ich auf 0XFFFE gesetzt, um herauszufinden mit
> welcher maximalen Frequenz ich den Pin am Ausgang toggeln kann.
> Jedenfalls kommen mir Frequenzen im kHz-Bereich sehr sehr spanisch vor.
> Von welchem Optimizer sprichst du?

Vom Optimizer des Compilers.
Ist der nicht aktiv ist es nicht ungewöhnlich, dass das Ergebnis
des Compilers doppelt so gross ist wie mit Optimizer.

>
> Kannst jemand die Taktfrequenz des ATMega169 auf dem Butterfly mit 1MHz
> bestätigen oder hab ich mich da vertan?

Mach dir ein Testprogramm und sieh nach (da ich den Butterfly
nicht kenne, gehe ich mal davon aus, dass am Port B ein paar
LED sein werden. Falls die woanders sind, musst du anpassen)

1
#define F_CPU 1000000
2
3
#include <avr/io.h>
4
#include <util/delay.h>
5
6
void Delay()
7
{
8
  unsigned char i;
9
10
  for( i = 0; i < 100; ++i )
11
    _delay_ms( 10 );
12
}
13
14
int main()
15
{
16
  DDRB = 0xFF;
17
 
18
  while( 1 ) {
19
20
    PORTB = 0xFF;
21
    Delay();
22
23
    PORTB = 0x00;
24
    Delay();
25
  }
26
}

Wichtig: Hier muss der Optimizer des Compilers eingeschaltet
sein (Option -Os), ansonsten stimmen die Zeiten im _delay_ms
nicht.

Und dann die Frage: Wie lange ist die LED ein bzw. aus.
Wenn das in etwa eine Sekunde ist, dann läuft dein µC auch
tatsächlich mit der unter F_CPU angegebenen Frequenz von
1000000Hz = 1Mhz

von Tom H. (dastoem)


Lesenswert?

Also, ich hab das jetzt mit Hilfe eines Compare-Timers realisiert. Damit 
kriege ich Frequenzen bis 20kHz, mehr ist leider nicht drin. Die 
Taktfrequenz des ATMega169 ist offenbar wirklich 1MHz, da bei 
Prescaler=1 der übergebene Zahlenwert 1:1 übernommen wird und mit einem 
Oszi problemlos gemessen werden kann.

Das -Os Flag des GCC war übrigens die ganze Zeit gesetzt. Mein 
Testprogramm sieht somit wiefolgt aus:
1
void init_timer3 (unsigned int wert)
2
{
3
  TCCR1A = (1<< COM1A0)|(1 << FOC1A);
4
  TIMSK1 |= (1 << OCIE1A); //Timer1 mit CompareA starten; Interrupt bei Gleicheit
5
  TCCR1B = (1 << WGM12)|(0 << CS11)|(1 << CS10); //Prescaler auf 1 stellen
6
  setcompare (wert);
7
    
8
}
9
10
void setcompare(unsigned int wert)
11
{
12
  OCR1A = wert;
13
}
14
15
int main (void)
16
{
17
  DDRD |= (1<<PD0);  //Port D für Output konfigurieren
18
  PORTD = ( 1 << PD0);
19
  
20
  init_timer3 (50); //Zeit in us an Timer übergeben
21
  sei();
22
  while (1) //main loop
23
  {
24
  
25
  }
26
  
27
  return 0;
28
  
29
}
30
31
ISR (TIMER1_COMPA_vect)
32
{
33
  PORTD ^= (1 << PD0); //Wert an PortD_Pin0 invertieren
34
}

Vielen Dank nochmal für deine Hilfe zum Timer und für die Anregungen zur 
korrekten Syntax :)

von Martin T. (mthomas) (Moderator) Benutzerseite


Lesenswert?

Ja, üblicherweise "läuft" der ATmega169V auf dem BF mit 1MHz, Grund 
dafür ist aber die Softwareeinstellung des Prescalers für den internen 
R/C-Oszillator (8MHz) in Register CLKPR. Dieser wird vom 
vorinstallierten Bootloader bzw. der Initialisierungsroutine des 
Beispielcodes auf 8 gesetzt wird (8MHz/8=1MHz). ATmega169V ist bis 8MHz 
ab 2,7V Betriebspannung (4MHz ab 1,8V) spezifiziert. Falls es etwas 
schneller werden soll: Prescaler verkleinern. Kann man per Software 
machen, Zugriff aus Fuses ist dafür nicht notwendig. Erhöht allerdings 
auch den Strombedarf. Noch schneller: CTC-Modus und "pinwackeln" per 
Hardware (nicht in einer ISR per Software), vgl. für Timer 1 
Beschreibung Register TCCR1A. Damit sind Frequenzen bis F_CPU/2 möglich 
(also bis zu 8MHz/2=4MHz), allerdings nur an bestimmten Pins. Z.B. sind 
die OC-Pins von Timer1 PB5 und PB6 auf dem BF durch Piezo-"Pieper" bzw. 
Joystick belegt. Nehme an, der Joystick soll zur Frequenzeinstellung 
ohne Einschränkung in Betrieb bleiben, bleibt PB5. Falls das "mitpiepen" 
stört, kann man den Pieper durch auslöten eines klitzekleinen 
Widerstands (R202) relativ problemlos stilllegen. Langer Rede: es ist 
schon "mehr drin".

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.