Forum: Mikrocontroller und Digitale Elektronik Timer zählt nur bis zu Hälfte


von Doubleyou W. (doubleyou)


Lesenswert?

Hallo,

ich habe mich heute ein bisschen in die Timer in Verbindung mit 
Interruptfunktionen beschäftigt. Ich würde gerne eine Softwarelösung zum 
generieren von zwei PWM Signale realisieren. Ich habe dazu die CTC 
Methode verwändet. Der Code hänge ich unten an.

Im Einsatz ist ein ATMEGA8 an einem 16MHz Quarz.
Zum Berechnen des OCR Wertes habe ich folgende Formel benutzt:

OCRnx = e ^ (0,00330082*iBit+5,86166)

Wobei iBit für einen Wert vom AD Wandler steht und somit zwischen 
0-1024bit liegt. Die Formel habe ich über Gnumeric ermittelt um den 
Bereich von 1,5kHz bis 4,5kHz abzudecken. Über die Formel zum Berechnen 
der Freqenzen mit Hilfe folgender Formel hat auch in Exel exakt 
gestimmt. Jedoch bekomme ich bei dem unten angehängten Programm exakt 
die hälfte der Frequenz. Obwohl der Prescaler auf 1 eingestellt ist.

Freq = (F_CPU/(Prescaler*(OCRnx+1)))

Wie man sie auch hier findet: 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR#CTC-Betriebsart_.28Clear_Timer_on_Compare_Match.29

Mir ist kein offensichtlicher Fehler aufgefallen vielleicht habt ihr ja 
noch einen Tipp für mich.
1
volatile uint8_t flag;
2
3
int OCR(int iBit)
4
{
5
  float f_e, f_func_var1, f_func_var2, f_OCR_res, f_OCR_zres;
6
  int i_OCR_res;
7
  f_e = 2.718282;
8
  f_func_var1 = 0.00330082;
9
  f_func_var2 = 5.86166;
10
  f_OCR_zres  = ((f_func_var1 * iBit) + f_func_var2);
11
  f_OCR_res   = (float) pow(f_e, f_OCR_zres);
12
  f_OCR_res  += 0.5;
13
  i_OCR_res   = (int) f_OCR_res;
14
  return i_OCR_res;
15
}
16
17
float frequenz(int i_OCR)        
18
{
19
  float f_freq_res;  
20
  f_freq_res = (CPU_T/(PRESCALE*(i_OCR+1)));
21
  f_freq_res = (f_freq_res / 1000);
22
  return f_freq_res;
23
}
24
25
26
ISR (TIMER1_COMPB_vect)//(SIG_OUTPUT_COMPARE1B)
27
{
28
  if (flag == 1) {
29
    flag = 0;
30
    PORTB |= (1<<PB1);
31
    PORTB &= ~(1<<PB2);
32
  }
33
  else {
34
    flag = 1;
35
    PORTB &= ~(1<<PB1);
36
    PORTB |= (1<<PB2);
37
  }
38
}
39
int main(void)
40
41
{
42
        ...
43
        //PortB Einstellen
44
  DDRB |= (1 << DDB1) | (1 << DDB2); //PIN PB1 und PB2 als Ausgang
45
  //Timer Einstellen
46
  TCCR1B = 0x00;
47
  TCCR1B |= (1<<WGM12); //Timer1 mit Auflösung 2¹⁶ in CTC Modus
48
  TCCR1B |= (1<<CS10);  //Prescaler auf 1
49
  TIMSK  |= (1<<OCIE1B);// Output Compare Match Interrupt Enable
50
  sei();
51
        ...
52
        //Hauptprogramm starten
53
54
  while(1){
55
        ...
56
        cli();
57
        OCR1A = (uint16_t) iOCR;
58
        sei();
59
        ...
60
}

von Stefan B. (Gast)


Lesenswert?

Vermutung: Pro ISR erzeugst du nur eine Halbperiode deines Signals. Die 
ISR müsste also doppelt so oft aufgerufen werden, um eine ganze Periode 
zu erzeugen.

Tipp: Bei Fragen den Quellcode und erwartete/gemessene Werte für ein 
komplettes Beispiel angeben. Dann kann man das im Simulator nachstellen 
und braucht nicht zu vermuten.

von Knut (Gast)


Lesenswert?

Moin,

im Datenblatt des ATmega8 steht auf Seite 91/307 die Formel zu 
Berechnung der Ausgangsfrequenz. Da der Ausgang bei jedem Compare Match 
toggelt, hast du immer einen Faktor von 1/2 drinn!

von Doubleyou W. (doubleyou)


Lesenswert?

Na dann hab ich da wohl was überlesen. Danke für die Information dann 
werde ich das ganze nochmal überarbeiten.

Mit freundlichen Grüßen

DoubleU

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.