Forum: Mikrocontroller und Digitale Elektronik AVR Atmega16 Timer Problem


von Marco M. (marco1987)


Angehängte Dateien:

Lesenswert?

Hallo ihr,

erstmal kurz vorab ich habe sehr wenig Kenntnisse in µC Programmierung 
und das nach LED an aus schalten meine ersten Versuche, also 
entschuldigt meine vllt einfachen Fragen

ich habe ein Problem mit dem 16 bit Timer. Ich sitze schon mehrere Tage 
an diesem Problem. Also mein Ziel:

Ich will einen Takt erzeugen. (siehe Bild)

Könnt ihr mir einen Tipp geben wie ich da am besten rangehe. Soweit ich 
das mitbekommen habe gehen solche niedrigen Frequenze nicht mit dem 8 
Bit Timer sonder nur mit dem 16 bit Timer.

Mein Ansatz nun:
Atmel Timer Calculator Prescaler , TCNT1H und L berchnen. Dies habe ich 
dann auch in meinen Quellcode eingetragen leider kam immer die flasche 
Frequenz raus bzw gar nichts.

Noch kurz als Info ich Programmiere in C mit dem STK500 und mein µC ist 
ein Atemega16.

Quellcode:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <timer_set.h>
#include <avr/delay.h>



SIGNAL (SIG_OVERFLOW1){

  TCNT1L=0x24;   // für 4 sec. Periodendauer
     TCNT1H=0xF4;

}


int main (void) {
   DDRC = 0xff;
   DDRD = 0xff;


PORTD |= (1<<PD0) | (1<<PD1) | (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) 
| (1<<PD6) | (1<<PD7)  ;

  TCCR1A= (1<<COM1A1) | (0<<COM1A0) |  (0<<WGM11)  | (0<<WGM10);// pwm 
aus durch wgm 0


   TCCR1B=(1<<CS10) | (1<<CS12);  // Prescaler 1024


   for (;;) {
}


ist dieser Ansatz völlig falsch?

Wie kann ich die beiden geforderten Signale generieren?


Danke

von Johannes M. (johnny-m)


Lesenswert?

SIGNAL ist lange veraltet. signal.h rauswerfen und ISR verwenden.

Was aber echt fatal ist:
1
  
2
  TCNT1L=0x24;   // für 4 sec. Periodendauer
3
  TCNT1H=0xF4;
Das High-Byte muss bei solchen 16-Bit-Registern immer zuerst 
geschrieben werden. Schau mal im Datenblatt nach, da ist bei dem 
16-Bit-Timer ein extra Abschnitt zum Thema "Accessing 16-bit registers". 
Allerdings programmierst Du in C und kannst den Krempel mit der 
Zugriffsreihenfolge ganz komfortabel dem Compiler überlassen. Der kennt 
nämlich ein 16-Bit-(Pseudo-)Register TCNT1 (ohne L und H), und der macht 
daraus automatisch die richtige Zugriffssequenz. Also
1
TCNT1 = 0xF424;
tut's auch. Oder noch einfacher
1
TCNT1 = 62500;

Und wenn Du einen Interrupt benutzen willst, dann musst Du den auch 
freigeben. In Deinem Code ist aber weder eine lokale Freigabe über das 
entsprechende Enable-Bit noch die globale Freigabe zu sehen.

Schau Dir bitte im AVR-GCC-Tutorial an, wie es geht. Da steht 
eigentlich alles drin. Es macht so wenig Sinn, in Deinem obigen Code 
noch nach irgendwelchen Fehlern zu suchen.

Vor Allem solltest Du Dich über die unterschiedlichen Betriebsarten der 
Timer informieren (v.a. CTC und PWM). Die sind für Dein Vorhaben nämlich 
wesentlich besser geeignet als ein Timer-Reload, der beim AVR eigentlich 
generell keinen großen Sinn macht.

Mit welcher Taktfrequenz läuft Dein Controller überhaupt? 62500 als 
Reload-Wert für 4 Sekunden lassen auf eine Taktfrequenz von unter 1 MHz 
schließen...

...und da Du das TCNT1 am Anfang nicht initialisierst, läuft der Timer 
erst mal von Null an bis zum ersten Overflow. Und das dauert bei einem 
Prescaler von 1024 sehr lange (bei 1 MHz mehr als eine Minute)...

von Marco M. (marco1987)


Lesenswert?

Vielen Dank für die schnelle Antwort.

Also mein Atmega16 müsste nach meinem Wissenstand mit 16 MHZ laufen.
Ich habe da wie gesagt so ein Tool womit man das ausrechnet, wie TcCNTL 
und H aussehen müssen. Das mit dem von = anlaufen ist mir auch schon 
aufgefallen das am Anfang das sehr lange dauert.

Also sagst du PWM. Noch eine schwierigkeit mehr:-)

Naja danke erstmal. vllt findet sich ja noch jmd der mir weiterhelfen 
kann.

von Karl H. (kbuchegg)


Lesenswert?

Marco Müeller wrote:
> vllt findet sich ja noch jmd der mir weiterhelfen
> kann.

Mein Tip.
Leg dieses Berechnungstool erst mal zur Seite und bemüh dich zu
verstehen, wie ein Timer arbeitet und wie der Zusammenhang zum
Prescaler ist. Das ist im Grunde Watscheneinfach.
Und erst dann, wenn du die Prescaler bzw. Comparewerte mit der
hand ausrechnen kannst (und das auch am 'lebenden' Objekt überprüft
hast), dann benutze wieder das Tool um dir die fade Rechnerei zu
ersparen.

Auch das beste Tool hilft nichts, wenn man nicht versteht was da
eigentlich abgeht.

von Joan P. (joan)


Lesenswert?

Marco Müeller wrote:
> Ich habe da wie gesagt so ein Tool womit man das ausrechnet, wie TcCNTL
> und H aussehen müssen.

Das kannst Du dir wie schon geschrieben sparen.. das macht der 
C-Compiler AUTOMATISCH.

TCNT1 = x; reicht völlig aus und lenkt dich nicht vom wesentlichen ab, 
wie zB:

sei(); // Interrupts global ein

oder die Einstellung der fCPU-Register..

Lies Dir wirklich erstmal das Tutorial durch, da klären sich 99% der 
Fragen die Du grad hast von selber und ganz andere treten an deren 
Stelle ;-)

von Marco M. (marco1987)


Lesenswert?

So also das habe ich bisher auf die Beine gestellt:

Ist das mit den Interrupts ausschalten richtig?
wann muss ich die Interrupts mit sei() zulassen?
so wie ich jetzt mein Programm verstanden habe zählt der der zähler von 
0 bis 65500 und dann wieder zurück,
jedesmal wenn er den wert im vergleichsregister OCR1A erreicht lösst er 
einen Interrrupt aus.

Und mit TCNT1 lade ich den Zähler dann vor, damit er dann nicht wieder 
von 0 anfängt oder wie?

kann mir jmd nochmal die Formel hier rein schreiben, mit der ich mir die 
frequenz mit prescaler usw ausrechnen kann?

Und im Datenblatt habe ich was von wegen BOTTOM und TOP gelesen für hoch 
und runter zählen wofür brauch man das?


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

#include <avr/delay.h>


SIGNAL (SIG_OVERFLOW1){


cli();  //interruptsausschalten
  TCNT1 = 0x1856;   //eigentlich tcntl und h aber das weiss compiler


}


int main (void) {
   DDRC = 0xff;
   DDRD = 0xff;

TCCR1A= (1<<COM1A1) | (0<<COM1A0)  | (1<<WGM11)  | (1<<WGM10);// pwmaus 
durch wgm 0
//  Hochzählen OC1 auf 0 runter auf 1| 10-Bit PWM Betriebsart 
aktivieren.

TCCR1B=(1<<CS10) | (1<<CS12);  // Prescaler 1024

//TCCR1B=(1<<ICES1) ; //input compare steigende flanke auswertung

//TCCR1B=(1<<CTC1); // Clear Timer/Counter on Compare Match 
Timer/Counter 1


OCR1A=6500;  //compare register;
   for (;;) {
}
}

von Johannes M. (johnny-m)


Lesenswert?

Noch mal: SIGNAL ist veraltet. Mach es so, wie es im 
AVR-GCC-Tutorial steht. Die delay.h steht auch nicht mehr im Ordner 
"avr", sondern unter "util". Also
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
5
//und dann:
6
ISR(TIMER1_OVF_vect)
7
{
8
    //Code
9
}

Aber der Rest macht auch keinen Sinn! Bitte bitte bitte lies Dir das im 
Tutorial genau durch! Das ICES1-Bit hat mit Deiner Anwendung überhaupt 
nichts zu tun. Und Du lädst im Interrupt Handler immer noch den Timer 
nach, was völliger Unsinn ist. Außerdem gibst Du nach wie vor den 
Interrupt nicht frei, weder lokal noch global! Und ich frage mich, was 
Du für ein Datenblatt hast. CTC1 ist ein veralteter Bitname, und das 
steht in den aktuellen Datenblättern auch drin. Das Sperren der 
Interrupts im Interrupt Handler ist ebenfalls überflüssig. Das passiert 
automatisch durch die Controller-Hardware.

Deine Ungeduld bringt gar nichts. Setz Dich in aller Ruhe hin und lies 
Dir die entsprechenden Passagen in der Literatur durch.

von Marco M. (marco1987)


Lesenswert?

So folgendes hier sind auszüge aus dem AVR-GCC-Tutorial

8-Bit Timer zB

zuerst stehen da in welchem register sich was befindet usw, schön und 
gut dann kommt dieser Absatz hier:


Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. 
Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 
0 wird das Timer Overflow Flag TOV0 im Timer Interrupt Flag 
TIFR-Register gesetzt und, falls so konfiguriert, ein entsprechender 
Timer-Overflow-Interrupt ausgelöst und die daran gebundene 
Interrupt-Routine abgearbeitet. Das TOV Flag lässt sich durch das 
Hineinschreiben einer 1 und nicht wie erwartet einer 0 wieder 
zurücksetzen.

Kann mir einer sagen was ein blutiger Anfänger mit sowas anfangen soll? 
Interrupts kommen auch erst später im Tutorial.

Ich weiss nicht was daran so schwer ist mir ein einfaches Beispiel zu 
geben mit dem ich das dann nachvollziehen könnte.


Wenn jmd von mir zum Beispiel Integralrechnung erklärt haben wollte dann 
würde ich ihm auchein einfaches Beispiel zeigen und nciht sagen "lies 
dir mal das und das buch durch"

Danke

von Marco M. (marco1987)


Lesenswert?

> Das ICES1-Bit hat mit Deiner Anwendung überhaupt
> nichts zu tun.

Deshalb ist es ja ausgeklammert

> Und ich frage mich, was
> Du für ein Datenblatt hast. CTC1 ist ein veralteter Bitname, und das
> steht in den aktuellen Datenblättern auch drin.
Deshalb auch ausgeklammert nur damit ich weiss das das eigentlich der 
CTC befehl ist

von Johannes M. (johnny-m)


Lesenswert?

So, ich hab mir grad mal im Tutorial die betreffenden Passagen 
angesehen, und festgestellt, dass da tatsächlich etwas 
Überarbeitungsbedarf besteht. Der CTC-Modus wird gar nicht explizit 
erwähnt, was mir entgangen war.

Abgesehen davon willst Du etwas mit dem 16-Bit-Timer machen (und nicht 
mit nem 8-Bit-Timer), weshalb Du natürlich besser den Abschnitt über den 
16-Bit-Timer als Referenz nimmst. Für Dein derzeitiges Vorhaben, ein 
Signal an einem Portpin auszugeben, brauchst Du auch zunächst gar keinen 
Interrupt. Das Schalten des Portpins kann der µC auch allein machen, 
ohne dass das Programm eingreifen muss.

Das Beste ist, sich das Datenblatt des µC (und zwar des Mega16 und 
nicht des Mega8, auf den sich das Tutorial bezieht) zur Hand zu nehmen 
und dort in den Tabellen nachzuschlagen, welche Bits für welche 
Betriebsart gesetzt werden müssen. Das ist in den betreffenden Passagen 
eigentlich sehr schön übersichtlich zusammengefasst.

Der CTC-Modus (Clear Timer on Compare match) ist eine Betriebsart, in 
der der Timer bei Erreichen des Compare-Wertes in dem betreffenden 
Compare-Register zurückgesetzt wird. Beim Compare-Ereignis kann 
zusätzlich noch ein bestimmter Portpin automatisch umgeschaltet werden 
(was Du ja auch schon versucht hast, über die COM-Bits einzustellen). 
CTC bildet im Prinzip die Grundlage für PWM. Bei der PWM kommt dann eben 
zusätzlich eine zweite Compare-Einheit ins Spiel.

Wie gesagt: Lass die ganze Interrupt-Sache erst mal weg und versuche, an 
einem OC-Pin ein Signal auszugeben.

von Johannes M. (johnny-m)


Angehängte Dateien:

Lesenswert?

Anbei mal eine Art "AVR-Tutorial", das zur Verwendung zusammen mit dem 
entsprechenden Controller-Datenblatt ausgelegt ist. Ich habe versucht, 
die wichtigen Komponenten und ihre Anwendung im Klartext verständlich zu 
beschreiben. Es werden dabei allerdings meist keine direkten Beispiele 
in Sachen I/O-Register u.ä. gegeben, v.a. weil es recht universell sein 
soll, und bei unterschiedlichen AVRs auch unterschiedliche Dinge zu 
beachten sind. Die konkreten Bitkonfigurationen für die Steuerregister 
muss man sich dann anhand des Datenblattes herleiten. Ist eben 
eigentlich für Studenten im Rahmen eines Programmierpraktikums gedacht, 
aber vielleicht hilft es Dir auch, gewisse Zusammenhänge zu verstehen.

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.