Ich versuche, Audio über eine 3,5-mm-Buchse mit einem Atmega644 zu erzeugen. Ich habe versucht, diesem Tutorial zu folgen , aber selbst mit dem bereitgestellten Sampling höre ich nur ein hohes Jammern. Ich habe viel im Datenblatt gelesen und bin auf diesen Code gekommen:
#define F_CPU 1000000
#include <stdint.h>
#include <avr/io.h>
#include "pcm_sample.h"
#include <avr/interrupt.h>
#define SAMPLE_RATE 8000;
volatile uint16_t sample = 0;
ISR(TIMER1_OVF_vect) {
if (sample >= pcm_length) {
sample = 0;
}
OCR1A = pcm_samples[sample++];
}
void init(void) {
DDRD = (1<<PD5); // OCR1A
// Fast PWM Mode 14, TOP=ICR1(0x7D), toggle OC1A on compare match
TCCR1A = (1<<COM1A1) | (1<<WGM11);
TCCR1B = (1<<CS10) | (1<<WGM12) | (1<<WGM13);
TIMSK1 = (1<<TOIE1); // Interrupt on overflow
// Timer ticks at 1us, samples every 125us, so overflow every 125us
ICR1 = 0x7D; // Overflow at 125
OCR1A = pcm_samples[0]; // Set initial duty
sei();
}
int main(void) {
init();
while (1);
}
Aber dann kommt nichts aus der Kopfhörerbuchse. Mein erster Gedanke war, dass der Interrupt nicht ausgelöst wird, aber ich bin mir ziemlich sicher, dass er richtig eingerichtet ist. Irgendwelche Ideen?
BEARBEITEN: Das WGM12-Bit befindet sich im TCCR1B-Register und Timer 1 benötigt eine Taktquelle, wie Bruce Abbot unten betonte. Ich habe dies im Code geändert und jetzt wird der Interrupt ausgelöst, der Ton wird jedoch immer noch nicht wiedergegeben. Stattdessen bekomme ich ein hohes Heulen und gelegentlich von einem pochenden Geräusch unterbrochen.
Außerdem wurde mir klar, dass das Timing wahrscheinlich falsch war, also habe ich mich geändert, ich habe ein bisschen gerechnet und bin auf das Obige gekommen (hoffentlich ist es richtig). Ich musste einstellen können, wann Timer 1 übergelaufen ist, also habe ich von Modus 5 zu Modus 14 gewechselt. Nachdem ich einige Rechenfehler korrigiert habe, wird es besser: Fuzzy-Rauschen anstelle eines hohen Heulens.
Die Lösung hierfür war eine Kombination der folgenden Antworten und Kommentare. Der obige Code funktioniert, obwohl die Qualität ziemlich niedrig ist, können Sie deutlich hören, dass "es funktioniert" (für diese Anwendung mache ich mir keine Sorgen um die Qualität, aber ich nehme an, um das Problem zu beheben, müssen Sie nur nach einer höheren Abtastrate suchen als Chris Stratton unten erwähnt). Danke an alle die geholfen haben.
Ihr Interrupt-Vektor ist falsch. Die ISR-Routine sucht nach TIMER0_1VF_vect, obwohl Sie den TIMER0-Timer nie eingerichtet haben und TIMER0_1VF_vect laut Atmels-Liste kein gültiger Vektor ist .
Sie richten TIMER1 so ein, dass es bei Überlauf unterbricht, also sollten Sie stattdessen TIMER1_OVF_vect verwenden, das bei TIMER1-Überlauf ausgelöst wird
#define SAMPLE_RATE 8000;
Abgesehen von allen anderen Problemen, die Sie haben könnten, ist eine Abtastrate von 8 KSPS gut im hörbaren Bereich und entspricht ohne weiteres der Beschreibung eines "hohen Heulens".
Um eine so niedrige Abtastrate zu verwenden, ohne dass das Framing hörbar ist, ist ein externes Tiefpassfilter mit ausreichendem Roll-Off erforderlich, um das Framing zu dämpfen, was Ihre Audiobandbreite erheblich einschränkt - im Grunde genommen bleibt Ihnen bei richtiger Implementierung möglicherweise " Bandbreite der Telefonklasse.
Für PWM-Audio benötigen Sie wahrscheinlich eine höhere Bildrate. Die ATtiny-Familie kann PWM mit einer ungenauen Taktrate von ~64 MHz ausführen, die vom internen Oszillator multipliziert wird. Verschiedene ARM-Teile können mit einer Rate von einem präziseren externen Quarz multipliziert werden.
Sie müssen eine Taktquelle für Timer 0 bereitstellen. Im ATmega644 wird dies durch die Bits 2:0 von TCCR0B ausgewählt, fügen Sie Ihrer init()
Funktion also die folgende Zeile hinzu:
TCCR0B = (1<<CS00);
Das PWM-Modus-Bit WGM12 befindet sich im Register TCCRnB (nicht TTCRnA), und Sie müssen auch eine Taktquelle für PWM auswählen. Also ändere das:-
TCCR1A = (1<<COM1A1) | (1<<WGM12) | (1<<WGM10);
dazu:-
TCCR1A = (1<<COM1A1) | (1<<WGM10);
TCCR1B = (1<<WGM12) | (1<<CS10);
Schließlich müssen Sie Überlauf-Interrupts für Timer 0 (nicht Timer 1) aktivieren, also ändern Sie: -
TIMSK1 = (1<<TOIE1); // Interrupt on overflow
Zu:-
TIMSK0 = (1<<TOIE0); // Interrupt on Timer 0 overflow
Dies sollte den Interrupt auslösen.
Wenn Ihre Beispieldaten jedoch im Flash-ROM (PROGMEM) gespeichert sind, werden Sie möglicherweise feststellen, dass sie nicht richtig wiedergegeben werden. Der Programmspeicher befindet sich in einem anderen Adressraum und erfordert einen speziellen Code, um darauf zuzugreifen. Daher müssen Sie möglicherweise die Funktion verwenden, pgm_read_byte()
um den Compiler anzuweisen, die richtigen Anweisungen zu generieren, wie folgt: -
OCR1A = pgm_read_byte(&(pcm_samples[sample++]));
AHB
Die Bohnenranke
Chris Stratton
Die Bohnenranke
Bruce Abbott