Forum: Compiler & IDEs suche beispielcode für 16-Bit Timer (Mega 8)


von kranker Jan (Gast)


Lesenswert?

Moinmoin,

Ich bin auf der suche nach einem möglichst vollständigen Beispielcode 
für einen 16-Bit-Timer im AtMega8.

Also konkret in main() den Timer vorbereiten und dann wie die 
Interruptroutine aussieht. Wie hält man Timer in der Interruptroutine 
wieder an, Wie weist man neuen Zählerwert zu und wie lässt man den dann 
weiterlaufen. Ich möchte zuerst nur eine LED in definierten Abständen 
blinken lassen oder einen Ton an einem Digitalport ausgeben, um die 
Verwendung erstmal besser zu verinnerlichen.


Jan

von irgendwer (Gast)


Lesenswert?

Guck ins Datenblatt!

von noch wer (Gast)


Lesenswert?

oder ins Tutorium!

von kranker Jan (Gast)


Lesenswert?

Im Tutorial steht aber leider kein brauchbarer Beispielcode :(

Jan

von Karl heinz Buchegger (Gast)


Lesenswert?

Da hat er recht :-)

Ist aber trivial. Wenn du den Rest verstanden hast, dann
sollte das kein Problem sein.


Aber was solls.
1
#define F_CPU 8000000UL
2
3
#include <stdio.h>
4
#include <avr\io.h>
5
#include <avr\interrupt.h>
6
7
ISR( TIMER0_OVF_vect )
8
{
9
  PORTB ^= 0xFF;
10
}
11
12
int main()
13
{
14
  DDRB = 0xFF;
15
  PORTB = 0x00;
16
17
  TCCR0 = ( 1 << CS01 ) | ( 1 << CS00 );
18
  TIMSK = ( 1 << TOIE0 ); 
19
  sei();
20
21
  while( 1 ) {
22
  }
23
}

Das mag jetzt vielleicht bei dir etwas schnell blinken.
Da musst du dann halt zusätzlich im Interrupt
noch etwas runterteilen.
1
#define F_CPU 8000000UL
2
3
#include <stdio.h>
4
#include <avr\io.h>
5
#include <avr\interrupt.h>
6
7
int Teiler;
8
9
ISR( TIMER0_OVF_vect )
10
{
11
  Teiler++;
12
  if( Teiler == 100 ) {
13
    Teiler = 0;
14
    PORTB ^= 0xFF;
15
  }
16
}
17
18
int main()
19
{
20
  DDRB = 0xFF;
21
  PORTB = 0x00;
22
23
  Teiler = 0;
24
25
  TCCR0 = ( 1 << CS01 ) | ( 1 << CS00 );
26
  TIMSK = ( 1 << TOIE0 ); 
27
  sei();
28
29
  while( 1 ) {
30
  }
31
}

  

von Luhar (Gast)


Lesenswert?

  Teiler++;
  if( Teiler >= 100 ) {

Das wäre dann perfekt :-)

von kranker Jan (Gast)


Lesenswert?

Hallo Karl Heinz,

Vielen Dank für den Code!

1. Leider ist der Code zum Realisieren von einer Melodie an einem Pieper 
über einen Digital-out noch nicht ganz vollständig, deshalb habe ich 
noch eine kleine Nachfrage.

2. Ist der von dir genannte Code nicht ein Beispielcode für den 8-Bit 
Counter oder sehe ich das falsch?

also erstmal zur Nachfrage:
Wie halte ich innerhalb der Serviceroutine den 16-Bit Counter an, weise 
einen neuen Zählerstand zu und lasse den dann weiterlaufen.

Des weiteren muss ich innerhalb der ISR (Interrupt Service Routine) 
zusätzlich die Interrupts deaktivieren, damit kein (wie nennt sich das 
nochmal) overflow stattfindet. (Also kein neuer Interrupt obwohl der 
alte noch nicht abgearbeitet ist.)

von 1001. Rahul (Gast)


Lesenswert?

>Des weiteren muss ich innerhalb der ISR (Interrupt Service Routine)
>zusätzlich die Interrupts deaktivieren, damit kein (wie nennt sich das
>nochmal) overflow stattfindet. (Also kein neuer Interrupt obwohl der
>alte noch nicht abgearbeitet ist.)

Nein, das macht der AVR für dich.
Dafür gibt es keine Prioritäten zwischen den Interrupts, die dafür 
sorgen könnten, dass ein Interrupt mit höherer Priorität einen mit 
niedriegerer unterbrechen kann (vgl. 8051).

von kranker Jan (Gast)


Lesenswert?

@ 1001. Rahul

Achso. Naja aber trotzdem muss man wissen, wie man in den 16-Bit-Timer 
innerhalb der 16-Bit-Timer-Interruptroutine eine neue Zählhöhe 
reinschreibt, um wie schon erwähnt z.B. eine Melodie an einem Digitalen 
Portpin auszugeben. Da liegt momentan mein Problem.

Außerdem halte ich es für Sinnvoll, dass dieser Beispielcode, wenn er 
fertig ist und eine Melodie (z.B. Alle meine Entchen) ausgeben kann auch 
in das Tutorial hier reinkommt weil das einfach Basics sind, die da 
stumpf fehlen, obwohl das Tutorial immer so hoch gelobt wird.

von SiO2 (Gast)


Lesenswert?

>Achso. Naja aber trotzdem muss man wissen, wie man in den 16-Bit-Timer
>innerhalb der 16-Bit-Timer-Interruptroutine eine neue Zählhöhe
>reinschreibt,

So wie du es auch ausserhalb des int machst, vielleicht?

von 1001. Rahul (Gast)


Lesenswert?

>Naja aber trotzdem muss man wissen

TCNT = neuer Wert; // und fertig.

Für sowas sollte man aber eher einen PWM-Mode benutzen...

von kranker Jan (Gast)


Lesenswert?

Wie sieht das mit den interrupts denn nun eigentlich aus...

Wenn sich dir CPU beispielsweise innerhalt von interrupt vom timer 
befindet, was passiert dann wenn ein Interrupt von einem Digitaleingang 
kommt? Garnicht? Wird der Interrupt komplett ignoriert? Oder wird er in 
eine "warteschleife" gesetzt? Ich habe nur erfahrungen mit einem 8051 
und da ist das offensichtlich komplett anders.

von Rahul, der Trollige (Gast)


Lesenswert?

>da ist das offensichtlich komplett anders.
Da liegst du richtig.

Hier auf der Seite (oben links bei den Links) gibt es den Abschnitt 
"AVR", dort gibt es Tutorien, die sich mit der Einführung zum AVR 
befassen.
Das sollte eine Anlaufstelle für die sein, genauso wie der erste Teil 
des Datenblattes deines Controller.

von kranker Jan (Gast)


Lesenswert?

Das Tutorial ist mir bekannt. Aber ein Beispiel für die benutzung des 
16-Bit Timers gibt es da trotzdem nicht. 8-Bit ist kein Problem. Das 
steht da, aber 16-Bit ebend nicht und da der 16-Bit Timer nen ganzes 
Stück komplizierter ist, ist das Tutorial so wie es jetzt ist für den 
16-Bit Counter völlig unbrauchbar, weil das was bisher zum 16-Bit Timer 
im Tutorial steht deutlich ausführlicher im Datenblatt steht. Aber auch 
das was im Datenblatt steht ist noch kein brauchbares Beispiel. Was 
daher in so ein Tutorial sollte, damit es sich vom Datenblatt 
unterscheidet, ist ein vernünftiges Beispiel für den 16-Bit counter. Und 
genau das versuche ich momentan (leider bisher mit wenig Erfolg) zu 
schreiben.

von Rahul, der Trollige (Gast)


Lesenswert?

>da der 16-Bit Timer nen ganzes Stück komplizierter ist

Wo das denn?

>Was daher in so ein Tutorial sollte, damit es sich vom Datenblatt
>unterscheidet, ist ein vernünftiges Beispiel für den 16-Bit counter.

Dann weisst du ja, was du demnächst machen wirst...

>Und genau das versuche ich momentan (leider bisher mit wenig Erfolg) zu
>schreiben.

Es gibt also schon Code.
Sowas postet man hier und lässt es dann von "uns" und anderen Experten 
zerpflücken bis man keine Lust mehr auf controllern oder dieses Forum 
hat ;-)

von kranker Jan (Gast)


Lesenswert?

@Rahul

Okay. Dann sei hiermit mein bisheriger Code zur Diskussion freigegeben 
;)

#define F_CPU 8000000UL

#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>

//#include <util/delay.h>

// ab avr-libc Version 1.2.0 möglich und empfohlen:
#include <stdint.h>
 // veraltet: #include <inttypes.h>


ISR( TIMER1_OVF_vect )
{
  PORTD ^= 0xFF;
}


int main (void) {

  //Portkonfiguration

  DDRD  = 0b11111111; // Datenrichtung des Ports Bit im Register gesetzt
          //76543210  // (1) für Ausgang, Bit gelöscht
            // (0) für Eingang. Als Ausgang 0=GND 1=VCC

  PORTD = 0b11100000; // Ausgang der letzten 5 Bits wird auf Masse 
gezogen (LED's eingeschaltet)

  //16 Bit Timer
  // Die Bits: COM1a1 COM1a0 NA NA NA NA PWM11 PWM10
  TCCR1A = 0b00000000;
  //PWM11  und PWM10  = 0 => PWM-Betriebsart nicht aktiviert - Timer1 
arbeitet als normaler Timer/Zähler
  //COM1a1 und COM1a0 = 0 => OC1 wird nicht angesteuert




  while(1) {
    //Hier in Sleepmodus setzen so dass Interrupts aufwecken
  }

   /* wird nie erreicht */
  return 0;
}

von johnny.m (Gast)


Lesenswert?

Die AVRs besitzen einen CTC-Modus, bei dem das Nachladen des Timers mit 
einem Startwert entfällt. Der ist für solche Sachen wesentlich besser 
geeignet, als der Overflow...

von Rahul, der Trollige (Gast)


Lesenswert?

Und was ist mit TIMSK?
Ein SEI sehe ich auch nirgends...

von Rahul, der Trollige (Gast)


Lesenswert?

Und den Timer startest du auch nirgends...
Guck dir am besten mal die Registerbeschreibungen des Timer1 im 
Datenblatt an.

von Rahul, der Trollige (Gast)


Lesenswert?

TCCR1B...

von Rahul, der Trollige (Gast)


Lesenswert?

Übrigens verhält sich der 16-Bit-Timer bei der Initialisierung (und dem 
Überlauf-Betrieb) meistens genauso wie ein 8-Bit-Timer...

von Rahul, der Trollige (Gast)


Lesenswert?

>kranker Jan

Wenn es eine Grippe ist, solltest du die vielleicht lieber erst mal 
loswerden...

von kranker Jan (Gast)


Lesenswert?

Was ist SEI ?

Aktuell siehts jetzt so aus:

ISR( TIMER1_OVF_vect )
{
  PORTD ^= 0xFF;
  //PORTD = ~PORTD;
}


int main (void) {

  //Portkonfiguration

  DDRD  = 0b11111111; // Datenrichtung des Ports Bit im Register gesetzt
          //76543210  // (1) für Ausgang, Bit gelöscht
            // (0) für Eingang. Als Ausgang 0=GND 1=VCC

  PORTD = 0b11100000; // Ausgang der letzten 5 Bits wird auf Masse 
gezogen (LED's eingeschaltet)


  //16 Bit Timer
  // Die Bits: COM1a1 COM1a0 NA NA NA NA PWM11 PWM10
  TCCR1A = 0b00000000;
  //PWM11  und PWM10  = 0 => PWM-Betriebsart nicht aktiviert - Timer1 
arbeitet als normaler Timer/Zähler
  //COM1a1 und COM1a0 = 0 => OC1 wird nicht angesteuert

  // Die Bits: ICNC1 ICES1 NA NA CTC1 CS12 CS11 CS10
  // CTC1 CS12 CS11
  // 0    0    0     angehalten
  // 0    0    1     CPU-Takt
  // 0    1    0     1/8
  // 0    1    1     1/64
  // 1    0    0     1/256
  // 1    0    1     1/1024
  TCCR1B = 0b00000011;


  // Die Bits: TOIE1 OCIE1A NA NA TICIE NA TOIE0 NA
  TIMSK = 0b10000000; //TOIE1 = 1 => Timer Overflow 1 Interrupt wird 
ausgelöst


  while(1) {
    //Hier in Sleepmodus setzen so dass Interrupts aufwecken
  }

   /* wird nie erreicht */
  return 0;
}


BTW: Wie postet man Quelltext so schön formatiert wie Heinz das gemacht 
hat?

von johnny.m (Gast)


Lesenswert?

Das heißt "sei()" und nicht "SEI". Bitte bitte bitte lies das 
Tutorial!!! Da steht wirklich alles drin!

von johnny.m (Gast)


Lesenswert?

> Wie postet man Quelltext so schön formatiert wie Heinz das gemacht
> hat?
OK, das steht nicht im Tutorial. Das geht mit "eckige Klammer auf C 
eckige Klammer zu" und "eckige Klammer auf /C eckige Klammer zu"...

von Peter D. (peda)


Lesenswert?


von kranker Jan (Gast)


Lesenswert?

Test
<c>
#define F_CPU 8000000UL

#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>

//#include <util/delay.h>

// ab avr-libc Version 1.2.0 möglich und empfohlen:
#include <stdint.h>
 // veraltet: #include <inttypes.h>


ISR( TIMER1_OVF_vect )
{
  //PORTD ^= 0xFF;
  PORTD = ~PORTD;
}


int main (void) {

  //Portkonfiguration

  DDRD  = 0b11111111; // Datenrichtung des Ports Bit im Register gesetzt
          //76543210  // (1) für Ausgang, Bit gelöscht
            // (0) für Eingang. Als Ausgang 0=GND 1=VCC

  PORTD = 0b11100000; // Ausgang der letzten 5 Bits wird auf Masse 
gezogen (LED's eingeschaltet)


  //16 Bit Timer
  // Die Bits: COM1a1 COM1a0 NA NA NA NA PWM11 PWM10
  TCCR1A = 0b00000000;
  //PWM11  und PWM10  = 0 => PWM-Betriebsart nicht aktiviert - Timer1 
arbeitet als normaler Timer/Zähler
  //COM1a1 und COM1a0 = 0 => OC1 wird nicht angesteuert

  // Die Bits: ICNC1 ICES1 NA NA CTC1 CS12 CS11 CS10
  // CTC1 CS12 CS11
  // 0    0    0     angehalten
  // 0    0    1     CPU-Takt
  // 0    1    0     1/8
  // 0    1    1     1/64
  // 1    0    0     1/256
  // 1    0    1     1/1024
  TCCR1B = 0b00000011;


  // Die Bits: TOIE1 OCIE1A NA NA TICIE NA TOIE0 NA
  TIMSK = 0b10000000; //TOIE1 = 1 => Timer Overflow 1 Interrupt wird 
ausgelöst

  sei();

  while(1) {
    //Hier in Sleepmodus setzen so dass Interrupts aufwecken
  }

   /* wird nie erreicht */
  return 0;
}
</c>

von kranker Jan (Gast)


Lesenswert?

1
#define F_CPU 8000000UL
2
3
#include <stdlib.h>
4
#include <avr/io.h>
5
#include <avr/interrupt.h>
6
7
//#include <util/delay.h>
8
9
// ab avr-libc Version 1.2.0 möglich und empfohlen:
10
#include <stdint.h>
11
 // veraltet: #include <inttypes.h> 
12
13
14
ISR( TIMER1_OVF_vect )
15
{
16
  //PORTD ^= 0xFF;
17
  PORTD = ~PORTD;
18
}
19
20
21
int main (void) {            
22
23
  //Portkonfiguration
24
           
25
  DDRD  = 0b11111111; // Datenrichtung des Ports Bit im Register gesetzt 
26
          //76543210  // (1) für Ausgang, Bit gelöscht 
27
            // (0) für Eingang. Als Ausgang 0=GND 1=VCC
28
29
  PORTD = 0b11100000; // Ausgang der letzten 5 Bits wird auf Masse gezogen (LED's eingeschaltet)
30
31
32
  //16 Bit Timer
33
  // Die Bits: COM1a1 COM1a0 NA NA NA NA PWM11 PWM10
34
  TCCR1A = 0b00000000;
35
  //PWM11  und PWM10  = 0 => PWM-Betriebsart nicht aktiviert - Timer1 arbeitet als normaler Timer/Zähler
36
  //COM1a1 und COM1a0 = 0 => OC1 wird nicht angesteuert
37
38
  // Die Bits: ICNC1 ICES1 NA NA CTC1 CS12 CS11 CS10
39
  // CTC1 CS12 CS11
40
  // 0    0    0     angehalten
41
  // 0    0    1     CPU-Takt
42
  // 0    1    0     1/8
43
  // 0    1    1     1/64
44
  // 1    0    0     1/256
45
  // 1    0    1     1/1024
46
  TCCR1B = 0b00000011;
47
48
49
  // Die Bits: TOIE1 OCIE1A NA NA TICIE NA TOIE0 NA
50
  TIMSK = 0b10000000; //TOIE1 = 1 => Timer Overflow 1 Interrupt wird ausgelöst
51
52
  sei();
53
54
  while(1) {
55
    //Hier in Sleepmodus setzen so dass Interrupts aufwecken        
56
  }            
57
 
58
   /* wird nie erreicht */
59
  return 0;                
60
}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Warum ziehst du dann nicht das unnütze Test-Posting zurück?  Du darfst
das nämlich (für deine Postings).  Außerdem hättest du es nach
Feststellung des Nicht-Funktionierens noch passend editieren können,
statt noch eins hinterher zu werfen.

von kranker Jan (Gast)


Lesenswert?

Ich weiß nicht wie man Posts löschen kann.

von Philipp B. (philipp_burch)


Lesenswert?

Editieren und löschen geht nur nach Anmeldung.

von Karl heinz Buchegger (Gast)


Lesenswert?

Du willst also einen Melodiegenerator bauen. Sag das doch gleich.

Also: Für sowas würde ich 2 Timer benutzen.

Timer1, der 16 Bit Timer, wird im CTC Modus betrieben.
Seine Aufgabe ist es das Tonsignal zu erzeugen, indem ein
Pin getogeglt wird.
CTC: Clear Timer on Compare. Schau dir das unbedingt im
Datenblatt an! Das Prinzip ist Folgendes: Ein Timer zählt
ja durch bis er seinen Endstand erreicht hat. Dann kann er
einen Interrupt auslösen und fängt wieder bei 0 an. Beim
normalen Timerbetrieb ist der Endstand vorgegeben und er
Interrupt ist der 'Overflow-Interrupt'. Im CTC Modus kannst
du aber den gewünschten Endstand vorgeben und der Interrupt
ist auch ein anderer. Steht aber alles im Datenblatt.
Der Rest ist ein bioschen rumrechnerei: Welches muss der
Timer Endstand sein, damit sich am Output Pin die richtige
Frequenz ergibt.

Soweit zum ersten Timer.
Der zweite Timer ist dafür zuständig, dass die Endstände
für den Timer1 in der richgtigen Reihenfolge gesetzt werden.
Der läuft also durch und sorgt mit seinen Overflows für
das Basistiming aus dem dann letztendendes die Länge der
Achtel-, Viertel-, Halbe-, ... Noten und Pausen erzeugt
werden. Im Prinizp kannst du da die Vorlage von oben nehmen
und dich im Overflow Interrupt austoben. Einfach mitzählen
wieviele Overflows das jetzt waren. Die abzuspielende Note
gibt durch ihre Längenangabe vor, wieviele das sein müssen.
Bei erreichen dieser Anzahl, wird die nächste NOte geholt,
der Endstand für den Timer1 neu gesetzt und in einer globalen
Variablen vermerkt wieviele Overflows der Timer0 wieder abwarten
muss, bis die nächste Note drankommt.

Aber erst mal klein anfangen. Ich würde mal damit anfangen,
den Timer1 im CTC Modus zu betreiben und einen Ton hörbar
zu machen. Die Tonhöhe kann durchaus im Programm zunächst mal
ein fixer Wert wein, der inn das Compare-Register des Timer1
geschrieben wird. Alleine mit dem umprogrammieren dieses
Compare-Wertes kann man schon eine Menge Spass haben:
zb. In einer Schleife den Wert erhöhen und eine kurze Zeit
warten; oder andersrum; oder nach einer Wartezeit von vielleicht
500ms Zufallszahlen reinschreiben, oder ...

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.