Forum: Mikrocontroller und Digitale Elektronik 16-Bit Timer ATmega16 Problem bei Compare Match Betrieb


von Julian Z. (julian1103)


Lesenswert?

Hallo zusammen,

ich habe folgendes Problem:
Und zwar möchte ich mit einem 16-Bit Timer einen Impulszähler 
realisieren.
Solange die Impulse nicht gleich der Zahl, die im OCR1A-Register steht 
sind, soll der Ausgang OC1A (an dem derzeit eine LED hängt) HIGH-Level 
haben. Sobald der Timer den Compare Match registriert soll OC1A auf 
LOW-Level gehen.

Als uC verwende ich einen ATmega16 mit 16MHz externen Oszilator.
Als Impulsgeber nutze ich derzeit einen entprellten Taster.

Hierzu der Code:
1
int main()
2
{
3
  DDRD |= (1 << PD5);    // PORT PD5 (LED) als Ausgang
4
  PORTD |= (1 << PD5);    // LED an
5
6
  OCR1A = 9;                              // Ereignis nach 10 Impulsen auslösen
7
  TCCR1A |= (1 << COM1A1);    // OC1A aus bei Vergleichwert
8
  TCCR1B |= (1 << CS10) | (1 << CS11) | (1 << CS12);  // External Clock an T1, steigende Flanke (PB1)
9
10
  
11
12
  while (1);    // endlos
13
  return 0;
14
}

Leider bleibt die LED aber immer aus.

Ich habe die Logik einmal umgekehrt, so das erst keine LED leuchtet und 
nach 10 Impulen die LED leuchtet. Dies funktioniert einwandfrei.

Hierzu noch der Code mit umgekehrter Logik.
1
int main()
2
{
3
  DDRD |= (1 << PD5);    // PORT PD5 (LED) als Ausgang
4
  PORTD &= ~(1 << PD5);    // LED aus
5
6
  OCR1A = 9;                              // Ereignis nach 10 Impulsen auslösen
7
  TCCR1A |= (1 << COM1A1) | (1 << COM1A0);    // OC1A ein bei Vergleichwert
8
  TCCR1B |= (1 << CS10) | (1 << CS11) | (1 << CS12);  // External Clock an T1, steigende Flanke (PB1)
9
10
  
11
12
  while (1);    // endlos
13
  return 0;
14
}

Ich komme nicht drauf woran es liegen könnte.

Vllt hatte ja jemand von euch schon einmal das selbe Problem oder sieht 
meinen Fehler, den ich wohl übersehe.

Schon mal Danke im Vorraus

MFG

Julian

von Peter D. (peda)


Lesenswert?

Da ist tricky, im Compare-Output ist das PORTD.D5-Bit wirkungslos.
Du must also erstmal ein Set-On-Compare ausführen.
Oder versuch mal die Fast-PWM.

Compare-Output mit externem Takt klingt aber schon ziemlich seltsam. Was 
soll das denn werden?

: Bearbeitet durch User
von Julian Z. (julian1103)


Lesenswert?

Geplant ist ein Wasserdurchflussmesser.
dieser gibt bei einer bestimmten Wassermenge z.B 0,5Litern jeweils einen 
Impuls aus.
Sprich wenn ich 10 Liter haben möchte muss ich 20 Impulse abwarten.
Dann soll mit mit dem OC1A das Ventil wieder abgeschalten werden und 
somit der Wasserfluss gestoppt werden.

So der Plan :-)

von chris (Gast)


Lesenswert?

Dann lass das doch nicht den Hardwarepin des OCA1 machen, sondern 
setz/lösche den Pin doch gleich in der Interruptroutine.

Vorteil du kannst jeden Pin nutzen und bist flexibel.

0. externen takt auf TCNT1 geben
1. TCNT1 < OCR1A ?
ja Int löst aus setze mit 2. fort
nein mache weiter Hauptprogramm oder sonst was
2. Int OCR1A löst aus, PinY, X setzen/löschen
3. Setze Programm fort bevor der Int ausgelöst hat und warte wieder auf 
INT OCR1A

von S. Landolt (Gast)


Lesenswert?

OCR1A-ISR einrichten und dort den Ausgang 'von Hand' schalten, d.h. die 
'Automatik' mit COM1A weglassen.

von Julian Z. (julian1103)


Lesenswert?

OK
Ich werde mal eine ISR einrichten und das mal probieren.
Klingt auf jeden Fall logisch.

Danke schonmal und ich werde weiter informieren obs und wie es geht ;-)

von Julian Z. (julian1103)


Lesenswert?

Ok leider tut es das nicht was es soll
zumindest die LED leuchtet schon mal am anfang.
Was ist an der ISR falsch

1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#define F_CPU 16000000UL
4
5
ISR (TIMER1_COMPA_vect)
6
{
7
  PORTD &= ~(1<<PD6);
8
}
9
10
int main()
11
{
12
  DDRD |= (1 << PD6);    // PORT PD6 (LED) als Ausgang
13
  PORTD |= (1<<PD6);    // LED An
14
  TIMSK |= (OCIE1A);  //  Interrupt Enabled Timer1 Output Compare Match
15
  OCR1A = 9;                              // Ereignis nach 10 Impulsen auslösen
16
  TCCR1B |= (1 << CS10) | (1 << CS11) | (1 << CS12);  // External Clock an T1, steigende Flanke (PB1)
17
  sei();
18
19
  while (1)    // endlos
20
  {
21
22
  }
23
24
  return 0;
25
}

von chris (Gast)


Lesenswert?

Im Int sollst du nur

INT:
 INVERT PinD , 6
ENDE INT

Mehr muss da nicht rein

Das was du hast ist die Initialisierung und ein CTC-Mode würde sich 
anbieten somit kannst du in der Timerinit OCR1A mit 9 laden und sobald 
>9 wird TCNT1 auf 0 gesetzt.

von Julian Z. (julian1103)


Lesenswert?

Ist es nicht egal ob ich den PinD 6 definiert auf LOW setze oder 
Invertiere??

In der Initialisierung beschreibe ich meine Register für den Timer1 und 
sezte die LED auf High.

Sobald mein Compare Match auslöst sezte ich die LED auf Low was aber 
nicht funktioniert.

Den CTC Mode kann ich mir wohl noch sparen bis nicht die eigentliche 
Funktion läuft. der CTC Mode sagt ja nur aus, das er mir den Counter 
wieder auf 0 zurücksetzt wenn ich das richtig verstehe

von chris (Gast)


Lesenswert?

chris schrieb:
> INT:
>  INVERT PinD , 6
> ENDE INT

Korrigiere nach 9 soll

INT:
CLR PIND , 6
CLR TCNT1, CS0:1:2
Ende INT

Die Frage ist was ist die Bedingung welches den Pin auf 1 setzt und den 
Timer wieder startet ?

von Julian Z. (julian1103)


Lesenswert?

Im moment soll das Programm nichts anderes machen als dass die LED von 
anfang an leuchtet und wenn ich 10 mal den taster drücke diese ausgeht. 
er soll derzeit noch keinen timer zurücksetzen oder erneut starten.

von S. Landolt (Gast)


Lesenswert?

>  TIMSK |= (OCIE1A);

Entschuldigung, das hat jetzt eine Weile gedauert - da fehlt "1<<"

von Julian Z. (julian1103)


Lesenswert?

Ohje ja das ist ein leichtsinnsfehler. Werde das gleich Mal probieren 
wenn ich daheim bin. Klingt aber plausibel

von Julian Z. (julian1103)


Lesenswert?

Danke für die schnellen Antworten.

Wie bereits vermutet, was es nur ein Leichtsinnsfehler und es war 
natürlich das "1<<".
Jetzt tut es was es tun soll.

Dankeschön für die schnelle Hilfe.

von c-hater (Gast)


Lesenswert?

Julian Z. schrieb:

> Jetzt tut es was es tun soll.

Schön, dass du wenigstens die Krücke mit Interrupthilfe zum Laufen 
gebracht hast...

Die korrekte Lösung mit alleinigem Einsatz der Hardware wäre übrigens 
gewesen:
1
int main()
2
{
3
  DDRD |= (1 << PD5);    // PORT PD5 (LED) als Ausgang
4
5
  OCR1A = 9;                              // Ereignis nach 10 Impulsen auslösen
6
7
  TCCR1A = (1 << COM1A1)|(1 << COM1A0); //Vorbereitung für LED an
8
  TCCR1A |= (1 << FOC1A);               //LED an durch Pseudo-compare match
9
10
  TCCR1A = (1 << COM1A1);               //Vorbereitung für LED aus durch
11
                                        //compare match des Timers
12
13
  TCCR1B = (1 << CS10) | (1 << CS11) | (1 << CS12);  // External Clock an T1, steigende Flanke (PB1)
14
15
16
17
  while (1);    // endlos
18
  return 0;
19
}

Den prinzipiellen Hinweis darauf, wie man es richtig macht, hast du 
übrigens bereits in der allerersten Antwort in diesem Thread von PeDa 
bekommen...

Übrigens: Aufpassen! Der obige Code ist zwar für Mega16 korrekt, aber 
viele andere Devices haben die FOC-Bits für Timer1 nicht in TCCR1A, 
sondern in TCCR1C.

von Peter D. (peda)


Lesenswert?

Julian Z. schrieb:
> Geplant ist ein Wasserdurchflussmesser.

Die Comparehardware ist hauptsächlich dafür gedacht, daß man jitterarm 
und ohne Verzögerung durch Programmlaufzeiten innerhalb 50ns etwas 
schalten kann, z.B. Audio- oder Videoausgabe.
Dein Durchflußsensor wird weder 10MHz Impulse liefern, noch wird das 
Ventil innerhalb 50ns schalten können.
Ein Lösung voll in Software ist daher dicke ausreichend. Vermutlich ist 
auch eine Entprellung/Entstörung der Sensorpulse nötig, wozu man z.B. 
den Timer benutzen kann.

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.