Forum: Mikrocontroller und Digitale Elektronik [avr/c++] 440Hz Ton ausgeben - so möglich?


von Andreas D. (escentf)


Lesenswert?

Hallo,
ich möchte ohne fast pwm einen 440Hz(ungefähr) Dauerton ausgeben.
verwende einen avr-8bit mit 16mhz

So sieht mein versuch aus:
https://gist.github.com/anonymous/2a6c619f6a1f27f41a94e3f9bc924f6e

Stimmt die Berechnung bzw. der Code oder bin ich auf dem komplett 
falschen Weg?

OCR1A = (16000000  2  1 / 440) - 1; //mit prescale 1
Hört sich irgendwie nicht nach 440 Hz, wie auf YouTube an.

Danke

von spess53 (Gast)


Lesenswert?

Hi

>(16000000  2  1 / 440) - 1

Was soll das bedeuten?

MfG Spess

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Angehängte Dateien:

Lesenswert?

spess53 schrieb:
> Was soll das bedeuten?
Dass er ein Opfer des Nichtlesens der Bedienungsanleitung geworden ist:
1
OCR1A = (16000000 / 2 / 1 / 440) - 1; //mit prescale 1

Andreas D. schrieb:
> verwende einen avr-8bit mit 16mhz
Sicher? CLKDIV? Interner Oszillator?

> Hört sich irgendwie nicht nach 440 Hz, wie auf YouTube an.
Sondern?
Was sagt die "Stimmgerät"-APP vom Handy?

: Bearbeitet durch Moderator
von Stefan F. (Gast)


Lesenswert?

Du hast vergessen, anzugeben, welchem Mikrocontroller du verwendest. 
Deine Formel ergibt einen Wert von ca 18.000 also muss der Timer ein 
16bit Timer sein. Lass uns mal annehmen, dass dies der Fall ist.

Mir ist aufgefallen, dass du TCCR1A und die WGM Bits nicht verwendest, 
daher arbeitet der Timer in "Normal" Modus, so daß er immer von 0 bis 
0xFFFF zählt.

Was genau willst du eigentlich mti deinen beiden Interrupt-Routinen 
erreichen und welcher Modus erfüllt deine Anforderung?

Beitrag #5026250 wurde von einem Moderator gelöscht.
Beitrag #5026257 wurde von einem Moderator gelöscht.
von Jürgen S. (jurs)


Lesenswert?

Andreas D. schrieb:
> Hallo,
> ich möchte ohne fast pwm einen 440Hz(ungefähr) Dauerton ausgeben.
> verwende einen avr-8bit mit 16mhz


Ich als Arduino-Programmierer schreibe zum Erzeugen einer 
Rechteckfrequenz im Audiobereich für so etwas einfach nur:
1
 tone(8,440);

Und es läuft.

Egal ob 8-bit AVR-Controller, oder ein 32-Bitter, der von der 
Arduino-IDE unterstützt wird.

Der erste Parameter der tone() Funktion gibt den Pin (nach 
Arduino-Zählweise) an, der zweite Parameter die gewünschte Frequenz).

Und wenn der Dauerton wieder stoppen soll, wäre er Befehl dafür:
1
noTone();


Kein Herumrechnen, keine manuelle Timerprogrammierung von internen 
Registern. Es hat auch seine Vorteile, wenn die IDE bereits umfangreiche 
Core-Libraries für typische Controller-Aufgaben mitbringt.

Kein Wunder, dass Arduino in den letzten 10 Jahren bei Hobbybastlern so 
beliebt geworden ist.

Aber wenn man keine so komfortablen Library-Funktionen zur Verfügung 
hat, es geht natürlich mit AVR-GCC auch komplizierter (sogar auch in der 
Arduino-IDE, wenn man möchte).

von Stefan F. (Gast)


Lesenswert?

Jetzt bitte nur noch hilfreiche Antworten geben.

von spess53 (Gast)


Lesenswert?

Hi

>Ich als Arduino-Programmierer schreibe zum Erzeugen einer
>Rechteckfrequenz im Audiobereich für so etwas einfach nur:

> tone(8,440);


>Und es läuft.

Ist ja gut. Es hat sich ja schon herumgesprochen das Arduino für 
Musiker, Künstler und ähnlich programmiertechnisch minderbemittelte 
erfunden wurde. Das brauchst du nicht extra betonen. Oder an wie viele 
der Arduino Libs hast du selbst mit Hand angelegt?

@ Andreas D. (escentf)

Wenn du es wirklich ohne PWM-Mode machen willst brauchst du keinen 
Overflow-Interrupt. Lediglich im ISR(TIMER1_COMPA_vect) TCNT1 auf 0 
setzen und den Ausgang invertieren. Einfacher wäre der CTC-Mode. Dazu 
müsstest du lediglich in TCCR1B noch WGM12 setzen und die Interrupts 
sperren. Ausgang wäre das PIN OC1A.

MfG Spess

von Andreas D. (escentf)


Lesenswert?

Ich verwende den atmega328p. Die Tone Funktion kann ich nicht verwenden.
Und die "Formel" hab ich von hier:
http://www.avrfreaks.net/forum/generating-frequencies

von Jürgen S. (jurs)


Lesenswert?

Andreas D. schrieb:
> Ich verwende den atmega328p. Die Tone Funktion kann ich nicht verwenden.


Ein Atmega328 hat drei Timer, Timer0, Timer1 und Timer2

Und jeder Timer kann im Prinzip auf "Interrupt on Overflow" oder 
"Interrupt on compare Match" progammiert werden

Programmiere Dir einen dieser drei Timer so, dass er alle 1136 
Mikrosekunden einen Timer-Interrupt erzeugt (1136µs= halbe Periodendauer 
bei 440Hz), und mache Dir eine Interruptbehandlungsfunktion, die beim 
Auftreten des Timer-Interrupts den gwünschten Pin toggelt!

Welchen der drei möglichen Timer möchtest Du verwenden?

: Bearbeitet durch User
von Andreas D. (escentf)


Lesenswert?

also ohne TIMER1_COMPA_vect ?

von Jürgen S. (jurs)


Lesenswert?

Andreas D. schrieb:
> also ohne TIMER1_COMPA_vect ?
Wie meinen?
Wenn Du Timer1  in der Betriebsart "Interrupt on compare match" 
verwenden möchtest, dann natürlich MIT einer 
Interrupt-Behandlungsfunktion , die vom Rumpf her so aussieht:

ISR(TIMER1_COMPA_vect){
   //interrupt commands for TIMER 1 here
}

Und in den leeren Funktionsfumpf müßtest Du die Befehle reinschreiben, 
die den gewünschten Ausgangspin toggeln.

Und zwar mit einer Timer-Frequenz von 880Hz für eine Ausgangsfrequenz 
von 440Hz.

Denn eine volle Periode bedeutet: Es sird zweimal getogelt, einmal nach 
der halben Periodendauer der Ausgangsfreuenz und einmal am Ende der 
Periodendauer.

D.h. die Interruptfrequenz muß daher genau doppelt so hoch sein wie die 
zu erzeugende Ausgangsfrequenz.

: Bearbeitet durch User
von Dichter (Gast)


Lesenswert?

Wie schon Wilhelm Busch sagte:

Mit Kürze, Klarheit und Struktur
geht's mit Arduino nur.

Hier findet sich beispielhaft die Einstellung der Timer Register:

https://github.com/Terbytes/Arduino-Atmel-sPWM


http://interface.khm.de/index.php/lab/interfaces-advanced/arduino-dds-sinewave-generator/

von Christian S. (roehrenvorheizer)


Lesenswert?

Hallo,

aber das Toggeln eines Pins mittels IRQ erzeugt doch einen Jitter, oder 
nicht?

MfG

von eProfi (Gast)


Lesenswert?

Lass doch die Hardware arbeiten: CTC mit toggle on compare match
1
// by selecting OC1B to toggle its logical level on each compare match (COM1B[1:0]=0x1).
2
  DDRB=1<<PB2; //enable PB2/OC1B output
3
  ICR1=0x4705; //16000000 / 2 / 440 = 18181,8  gerundet 18182  -1 = 0x4705
4
  TCCR1A=(1<<COM1B0) + (0<<WGM10); //0x10 toggle output OC1B, WGM=12 (CTC with ICR1)
5
  TCCR1B=(3<<WGM12 ) + (1<<CS10 ); //0x19 prescaler=1

von Jürgen S. (jurs)


Lesenswert?

Dichter schrieb:
> Wie schon Wilhelm Busch sagte:
>
> Mit Kürze, Klarheit und Struktur
> geht's mit Arduino nur.
>
> Hier findet sich beispielhaft die Einstellung der Timer Register:
>
> https://github.com/Terbytes/Arduino-Atmel-sPWM
>
>
> 
http://interface.khm.de/index.php/lab/interfaces-advanced/arduino-dds-sinewave-generator/

Gerade in Deinem zweiten Beispiel-Link erfolgt die Tonerzeugung aber auf 
vollkomen andere Art.

Den Themenstarter hatte ich so verstnaden, als wenn er eine 
Rechteckfrequenz mit 440Hz, quasi PWM mit 50% Duty-Cycle an einem 
Ausgang erzeugen möchte, um einen Ton auf einem Piezo-Lautsprecher 
(Transducer) hörbar zu machen.

In Deinem Sinusgenerator-Beispiel läuft es aber so:

Es wird eine (für Atmega-Verhältnisse extrem hochfequente PWM erzeugt, 
und diese wird während einer einzigen Periode dutzendfach im 
Duty-Cycldangepaßt, der mit einem Sinus steigt und fällt.

Da die PWM-Frequenz weit über der Resonanzfrequenz des Lautsprechers 
liegt, wird quasi die Auslenkung der Lautsprechermembrane über den 
Duty-Cycle der PWM gesteuert. Und da der Duty-Cycle einem Sinus folgt, 
folgt auch die Auslenkung der Lautsprechermembrane diesem Sinus. Das 
Ergebnis ist ein sehr reiner Glockenklang bei dieser Art digitaler 
Klangsynthese, der sich beim Anhören deutlich wahrnehmbar von einem 
Square-Wave-Piepsen mit derselben Frequenz unterscheidet.

von Jürgen S. (jurs)


Lesenswert?

eProfi schrieb:
> Lass doch die Hardware arbeiten: CTC mit toggle on compare match
Dann kann er sich den Pin, der getoggelt werden soll, aber NICHT frei 
aussuchen, sondern der wird ihm vom Datenblatt vorgegeben. Kann man 
natürlich machen, sofern man bei dem Pin, an dem der Lautsprecher 
angeschlossen sein soll, flexibel ist und keine eigenen Wünsche hat.

von Jürgen S. (jurs)


Lesenswert?

Christian S. schrieb:
> Hallo,
>
> aber das Toggeln eines Pins mittels IRQ erzeugt doch einen Jitter, oder
> nicht?

Weniger als eine handvoll Contollertakte, vielleicht.

Das kommt aber auch drauf an:
Wenn Du noch ein halbes Dutzend anderer Interrupts auf dem Controller 
laufen hast, womöglich alle mit unterschiedlicher Laufzeit der 
jeweiligenInterruptbehandlungsroutine, dann gibt es natürlich Jitter.

Aber wenn es der einzige Interrupt ist, der alle1136µs feuert, dann gibt 
es keinen Jitter. Wo soll der auch herkomen, es ist ja kein PC mit einem 
Eigenleben im Betriebssystem, sondern nur ein Mikrocontroller ohne 
Betriebssystem. Da läuft alles seinen geregelten (auch zeitlichen) Gang, 
es sei denn, Du hast viele Interrupts gleichzeitig aktiv, die sich im 
Ablauf gegenseitig behindern.

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

Hi

>Es wird eine (für Atmega-Verhältnisse extrem hochfequente PWM erzeugt,
>und diese wird während einer einzigen Periode dutzendfach im
>Duty-Cycldangepaßt, der mit einem Sinus steigt und fällt.

Ja. nennt sich DDS (Direkte Digitale Synthese).

http://www.mikrocontroller.net/articles/Digitaler_Funktionsgenerator

MfG spess

von c-hater (Gast)


Lesenswert?

Christian S. schrieb:

> aber das Toggeln eines Pins mittels IRQ erzeugt doch einen Jitter, oder
> nicht?

Kommt drauf an. Man kann durchaus vollständig jitterfreie Timer-ISRs 
programmieren, wenn man die maximale variable Interruptlatenz des 
Restsystems kennt.

Allerdings läuft dieser Ansatz letztlich darauf hinaus, eben diese 
variable Latenz (und noch etwas mehr) IMMER in der Timer-ISR zu 
verheizen, also die Gesamtperformance des Systems runterzuziehen. Und 
oft viel schlimmer: Man erhöht damit wiederum auch die variable Latenz 
für alle konkurrierenden Interrupts...

Die Entscheidung für eine Anwendung des Prinzips wird man sich deshalb 
immer sehr gut überlegen und wenn man sie überhaupt nutzt, wird man sie 
i.d.R. auf eine einzelne ISR beschränken.

Manchmal geht's aber halt nicht anders. Typisches Beispiel dürfte wohl 
wohl eine Ausgabe auf VGA- oder Videomonitore sein. Hierfür ist genau so 
eine latenzkompensierte ISR für die Ausgabe jeder einzelnen Bildzeile 
eine recht gute, weil halbwegs universelle Lösung. Besser geht's nur 
mittels spezieller Hardware, die halt nicht immer verfügbar ist, sei es, 
weil der µC garnicht darüber verfügt oder sei es, dass sie just in der 
konkreten Anwendung für einen anderen Zweck benötigt wird.

von Rolf M. (rmagnus)


Lesenswert?

Christian S. schrieb:
> aber das Toggeln eines Pins mittels IRQ erzeugt doch einen Jitter, oder
> nicht?

Jo, um ein paar Dutzend Nanosekunden könnte das schon wackeln.

Jürgen S. schrieb:
> Ich als Arduino-Programmierer schreibe zum Erzeugen einer
> Rechteckfrequenz im Audiobereich für so etwas einfach nur:
> tone(8,440);
>
> Und es läuft.

So, und jetzt variiere dabei mal noch die Impulsbreite...
Man kann sich auf dem PC auch ganz einfach eine komplette 
Tabellenkalkulation "programmieren" (und stolz wie Oskar drauf sein), 
indem man die Funktion createSpreadShead() der passenden Bibliothek 
aufruft. Aber wehe, der dritte Button von links soll ein blaues statt 
ein grünes Icon darstellen. Dann ist der stolze "Coder" schon völlig 
aufgeschmissen.

von spess53 (Gast)


Lesenswert?

Hi

Jürgen S. (jurs) schrieb

>Aber wenn es der einzige Interrupt ist, der alle1136µs feuert, dann gibt
>es keinen Jitter.

Kommt dir als "Arduino-Programmierer" nur so vor. Ein Interrupt wird 
erst angesprungen wenn der gerade laufende Assemblerbefehl abgearbeitet 
ist. Da ein Assemblerbefehl bis zu 5 Takte dauern kann, ist ein Versatz 
von bis zu vier Takten, je nach Zeitpunkt der Interruptauslösung, 
möglich.

MfG Spess

von Jürgen S. (jurs)


Lesenswert?

spess53 schrieb:
> Ein Interrupt wird
> erst angesprungen wenn der gerade laufende Assemblerbefehl abgearbeitet
> ist. Da ein Assemblerbefehl bis zu 5 Takte dauern kann, ist ein Versatz
> von bis zu vier Takten, je nach Zeitpunkt der Interruptauslösung,
> möglich.
>
> MfG Spess

Das wären bei 16 MMHz Controllertakt weniger als 400 Nanosekunden bei 
einem Interrupt, der alle 1136 Mikrosekunden feuern soll.

Das halte ich in Anbetracht der Aufgabe, einen Piezo-Transducer im 
Kammerton A piepsen zu lassen, für vernachläsigbar.

Ob der Themenstarter hier überhapt noch mitliest und Interesse an 
zielführendem AVR-Code hat?
Deine Rückfrage, die Du kaum eine Viertelstunde nach dem Start des 
Themas gestellt hast, ist vom TO bis jetzt jedenfalls nicht beantwortet 
worden.

Für mich sieht da so aus, als wenn er das Interesse an einer Lösung 
verloren hat, noch innerhalb der ersten Stunde nach dem Start seines 
Themas.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Vllt, hat er ja auch einfach Atmels Application Note AVR314 oder sogar 
AVR131 gefunden.

von Manfred (Gast)


Lesenswert?

Lothar M. schrieb:
>> Was soll das bedeuten?
> Dass er ein Opfer des Nichtlesens der Bedienungsanleitung geworden ist:
Dir ist schon klar, dass kursiv und unterstreichen in diesem Forum gerne 
mal Zufallsergebnisse liefern? Ich komme da nur schlecht mit klar.

von spess53 (Gast)


Lesenswert?

Hi

>Das wären bei 16 MMHz Controllertakt weniger als 400 Nanosekunden bei
>einem Interrupt, der alle 1136 Mikrosekunden feuern soll.

>Das halte ich in Anbetracht der Aufgabe, einen Piezo-Transducer im
>Kammerton A piepsen zu lassen, für vernachläsigbar.

Das ist mir klar. Trotzdem ist die Aussage '...  dann gibt es keinen 
Jitter.' falsch. Die Länge der Befehle war nur ein Beispiel. C/C++ hält 
noch eine ganze Reihe andere Szenarien bereit die Interrupts für eine 
gewisse Zeit sperren können.

>Deine Rückfrage, die Du kaum eine Viertelstunde nach dem Start des
>Themas gestellt hast, ist vom TO bis jetzt jedenfalls nicht beantwortet
>worden.

Du hast meine Frage von 14:27 auch nicht beantwortet.

MfG Spess

von Jürgen S. (jurs)


Lesenswert?

spess53 schrieb:
> Du hast meine Frage von 14:27 auch nicht beantwortet.
> MfG Spess

Damit kannst Du ja nur Deine Off-Topic Frage meinen:
"an wie viele der Arduino Libs hast du selbst mit Hand angelegt?"

Hat zwar mit dem Thema dieses Threads nichts zu tun, aber wenn es Dich 
so stark interessiert, dass Du nochmal darauf zurückkommst, ist meine 
Antwort:
Keine einzige!

Meine Beiträge zur Arduino-Community beschränken sich darauf, dass ich 
im Forum von Arduino.cc gelegentlich mal das eine oder andere 
Beispielprogramm zu dort geposteten Fragestellungen poste,aber oft sind 
die Themenstarter so ignorant, dass in ihrem eigenen Thema nicht mal 
dann mehr antworten, wenn sie vollständig copy-and-paste-fähigen Code 
als Programmbeispiel gepostet bekommen. Das geht mir zunehmend auf die 
Nüsse, wenn Leute erst ein Thema starten und nach Code fragen, sich dann 
aber tot stellen, wenn sie tatsächlich Code gepostet bekommen. Und hier 
in diesem Forum und diesem Thema bin ich mir auch nicht sicher, ob der 
Themenstarter noch auf einen copy-and-paste-fähigen AVR-Beispielcode zur 
Piepstonausgabe mit 440 Hz wartet, oder ob er verschwunden ist und daran 
gar kein Interesse mehr hat.

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.