Un método para generar un tono constante es Direct Digital Synthesis. Necesitarás un DAC, ya sea un chip dedicado o una escalera de resistencia.
Configura un contador para desbordamiento en la frecuencia que desea generar, y en cada tic del contador lo usa para indexar una tabla de ondas y obtener un valor de salida para su DAC.
He escrito un par de diferentes técnicas de generación de tonos para el Arduino aquí en New Noises From MidiVox. El código de actualización DAC es específico de MidiVox (y Adafruit WaveShield) MCP4921, pero la generación de onda sinusoidal debe ser generalmente aplicable.Intenté mantener el código en general general para ATmegas, pero hay un par de Arduino-ismos que se arrastraron.
Pegado de esa publicación, aquí hay un código para reproducir un tono de 440Hz en un Arduino con un MCP4921 en el bus SPI:
uint16_t sample = 0;
/* incr = freq * (2^16/15625)
* So for 440Hz, incr = 1845 */
uint16_t incr = 1845;
/* oscillator position */
uint16_t pos = 0;
const uint8_t sine[] = {
0x80, 0x83, 0x86, 0x89, 0x8C, 0x8F, 0x92, 0x95, 0x98, 0x9B, 0x9E, 0xA2,
0xA5, 0xA7, 0xAA, 0xAD, 0xB0, 0xB3, 0xB6, 0xB9, 0xBC, 0xBE, 0xC1, 0xC4,
0xC6, 0xC9, 0xCB, 0xCE, 0xD0, 0xD3, 0xD5, 0xD7, 0xDA, 0xDC, 0xDE, 0xE0,
0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEB, 0xED, 0xEE, 0xF0, 0xF1, 0xF3, 0xF4,
0xF5, 0xF6, 0xF8, 0xF9, 0xFA, 0xFA, 0xFB, 0xFC, 0xFD, 0xFD, 0xFE, 0xFE,
0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFD,
0xFD, 0xFC, 0xFB, 0xFA, 0xFA, 0xF9, 0xF8, 0xF6, 0xF5, 0xF4, 0xF3, 0xF1,
0xF0, 0xEE, 0xED, 0xEB, 0xEA, 0xE8, 0xE6, 0xE4, 0xE2, 0xE0, 0xDE, 0xDC,
0xDA, 0xD7, 0xD5, 0xD3, 0xD0, 0xCE, 0xCB, 0xC9, 0xC6, 0xC4, 0xC1, 0xBE,
0xBC, 0xB9, 0xB6, 0xB3, 0xB0, 0xAD, 0xAA, 0xA7, 0xA5, 0xA2, 0x9E, 0x9B,
0x98, 0x95, 0x92, 0x8F, 0x8C, 0x89, 0x86, 0x83, 0x80, 0x7D, 0x7A, 0x77,
0x74, 0x71, 0x6E, 0x6B, 0x68, 0x65, 0x62, 0x5E, 0x5B, 0x59, 0x56, 0x53,
0x50, 0x4D, 0x4A, 0x47, 0x44, 0x42, 0x3F, 0x3C, 0x3A, 0x37, 0x35, 0x32,
0x30, 0x2D, 0x2B, 0x29, 0x26, 0x24, 0x22, 0x20, 0x1E, 0x1C, 0x1A, 0x18,
0x16, 0x15, 0x13, 0x12, 0x10, 0x0F, 0x0D, 0x0C, 0x0B, 0x0A, 0x08, 0x07,
0x06, 0x06, 0x05, 0x04, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06,
0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C, 0x0D, 0x0F, 0x10, 0x12, 0x13, 0x15,
0x16, 0x18, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x24, 0x26, 0x29, 0x2B, 0x2D,
0x30, 0x32, 0x35, 0x37, 0x3A, 0x3C, 0x3F, 0x42, 0x44, 0x47, 0x4A, 0x4D,
0x50, 0x53, 0x56, 0x59, 0x5B, 0x5E, 0x62, 0x65, 0x68, 0x6B, 0x6E, 0x71,
0x74, 0x77, 0x7A, 0x7D
};
void setup() {
cli();
/* Enable interrupt on timer2 == 127, with clk/8 prescaler. At 16MHz,
this gives a timer interrupt at 15625Hz. */
TIMSK2 = (1 << OCIE2A);
OCR2A = 127;
/* clear/reset timer on match */
TCCR2A = 1<<WGM21 | 0<<WGM20; /* CTC mode, reset on match */
TCCR2B = 0<<CS22 | 1<<CS21 | 0<<CS20; /* clk, /8 prescaler */
SPCR = 0x50;
SPSR = 0x01;
DDRB |= 0x2E;
PORTB |= (1<<1);
sei();
}
ISR(TIMER2_COMPA_vect) {
/* OCR2A has been cleared, per TCCR2A above */
OCR2A = 127;
pos += incr;
/* shift left a couple of bits for more volume */
sample = sine[highByte(pos)] << 2;
PORTB &= ~(1<<1);
/* buffered, 1x gain, active mode */
SPDR = highByte(sample) | 0x70;
while (!(SPSR & (1<<SPIF)));
SPDR = lowByte(sample);
while (!(SPSR & (1<<SPIF)));
PORTB |= (1<<1);
}
void loop() {
}
el Clou síntesis digital directa es que es muy fácil de jugar múltiples tonos juntos (por adición) o mezclarlas en volúmenes deseados (multiplicando por el volumen antes de añadir).
He encontrado que un Arduino puede reproducir unos 30 tonos simultáneamente con este método. Mi aplicación particular es para Hammond organ simulation, y eso también puede ser útil para la lectura.
Interesante proyecto, pero desafortunadamente él "externalizó" la parte de decodificación de sonido a un chip de un tercero: "El otro chip a bordo es un VS1011 de VLSI, Finlandia. Es un chip decodificador .mp3 y .wav, DAC, y un amplificador de auriculares todo en un paquete de 28 pines " Estoy después de hacerlo yo mismo, solo por el bien de aprender :) – palako