Audio PWM en atmega644

1

Estoy intentando generar audio a través de un conector de 3.5 mm utilizando un atmega644. Intenté seguir este tutorial , pero incluso con el muestreo proporcionado, sólo me da un gemido agudo. Leí mucho en la hoja de datos y encontré este código:

#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);
}

Pero entonces no sale nada de la toma de auriculares. Mi primer pensamiento fue que la interrupción no se está disparando, pero estoy bastante seguro de que está bien configurado. ¿Alguna idea?

EDITAR: El bit WGM12 está en el registro TCCR1B y el temporizador 1 necesita una fuente de reloj, como señala Bruce Abbot a continuación. He cambiado esto en el código y ahora se dispara la interrupción, sin embargo todavía no reproduce el sonido. En su lugar, recibo un gemido agudo y ocasionalmente roto por un ruido sordo.

También, me di cuenta de que probablemente no había tiempo, así que cambié. Hice algunos cálculos y se me ocurrió lo anterior (espero que sea correcto). Se requería poder configurar cuando el Temporizador 1 se desbordó, así que he cambiado del Modo 5 al Modo 14. Después de corregir algunos errores matemáticos, está mejorando: ruido borroso en lugar de un silbido agudo.

La solución a esto fue una combinación de las siguientes respuestas y comentarios. El código anterior está funcionando, aunque es bastante baja la calidad que se puede escuchar claramente "está funcionando" (para esta aplicación, no me molesta la calidad, pero supongo que para corregirlo solo debe considerar una mayor tasa de muestreo como Chris Stratton menciona a continuación). Gracias a todos los que ayudaron.

    
pregunta The Beanstalk

3 respuestas

1

Su vector de interrupción es incorrecto. La rutina ISR busca TIMER0_1VF_vect aunque nunca configuró el temporizador TIMER0 y TIMER0_1VF_vect no es un vector válido de acuerdo con lista de canales .

Configura el TIMER1 para que interrumpa el desbordamiento, por lo que debe usar el TIMER1_OVF_vect, que se activará en el desbordamiento del TIMER1

    
respondido por el Funkyguy
1
#define SAMPLE_RATE 8000;

Aparte de cualquier otro problema que pueda tener, una frecuencia de muestreo de 8 KSPS está dentro del rango audible y cumple fácilmente con la descripción de "un zumbido agudo".

Para utilizar una frecuencia de muestreo tan baja sin que el cuadro sea audible, se requerirá un filtro de paso bajo externo con reducción suficiente para atenuar el cuadro, lo que reducirá sustancialmente el ancho de banda de audio. Básicamente, si lo implementas correctamente se fue con el ancho de banda de la clase "telefónica".

Para hacer audio PWM, es probable que desee una mayor velocidad de cuadros. La familia ATtiny puede hacer PWM con una frecuencia de reloj imprecisa de ~ 64 MHz multiplicada por el oscilador interno. Varias partes de ARM pueden multiplicarse a partir de un cristal externo más preciso.

    
respondido por el Chris Stratton
0

Debe proporcionar una fuente de reloj para el temporizador 0. En el ATmega644, esto se selecciona con los bits 2: 0 de TCCR0B, así que agregue la siguiente línea a su función init() : -

TCCR0B = (1<<CS00);

El bit de modo PWM WGM12 está en el registro TCCRnB (no TTCRnA), y también debe seleccionar una fuente de reloj para PWM. Así que cambia esto: -

TCCR1A = (1<<COM1A1) | (1<<WGM12) | (1<<WGM10);

a esto: -

TCCR1A = (1<<COM1A1) | (1<<WGM10);
TCCR1B = (1<<WGM12) | (1<<CS10); 

Finalmente, debe habilitar las interrupciones por desbordamiento en el temporizador 0 (no en el temporizador 1), por lo que debe cambiar: -

TIMSK1 = (1<<TOIE1); // Interrupt on overflow

a: -

TIMSK0 = (1<<TOIE0); // Interrupt on Timer 0 overflow

Esto debería hacer que se dispare la interrupción.

Sin embargo, si sus datos de muestra se almacenan en Flash ROM (PROGMEM), es posible que no se reproduzca correctamente. La memoria del programa está en un espacio de direcciones diferente y necesita un código especial para acceder a ella, por lo que es posible que tenga que usar la función pgm_read_byte() para decirle al compilador que genere las instrucciones correctas, como esta: -

OCR1A = pgm_read_byte(&(pcm_samples[sample++])); 
    
respondido por el Bruce Abbott

Lea otras preguntas en las etiquetas