mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Frequenzberechnung bei PWM


Autor: Maruu ;-) (maruu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich hab ein kleines Problem mit PWM. Ich will ein Stimmgerät für meine 
Trompete bauen und will auch mit einem Piezo-Piepser einen Referenzton 
erzeugen nachdem ich dann meine Trompete stimmen kann. Dafür versuch ich 
nun ein Rechtecksignal zu generieren das ich dann auf meinen 
Piezo-Piepser loslasse. Folgedes Programm hab ich dazu geschrieben:

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

void port_init(void)
{
 PORTD = 0xFF;
 DDRD  = 0xFF;
}

void timer1_init(void)
{
 TCCR1A = 0x00; //Timer stoppen
 TCCR1B = 0x00;

 OCR1A=512;    // Pulsbreite 50% High  50% Low

 TCCR1A = (1 << WGM11) | (1 << WGM10) | (1 << COM1A1); // nicht inv. 
10Bit PWM

 TCCR1B = (1 << CS10) | (1<<CS11); // Counterfrequenz = Taktfrequenz 
(8MHz) / 64

}

void init_devices(void)
{
 cli(); //Alle Interrupts ausschalten
 port_init();
 timer1_init();
}

int main(void)
{
  init_devices();

  while(1);

  return(0);
}


So nun kurz zur Erklärung, ich setze alle Pins des Ports D als Ausgang. 
Dann initialisiere ich den PWM-Timer und setze die Werte für PWM also 
welche Pulsbreite das Signal haben soll. So das kompiliert auch und ich 
bekomm auch ein Rechtecksignal an PIN 19 meines Atmega32. Aber das hat 
laut meinem Oszi eine Freuenz von 7,6Hz.

Ich dachte nun das sich die Frequenz anhand des oben stehenden Codes 
folgendermaßen berechnet:

F-PWM = F-CPU(8Mhz) / 64 (durch Bit CS10 und CS11) / 2046 (durch WGM10 
und WGM11 also 10Bit Counter)

So das ergibt bei mir 61,09Hz. Wie oben geschrieben messe ich aber 
7,6Hz. Irgendwo steckt da noch ein Faktor 8 drin. Nur leider weiß ich 
nicht wo. Mache ich irgendwo nen Denkfehler?

Gruß

Maruu

Autor: sch_michael (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

könnte es sein, dass dein µC mit internem Quarz, das entspricht 1MHz, 
läuft, das könnte den Faktor 8 erklären.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ sch_michael (Gast)

>könnte es sein, dass dein µC mit internem Quarz, das entspricht 1MHz,
>läuft, das könnte den Faktor 8 erklären.

uCs haben keinen internen Quarz, nur interne RC-Oszillatoren.

MFG
Falk

Autor: sch_michael (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hast ja recht,
Takt
 wäre das richtige Wort gewesen.

Autor: Maruu ;-) (maruu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

das könnte natürlich sehr gut sein. Bisher hab ich leider immer PIC's 
programmiert. Habt Ihr mir nen Tip welches Register ich wie beschreiben 
muss für nen Externen Quarz mit 8Mhz?

Gruß

Maruu

Autor: Düsentrieb (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Maruu ;-) (maruu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

@ Düsentrieb: Danke für den Tip den Link kannte ich schon und auf dessen 
Basis werd ich wohl auch das Stimmgerät entwickeln, nur werd ich wohl 
den dort geposteten C-Code modifizieren und die Nulldurchgänge des 
Signals mit einem Comperator zählen. Den Referenzton wollte ich noch als 
Feature inbauen. Auch ein Metronom wäre noch schön. Naja alles noch 
Spielerei und ein nettes erstes Projekt für nen Atmel. Bisher hatte ich 
immer mit PIC's programmiert, da is die Doku für Assembler sehr gut 
siehe Sprut aber es gibt keine guten kostenlosen C-Compilier. Ab einer 
gewissen komplexität wird es aber mit Assembler schwer. Deshalb will ich 
für die Zukunft eher auf AVR setzen. Is zwar schade aber sooo 
unterschiedlich sind die beiden ja nicht.

Gruß

Maruu

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Maruu ;-) (maruu)

>programmiert. Habt Ihr mir nen Tip welches Register ich wie beschreiben
>muss für nen Externen Quarz mit 8Mhz?

AVR Fuses

MFG
Falk

Autor: Maruu ;-) (maruu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ahhh super, den Beitrag hab ich zu spät gesehen. Hab mir erst mal einen 
meiner 2 AVR's Ver-Fused den werd ich dann morgen (falls ich da schon 
nüchtern bin ;-) retten mit nem externen Takt.

Jetzt funktionieren auch meine berechneten 61,1Hz.

So nun hab ich noch eine letzte Frage. Ich will genau eine Frequenz von 
440Hz erzeugen. Aber wie bitte bekomm ich das hin? Geht das überhaupt 
mit PWM?

Gruß

Maruu

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Du willst doch nor eine Tonfrequenz erzeugen?
Im einfachsten Fall ist das ein Rechtecksignal mit Tastverhältnis 1:1.
Schau mal vei den Timern nach CTC-Mode, der ist für sowas gedacht.

Den Link zum passenden Tutorial hat sicher Falk parat. :)

PS: Bei 8MHz sind genau 440Hz nicht möglich, bei Teiler 18182 kämen nur
439,9956Hz raus, bei Teiler 18181 wären es 440,0198Hz.

Gruß aus Berlin
Michael

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Michael U. (amiga)

>Den Link zum passenden Tutorial hat sicher Falk parat. :)

Kann man sich auch ggf. selber suchen.

http://www.mikrocontroller.net/articles/Spezial:Allpages
AVR-Tutorial: Timer

>PS: Bei 8MHz sind genau 440Hz nicht möglich, bei Teiler 18182 kämen nur
>439,9956Hz raus, bei Teiler 18181 wären es 440,0198Hz.

Karajan ist tot. Und nicht mal der hätte den Unterschied zwischen 440 
und 4439,9956 Hzb gehört. ;-)

MFG
Falk

Autor: Maruu ;-) (maruu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

danke euch Beiden für den Tipp. Das mit dem CTC scheint das passende zu 
sein. Hab mich mal ein bisschen eingelesen und folgendes Programm 
geschrieben:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>

void Init_Timer_2(void)
{

//440,1408Hz
TCCR2  = (1 << WGM21)|(1 << CS22)|(1 << COM20); //CTC-Modus, Teiler auf 64, Toggel des Pins D7
OCR2   = 141; 
}

int main (void)
{
DDRD  |= (1<<PD7);
sei();
Init_Timer_2();

while(1);
}

Das liefert mir eine Frequenz von 440,1408Hz. Aber wieso? Ich hab einen 
8Mhz Quarz dran. Dann teile ich durch 64 mit dem Prescaler. Also

8000000 / 64 = 125000

so nun noch durch den Wert der in OCR2 steht teilen:

125000 / 141 = 886,5248

Da für eine 1Hz ja 2 mal getoggelt werden muss muss ich den Wert noch 
durch 2 teilen:

886,5248 / 2 = 443,262 Hz

So das sollte das Ergebnis sein das ich mit den obigen Einstellungen 
erwartet hab aber raus kommen 440,1...Hz. Mach ich da irgendwas falsch? 
Hat mein Quarz keine 8Mhz (drauf steht aber 8,000000b Mhz) oder woran 
liegt das?

Gruß

Maruu

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Maruu ;-) wrote:
>
> 125000 / 141 = 886,5248


125000 / 142

Wenn der Timer von 0 bis 141 zählt, dann braucht er dazu
142 Zyklen.

125000 / 142 = 880,28
880,28 / 2 = 440.14

Autor: Maruu ;-) (maruu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ahhhhh jetzt wird es Tag ;-)
Deshalb, hab mich schon gefragt woher das kommt.

So nun noch ne kleine Frage, Michael U. hat oben geschrieben das ich nen 
Teiler von 18181 verwenden könnte. Durch das oben eingestellte 
Verhältnis hab ich ja nen Teiler von:

142*64*2 = 18176

Wie komm ich denn jetzt auf den Teiler 18181? Das OCR2 Register is ja 
nur 8bit groß da kann ich dann doch keine höhere genauigkeit mehr 
einstellen oder?

Gruß

Maruu

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Maruu ;-) wrote:

> Wie komm ich denn jetzt auf den Teiler 18181? Das OCR2 Register is ja
> nur 8bit groß da kann ich dann doch keine höhere genauigkeit mehr
> einstellen oder?

Richtig.

Aber der Timer 1 ist 16-bittig (Denke ich zumindest. Du hast ja
nicht gesagt welchen Prozessor du benutzt)

Du solltest dich aber fragen, ob dieses eine Zehntel Herz
tatsächlich hörbar ist.

Autor: Maruu ;-) (maruu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

ich verwende den Atmega32. Stimmt der Timer1 is ja ein 16Bit Timer. Hab 
den jetzt auch verwendet. Da kann ich dann auch höhere Werte ins OCR 
Register schreiben.

So für alle die es noch interessiert hier die Quellcode:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>

void Init_Timer_1(void)
{

//440Hz
//TCCR1A = (1 << COM1A0);
//TCCR1B = (1 << WGM12)|(1 << CS10);
//OCR1A  = 9090; 

//442Hz
TCCR1A = (1 << COM1A0);
TCCR1B = (1 << WGM12)|(1 << CS10);
OCR1A  = 9049; 
}

int main (void)
{
DDRD  |= (1<<PD5);
sei();
Init_Timer_1();

while(1);
}

Danke für eure Hilfe, damit ist der erste Teil schon mal geschafft.

Gruß

Maruu

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.