Página principal
Arduino
reproductor de voz mediante tarjeta SD
wav - 8bits
-11KHz
xsetaseta@gmail.com
El arduino da muchas satisfaciones, la
documentación que tiene y sus librerías le hacen totalmente
manejable para distintos montajes, es camaleónico. Creo que su
secreto es la documentación de su microcontrolador y la facilidad de
programación de su entorno. Su entorno de programación basado en
java es un tanto simple, y quizas sea esa la base de su
éxito.
En este montaje realizo un reproductor de voz. En una tarjeta SD
almaceno ficheros wav en un formato de 8bits y con una
frecuencia de 11KHz. Mediante la consola serie escribo el nombre del
fichero a reproducir, y el arduino lo reproduce si existe.
Una de las pocas cosas que no dispone el arduino es un conversor de
D/A de serie, no conozco el motivo, los micros PIC tampoco lo
llevan, debe ser un problema de fabricación para rebajar
costes. Lo mas fácil para hacer un conversor D/A sería mediante una
serie de resistencias, esto implica un circuito de 16 resistencia.
TMRpcm |
Circuito ideal para conversor
D/A
|
Como queremos hacer un circuito lo mas sencillo posible,
utilizaremos la técnica de modulación
por anchura de pulso PWM (Pulse-Width Modulation) .
Consiste como dice el hombre, modular el ancho del pulso para
conseguir un voltage de salida dependiendo de la anchura del pulso.
En la salida colocamos un condensador a modo de filtro para
conseguir una salida analógica mas real.
Una vez explicada la teoría del conversor D/A por ancho de pulso,
vamos a implementarlo mediante el microcontrolador ATMEGA328.
Existen varias librerías que realizan la reproducción de un fichero
wav mediante el arduino como por ejemplo TMRpcm. Pero resultó que
funcionaba en el arduino mega pero no en el arduino diecimila,
no se el motivo.
Ante esta situación decidí realizar toda la programación, y de
esta forma aprender un poco de PWM.
Para la generación de la frecuencia patrón del PWM he
utilizado el timer1 con salida al Pin 9 de arduino.
Se genera una frecuencia de 31KHz por medio del timer1. Viene muy
bien explicado en la página web:
http://ravc00cs.blogspot.com.es/2012/07/pwm.html
Página 134 ATmega328
Se colocan los registros del timer1 con los siguientes valores:
TCCR1A =
0x81; // seleccion del contador. PWM, Phase Correct, 8-bit
TCCR1B = 0x01; // prescaler = 1, clkI/O/1 (No prescaling)
TIMSK1=0; //habilita cualquier
interrupción.
Ahora ya tenemos una señal de 31KHz en la salida 9 del arduino que
podemos modificar el ancho del pulso cambiando el valor del
registro OCR1A
.
Para poder generar en tiempo real sonido a una tasa de muestras de
11KHz, debemos modificar el registro OCR1A
11000 veces por segundo. Esto lo realizamos generando una
interrupción 11000 veces por segundo, esto lo conseguimos
mediante el timer2 del ATMEGA328.
Página 158 ATmega328
Se colocan los registros del timer2 con los siguientes valores:
TCCR2A = 0;// set
entire TCCR2A register to 0
TCCR2B = 0;// same
for TCCR2B
TCNT2 = 0;//initialize
counter value to 0
OCR2A = 182;// =
(16*10^6) / (11000*8) - 1 (must be <256) 11khz
TCCR2A |= (1 << WGM21); //
turn on CTC mode
TCCR2B |= (1 << CS21); // Set CS21 bit for 8 prescaler
TIMSK2 |= (1 << OCIE2A);//
enable timer compare interrupt
En el vector de interrupción ISR(TIMER2_COMPA_vect)
colocamos el programa que modifica el registro OCR1A,
según el valor que nos proporciona en cada instante el fichero wav.
Como la lectura de la SD requiere un tiempo, y los datos para
actualizar el registro OCR1A deben ser
inmediatos, debemos crear un doble buffer donde almacenar los datos
para que su disponibilidad sea inmediata.
//SETA43 21/01/2014
// xsetaseta@gmail.com
// reproductor de wav desde
tarjeta SD
// wav 11kHz mono
// Arduino Diecimila
#include <SD.h>
File dataFile;
String inputString = "";
char fileplay[20]
;
boolean stringComplete = false;
byte valor;
byte bufer0[100];
byte bufer1[100];
int din=0;
int dout=0;
byte nf=0;
byte vacio=1;
byte fin=1;
void setup()
{
Serial.begin(9600);
inputString.reserve(50);
pinMode(9,
OUTPUT);
// pinMode(4, OUTPUT);
// si utilizamos la salida 4 como
CS de la tarjeta SD
// if(!SD.begin(4)){
//
pinMode(10,
OUTPUT); // si utilizamos la salida 4 como CS
de la tarjeta SD
if(!SD.begin(10))
{
Serial.println("Tarjeta
erronea o no insertada"); // Texto informativo
return;
}
else
{
Serial.println("Tarjeta
OK"); // Texto informativo
}
cli();//stop interrupts
TCCR1A =
0x81;
TCCR1B =
0x01;
OCR1A =
255;
TIMSK1=0;
//timer interrupts
//by Amanda Ghassaei
//set timer2 interrupt at 11kHz
TCCR2A = 0;//
set entire TCCR2A register to 0
TCCR2B = 0;//
same for TCCR2B
TCNT2 = 0;//initialize
counter value to 0
OCR2A = 182;//
= (16*10^6) / (11000*8) - 1 (must be <256)
11khz
// turn on CTC mode
TCCR2A |= (1 << WGM21);
// Set CS21 bit for 8 prescaler
TCCR2B |= (1 << CS21);
// enable timer compare interrupt
TIMSK2 |= (1 << OCIE2A);
sei();//allow interrupts
}//end setup
ISR(TIMER2_COMPA_vect)
{
//timer1 interrupt 11kHz toggles
if(nf==0) valor=bufer1[dout];
if(nf==1) valor=bufer0[dout];
dout++;
if(dout>99)
{
vacio=1;
dout=0;
if(nf==1)
nf=0;
else
nf=1;
}
OCR1AH = 0;
OCR1AL = valor;
//PORTC= valor;
}
void loop()
{
if(vacio == 1 && fin == 0)
{
if(nf==0) for(din=0;din<100;din++) bufer0[din]
= dataFile.read();
if(nf==1) for(din=0;din<100;din++) bufer1[din]
= dataFile.read();
vacio=0;
if(!dataFile.available())
{
Serial.println("Fin
de cancion");
for(din=0;din<100;din++){ bufer0[din]=128; bufer1[din]=128;} //vacia
bufer
fin=1;
}
}
if (stringComplete)
{
inputString.toCharArray(fileplay,19);
fileplay[inputString.length()-1]=0; //clear
\n
if(strcmp(fileplay,
"stop")
== 0)
{
Serial.println("Stop
cancion");
for(din=0;din<100;din++){ bufer0[din]=128; bufer1[din]=128;} //vacia
bufer
dataFile.close();
fin=1;
}
else
{
inputString.toCharArray(fileplay,19);
fileplay[inputString.length()-1]=0;
dataFile.close();
dataFile = SD.open(fileplay,FILE_READ);
if (dataFile)
{
Serial.print("OK
");
for(din=0;din<1000;din++) dataFile.read(); //quita
1000 bytes del wav
fin=0;
}
else
{
Serial.print("Error
");
}
Serial.println(fileplay);
}
inputString = "";
stringComplete = false;
}
}
void serialEvent()
{
while (Serial.available())
{
char inChar
= (char)Serial.read();
inputString += inChar;
if (inChar == '\n')
stringComplete = true;
}
}
|
El código fuente no está optimizado ni nada, es un ejercicio de
programación rápida que funciona.
El programa no hace ninguna comprobación de tipo de fichero wav,
ni de tasa de muestreo ni de resolución de bits, solo toma
el fichero de la SD, quita los 1000 primeros bytes y los vuelca en
el conversor D/A a una tasa de muestreo de 11KHz.
En la salida, pin 9 del arduino, sale la señal de audio que
se filtra mediante un condensador.
La tarjeta SD funciona a 3,3V, y como el arduino funciona a 5V
debemos hacer adaptar la entradas y salidas. Para no complicar el
circuito y no gastarnos dinero en un adaptador, lo realizo con
resistencias.
Es fácil y normalmente funciona bien.
Esquema eléctrico.
Circuito eléctrico montado en un adaptador de SD a MicroSD.
Diagrama del filtro de audio.
Circuito montado y funcionando.
Video del funcionamiento
Para la conversión y edición de ficheros wav, utilizo el
programa audacity.
Conversor de audio con audacity.
En el siguiente circuito crearé un interface desde el arduino para
manejar los ficheros wav.
Saludos
Juan Galaz
Bibliografía:
http://ravc00cs.blogspot.com.es/2012/07/pwm.html
https://github.com/TMRh20/TMRpcm/wiki
http://apcmag.com/arduino-project-5-digital-audio-player.htm
http://arduino-info.wikispaces.com/Arduino-PWM-Frequency
http://hykrion.com/?q=node/153
http://arduino.cc/es/Tutorial/SecretsOfArduinoPWM