Forum: Mikrocontroller und Digitale Elektronik CTC mit flag setzen, ohne interrupt


von Jürgen M. (Gast)


Lesenswert?

N'abend community,

ich habe Atmel studio 7, arduino uno r3 board, atmega 328p mit 16mhz.

Ich möchte ein 38khz signal mithilfe eines timers erstellen. Leider hat 
es nach vielem lesen und probieren geklappt. Nun verstehe ich die 
programmierung nicht. :D

ich will durch das hochzählen bis 211 eine halbwelle erzeugen, pin 
toggeln und anschließend mit ausgeschaltetem pin hochzählen.
Soweit klappt es. Nur verstehe ich nicht, warum die frequenz sich so 
stark ändert.
Wenn ich ocr0a=210 eingebe, erhalte ich 18,94khz.
bei 209 - garnichts.
208 - 7,692khz.
207 - 15,6khz
206 - 9,615khz

Egal was ich bei ocr0a in richtung "0" eingebe, ich sehe keine 
proportionale veränderung der freuquenz am oszi.

hab ich etwas übersehen? und ist es überhaupt ohne interrupt machbar?
1
#include <avr/io.h>
2
3
  int main(void)
4
  {
5
  DDRB |= 0x04;
6
  TCCR0A |= (1 << WGM01); //CTC
7
  TCCR0B|=(1 << CS00); //no prescaling 
8
  OCR0A = 211; //  16mhz/2/38000khz = 210,52takte pro halbe periode 
9
         // -> ich wähle 211 takte und erhalte 37,880khz auf dem oszi
10
11
  while(1){
12
  if (TIFR0 & (1 << OCF0A)) //Wenn OfC0A "1" ist soll PB2 toggeln. 
13
  {
14
  PORTB ^= 0x04;
15
  }
16
  TIFR0 |= (1 << OCF0A); 
17
  }
18
  }

von Karl M. (Gast)


Lesenswert?

Hallo,

hier
1
OCR0A = 211;
, sind es 212 Schritte bis ein Event erzeugt wird!

Und dies ist so nicht korrekt, siehe Datenblatt:
1
TIFR0 |= (1 << OCF0A);

Korrekt:
1
TIFR0 = (1<<OCF0A);

von Edi R. (edi_r)


Lesenswert?

Jürgen M. schrieb:
> if (TIFR0 & (1 << OCF0A)) //Wenn OfC0A "1" ist soll PB2 toggeln.
>   {
>   PORTB ^= 0x04;
>   }
>   TIFR0 |= (1 << OCF0A);

Hier kann es passieren, dass der Timer genau dann seinen Endwert 
erreicht, nachdem der Vergleich "if (TIFR0 & (1 << OCF0A))" durchgeführt 
wurde, aber bevor das Löschen des Flags kommt. Das bedeutet, dass das 
Flag anschließend gelöscht ist, ohne dass der Portausgang getoggelt 
wurde. Besser ist es, das Löschen des Flags in die if-Klammer 
reinzunehmen:
1
if (TIFR0 & (1 << OCF0A)) //Wenn OfC0A "1" ist soll PB2 toggeln.
2
{
3
  PORTB ^= 0x04;
4
  TIFR0 = (1 << OCF0A);
5
}

von Jürgen M. (Gast)


Lesenswert?

sry, mein fehler. meinte 210.
1
 TIFR0 = (1<<OCF0A);
  hab ich abgeändert, aber die frequenzen verhalten sich immer noch 
unproportional.

wenn ich die takte zurückrechne, dann müsste doch eigentlich eine 
taktzahl von 208

16000000/2/208=38461hz ergeben. aber mein oszi zeigt mir nur 7,6khz.

von M. K. (sylaina)


Lesenswert?

Jürgen M. schrieb:
> Leider hat
> es nach vielem lesen und probieren geklappt.

Leider? Ist doch prima ;)

Jürgen M. schrieb:
> Nun verstehe ich die
> programmierung nicht.

Nochmal lesen kann helfen ;)

Jürgen M. schrieb:
> hab ich etwas übersehen? und ist es überhaupt ohne interrupt machbar?

Ja, aber warum willst du es ohne ISR machen? Ist doch Quatsch.
1
while(1){
2
  if (TIFR0 & (1 << OCF0A)) //Wenn OfC0A "1" ist soll PB2 toggeln. 
3
  {
4
    PORTB ^= 0x04;
5
  }                      // <- Stellen wir uns vor wenn der Mikrocontroller hier ist tritt der CTC auf.
6
  TIFR0 |= (1 << OCF0A); // dann wird er hier direkt wieder gelöscht. Die if-Bedingung sieht das aber nie.
7
}                        // das ist viel zu ungenau/unsicher, daher ISR benutzen! Kostet doch nix.
Karl M. schrieb:
> Und dies ist so nicht korrekt, siehe Datenblatt:

Wieso?
Atmega328 Datasheet Page 148
> ...
> OCF0A is cleared by writing a logic one to the flag.
> ...
Da steht nichts davon, dass man in alle anderen Bits ne 0 reinschreiben 
soll oder anders gesagt ein
1
TIFR0 |= (1 << OCF0A);
bewirkt an der Stelle das Selbe wie ein
1
TIFR0 = (1 << OCF0A);
Da sich Bits in diesem Register nur durch das Reinschreiben einer 1 
löschen lassen.

: Bearbeitet durch User
von Edi R. (edi_r)


Lesenswert?

M. K. schrieb:
> Atmega328 Datasheet Page 148
>> ...
>> OCF0A is cleared by writing a logic one to the flag.
>> ...
> Da steht nichts davon, dass man in alle anderen Bits ne 0 reinschreiben
> soll oder anders gesagt einTIFR0 |= (1 << OCF0A);
> bewirkt an der Stelle das Selbe wie einTIFR0 = (1 << OCF0A);

Nein, das bewirkt nur dann das Selbe, wenn alle anderen Bits im Register 
vorher 0 waren. Wenn ein anderes Bit 1 war, dann wird dieses meiner 
Ansicht nach ebenfalls gelöscht.

von Jürgen M. (Gast)


Lesenswert?

@Edi:
Danke, das war der Fehler.

@köhler:
Ich danke dir für deinen Rat. Sicher kommt eine gute programmierung an 
interrupts nicht vorbei. Nur bin ich noch in den anfängen und will 
erstmal grundkenntisse in C erlangen. Davor hatte ich ne zeitlang in 
arduino programmiert und da war alles mit hintergrundabläufen verbastelt 
und ungenau, da sind solche kleinen Fehler kaum aufgefallen.

Gruß Jürgen

von M. K. (sylaina)


Lesenswert?

Edi R. schrieb:
> Wenn ein anderes Bit 1 war, dann wird dieses meiner
> Ansicht nach ebenfalls gelöscht.

Nein, wird es nicht. Ein Bit im TIFR kann nur dadurch gelöscht werden 
indem man eine eins reinschreibt. Das steht genau so auch im Datenblatt. 
Schreibt man eine null rein ändert sich das entsprechende Bit nicht.

Unter der Berücksichtigung der Wahrheitstabelle für das TIFR (0 nur dann 
wenn zwei Einsen im Spiel sind) bewirkt dann ein
1
TIFR0 = (1 << OCF0A);
2
// ist z.B. ein 0b00000110 :TIFR0
3
//             &0b00000100 :1<<OCF0A
4
//             -----------
5
//              0b00000010 :new TIFR0

exakt das Selbe wie ein
1
TIFR0 |= (1 << OCF0A)
2
// ist z.B. ein 0b00000110 :TIFR0
3
//             |0b00000100 :1<<OCF0A
4
//             -----------
5
//              0b00000010 :new TIFR0
[/c]

den ein
1
x |= 1 << y;

schreibt lediglich eine 1 in x an die Bitstelle y und lässt alle anderen 
Stellen unverändert.

Ein
1
x &= 1 << y;
würde eine 1 in x an die Bitstelle y schreiben und an alle anderen 
Stellen einen 0.

von Stefan E. (sternst)


Lesenswert?

M. K. schrieb:
> Edi R. schrieb:
>> Wenn ein anderes Bit 1 war, dann wird dieses meiner
>> Ansicht nach ebenfalls gelöscht.
>
> Nein, wird es nicht.

Doch, wird es.

M. K. schrieb:
> // ist z.B. ein 0b00000110 :TIFR0
> //             |0b00000100 :1<<OCF0A
> //             -----------
> //              0b00000010 :new TIFR0

Nein, 0b00000110 | 0b00000100 ist 0b00000110

M. K. schrieb:
> den ein
> x |= 1 << y;
> schreibt lediglich eine 1 in x an die Bitstelle y und lässt alle anderen
> Stellen unverändert.

Und wie passiert das "lässt alle anderen Stellen unverändert"? Indem das 
|= überall dort eine 0 hinschreibt, wo auch vorher eine 0 war, und 
überall dort eine 1 hinschreibt, wo auch vorher eine 1 war. Und 
letzteres bedeutet im Kontext des TIFR0 "lösche ALLE gesetzten Flags".

von Stefan E. (sternst)


Lesenswert?

Nachtrag:

M. K. schrieb:
> Ein
> x &= 1 << y;
> würde eine 1 in x an die Bitstelle y schreiben und an
> alle anderen
> Stellen einen 0.

Auch das ist falsch. "alle anderen Stellen eine 0" ist richtig, aber an 
die Bitstelle y wird geschrieben, was auch vorher drin war, nicht immer 
eine 1.

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.