Ich kann monophone Töne erzeugen, indem ich einen einzelnen Stift ( mit unterschiedlicher Geschwindigkeit ) umschalte, der mit einem Piezo-Summer verbunden ist.
Wie kann ich in Software zwei gemischte Audiosignale erzeugen, um Polyphonie zu erzeugen?
Hier ist der Code, den ich verwende, um eine einfache Melodie zu spielen.
#define F_CPU 8000000UL // 8MHz
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/delay.h>
// number of timer0 overflows/sec
#define INT_PER_SEC 31250
// Frequencies (in Hz) of notes
#define F_FSH_4 370
#define F_A_4 440
#define F_B_4 494
#define F_E_4 330
#define F_CSH_5 554
#define F_D_5 587
#define F_FSH_5 740
#define F_CSH_4 277
#define F_GSH_4 415
// number of timer0 overflows for notes
#define REST -1 // special case
#define FSH_4 INT_PER_SEC/F_FSH_4
#define A_4 INT_PER_SEC/F_A_4
#define B_4 INT_PER_SEC/F_B_4
#define E_4 INT_PER_SEC/F_E_4
#define CSH_5 INT_PER_SEC/F_CSH_5
#define D_5 INT_PER_SEC/F_D_5
#define FSH_5 INT_PER_SEC/F_FSH_5
#define CSH_4 INT_PER_SEC/F_CSH_4
#define GSH_4 INT_PER_SEC/F_GSH_4
#define SEMIQUAVER_TIME 60 // ms
#define BREATH_TIME 20 // ms
volatile uint32_t intrs = 0;
volatile int32_t curNote = REST;
// TIMER0 overflow
ISR(TIMER0_OVF_vect)
{
if (curNote == REST)
intrs = 0;
else
{
intrs++;
if (intrs >= curNote)
{
PORTD ^= _BV(PD4);
intrs = 0;
}
}
}
void play(int32_t note, uint32_t len)
{
int i;
curNote = note;
for (i = 0; i< len; i++)
_delay_ms(SEMIQUAVER_TIME);
curNote = REST;
_delay_ms(BREATH_TIME);
}
int main(void)
{
/* setup clock divider. Timer0 overflows on counting to 256.
* 8Mhz / 1 (CS0=1) = 8000000 increments/sec. Overflows every 256, so 31250
* overflow interrupts/sec */
TCCR0B |= _BV(CS00);
// enable overflow interrupts
TIMSK0 |= _BV(TOIE0);
// PD4 as output
DDRD = _BV(PD4);
TCNT0 = 0;
intrs = 0;
curNote = REST;
// enable interrupts
sei();
while (1)
{
// Axel F
play(FSH_4, 2);
play(REST, 2);
play(A_4, 3);
play(FSH_4, 2);
play(FSH_4, 1);
play(B_4, 2);
play(FSH_4, 2);
play(E_4, 2);
play(FSH_4, 2);
play(REST, 2);
play(CSH_5, 3);
play(FSH_4, 2);
play(FSH_4, 1);
play(D_5, 2);
play(CSH_5, 2);
play(A_4, 2);
play(FSH_4, 2);
play(CSH_5, 2);
play(FSH_5, 2);
play(FSH_4, 1);
play(E_4, 2);
play(E_4, 1);
play(CSH_4, 2);
play(GSH_4, 2);
play(FSH_4, 6);
play(REST, 12);
}
}
Nun, ein einfacher Trick besteht darin, zwei Pins mit PWM zu verwenden und sie an den gegenüberliegenden Seiten des Lautsprechers zu befestigen. Modulieren Sie dann jeden Pin mit einer anderen Geschwindigkeit, und Sie können zwei Noten gleichzeitig spielen ... im Grunde mischt der Lautsprecher sie für Sie zusammen. Mehr als zwei Noten und Sie müssen dies wie erwähnt in der Software tun.
Die Standardmethode, um Polyphonie zu erhalten, besteht darin, bei einer festen Unterbrechungsrate (meistens 8000 Hz oder 44100 Hz) zu unterbrechen und von jeder Tonquelle ein "hoch" (+1) oder "niedrig" (-1) (oder etwas dazwischen) zu erhalten , addieren Sie alle Zahlen, um eine Gesamtzahl zu erhalten, und senden Sie diese Gesamtzahl dann an den DAC.
Wie andere hier gesagt haben, kann ein Hochgeschwindigkeits-PWM mit ein bisschen Cleverness einen DAC ersetzen.
Auf der Seite "Mikrocontroller-Polyphonie" finden Sie weitere Details und Tipps.
Ich denke, dieses nette alte kleine PC-DOS-Spieljuwel verwendet echten polyphonen Sound über den PC-Lautsprecher: Digger .
Ich weiß nicht, wie sie es gemacht haben, aber Sie können den C-Quellcode von der Website herunterladen.
Dies könnte helfen -> einfacher PWM-DAC
Sie könnten einfach zwei Rechteckwellen hinzufügen und schnelle PWM verwenden, um das "analoge" Signal an den Lautsprecher auszugeben.
Hier ist noch eine andere Methode, wenn Sie Spielsound mögen, schnell und schmutzig:
Wenn Sie Software verwenden, um Ihre Rednerereignisse zu timen, besteht der einfachste Ansatz wahrscheinlich darin, zwei unabhängige Datenströme zu generieren und zwischen ihnen zu wechseln. Dieser Ansatz kann ziemlich gut funktionieren, unabhängig davon, ob der Lautsprecherausgang von einem I/O-Pin oder einem DAC gesteuert wird. Zum Beispiel:
int-Selektor; uint16_t phase[8],freq[8];Das obige ist der wesentliche Ansatz, den ich 1996 in einer PIC-basierten Spieluhr verwendet habe (unter Verwendung von Assembler-Code anstelle von C). Beachten Sie, dass die Interrupt-Rate das 8-fache der effektiven Abtastrate betragen muss, aber jeder Interrupt nur die Verarbeitung für eine einzelne Stimme übernehmen muss. Beachten Sie, dass dieser Ansatz bei guter Ausgangsfilterung 3 Bit mehr effektive DAC-Auflösung liefert, als wenn die Abtastwerte numerisch addiert und dann ausgegeben würden, aber er erzeugt viel Rauschen bei der Abtastrate und Vielfachen davon. Das Filtern ist daher noch wichtiger als es sonst der Fall wäre.ungültige Unterbrechung (nichtig) { Selektor++; Selektor&=7; Phase[Wahlschalter] + Frequenz[Wahlschalter]; DAC_OUT = Sinuswelle [Phase [Selektor] >> 8]; }
Früher hat man das auf alten Spielsystemen und zu Zeiten der „ PC-Lautsprecher “ gemacht, aber ich weiß nicht wie.
Erste Vermutung: Denken Sie an die Welle, die Sie idealerweise erzeugen würden, stellen Sie sich dann vor, sie in eine stark beschnittene quadratische Form zu verzerren, und erzeugen Sie dann diese quadratische Form, indem Sie Ihre Ausgabe zu den entsprechenden Zeiten umschalten. Hätte aber viel Intermodulation .
Zweiter Gedanke: Können Sie die Schwingungsfrequenz stark erhöhen und analoge Signale im PWM-Stil ausgeben ?
Wie bereits erwähnt, können Sie dies genauso tun wie früher mit einem PC-Lautsprecher (der nur das Ein- und Ausschalten unterstützt, das optional an einen PWM-Controller angeschlossen ist). Grundsätzlich verstehe ich die Methode, dass Sie den Lautsprecher ein- und ausschalten schnell genug, dass es nie vollständig ein- oder ausgeschaltet ist (ein bisschen wie ein Schaltnetzteil funktioniert). Dadurch bewegt sich der Lautsprecher ständig zwischen ein und aus und erzeugt ein analoges Signal.
Die einzigen Fallstricke sind, dass Sie einen echten Lautsprecher brauchen (ich denke, ein Piezo bewegt sich so schnell, dass er zu schnell voll an und voll aus ist) und Sie müssen in der Lage sein, das Bit schnell genug umzuschalten. Ich habe einige Experimente durchgeführt und bin auf eine maximale Geschwindigkeit von etwa 5 MHz gekommen , was für ein 11.025-Hz-Audiosignal ausreichend sein sollte (wahrscheinlich die beste Qualität, die Sie sich erhoffen können).
Natürlich sind 11025 Hz bei 8 Bit 11 Kilobyte/Sekunde, was viel schneller ist als die Geschwindigkeit einer seriellen Schnittstelle. Es würde nur das Speichern von Audio im Wert von vielleicht ein oder zwei Sekunden im Flash erlauben, also sind Sie ziemlich darauf beschränkt, Audio abzuspielen, das spontan generiert wird, vorausgesetzt, dass genügend CPU-Zeit übrig bleibt, um den Lautsprecher zu drehen!
Es gibt auch ein paar andere Methoden, um dies zu erreichen, und es sieht so aus, als ob es bereits eine Implementierung für das Arduino der oben beschriebenen Methode gibt.
Sie können die Streams einfach wie hier beschrieben hinzufügen:
Spielen Sie Ton A für einen Moment, etwa 50 ms, dann Ton B und schalten Sie hin und her. Die Idee ist, schneller umzuschalten, als das Ohr sagen kann, und es wird so klingen, als würden beide gleichzeitig spielen.
Ich glaube, es gibt eine Tonbibliothek für den Arduino, die zwei Töne macht. Sie sollten den Code an den von Ihnen verwendeten AVR-Chip anpassen können. Auf arduino.cc gibt es auch ein paar hervorragende Threads zur Wellenformerzeugung
Wenn Sie sich entscheiden, einen DAC hinzuzufügen, habe ich ein Beispiel für einen numerisch gesteuerten Oszillator unter http://wiblocks.luciani.org/docs/app-notes/nb1a-nco.html Vier unabhängige Ausgangskanäle. Der Quad-DAC und die Referenz kosten nur etwa 2 US-Dollar.
Hier ist mein Code zum gleichzeitigen Abspielen von 2 Melodien. Entschuldigung, Sie müssen sich bei AVR-Freaks registrieren, um Zugriff zu erhalten.
Rick_2047
Toby Jaffey
Rick_2047
Toby Jaffey
Rick_2047
Toby Jaffey
joeforker