Muy buenas!
Os presento mi proyecto de final de carrera que estoy desarrollando. Es un instrumento de música, inspirado en el Theremín. Es una caja con dos sensores de luz dentro y dos aperturas. Tú con las manos regulas la luz que incide en los sensores, introduciéndolas en las aperturas. Un sensor regula la nota (freqüencia), y el otro el volumen (amplitud). Funciona todo de forma analógica, por lo tanto uso un DAC MCP4725, en la salida de arduino para poder conseguir obtener una onda sinusoidal de salida.
Entonces comparto mi proyecto porqué tengo algún problema con el codigo. Usamos los timer (clocks) de arduino para convertir la señal cuadrada en sinusoidal, asta ahí bien.
Introduzco en un array las diferentes freqüencias de las notas musicale. Entonces, mediante la función "map", consigo que la freqüencia de salida sea regulada por la LDR1, según la posición que detecte LDR1 la freqüencia de salida será una u otra del array. Asta aquí también funciona bien.
El caso es, que como el instrumento funciona con luz ambiente, el nivel de luz incidente no es siempre el mismo, y antes de todo hay que hacer una fase previa de calibración para adaptar los rangos de la función "map". Lo soluciono con el led de la placa arduino i las variables ref1 y ref2 (ver codigo más abajo). También funciona, pero el caso es que se puede apreciar como la onda sinusoidal de salida pierde mucha calidad o resolución.
Y mi pregunta en este foro es, que puedo hacer si me doy cuenta de que cada vez que voy añadiendo cosas al codigo la onda sinusoidal de salida (sonido) va perdiendo calidad progresivamente cada vez que añado algo?
Muchissimas gracias de antemano.
Marcel Esteve
#include <Wire.h>
#include <Adafruit_MCP4725.h>
#include "avr/pgmspace.h"
#include <avr/interrupt.h>
#include <avr/io.h>
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
Adafruit_MCP4725 dac;
int sineVal = 0;
int tono[ ] = {261, 277, 294, 311, 330, 349, 370, 392, 415, 440,466};
// mid C C# D D# E F F# G G# A
int p = 0;
int n = 0;
int ref1 = 0;
int ref2 = 0;
int ledPin = 13;
// table of 256 sine values / one sine period / stored in flash memory
const PROGMEM byte sine256[] = {
127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,217,219,221,223,225,227,229,231,233,234,236,238,239,240,
242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,254,253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,233,231,229,227,225,223,
221,219,217,215,212,210,208,205,203,200,198,195,192,190,187,184,181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,115,111,108,105,102,99,96,93,90,87,84,81,78,
76,73,70,67,64,62,59,56,54,51,49,46,44,42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31,
33,35,37,39,42,44,46,49,51,54,56,59,62,64,67,70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124
};
double dfreq;
// const double refclk=31372.549; // =16MHz / 510
const double refclk=31376.6; // measured
// variables used inside interrupt service declared as voilatile
volatile byte icnt; // var inside interrupt
volatile unsigned long phaccu; // pahse accumulator
volatile unsigned long tword_m; // dds tuning word m
volatile int timerTick = 0;
void setup(void) {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
// For Adafruit MCP4725A1 the address is 0x62 (default) or 0x63 (ADDR pin tied to VCC)
// For MCP4725A0 the address is 0x60 or 0x61
// For MCP4725A2 the address is 0x64 or 0x65
dac.begin(0x60);
Setup_timer2();
// disable interrupts to avoid timing distortion
cbi (TIMSK0,TOIE0); // disable Timer0 !!! delay() is now not available
sbi (TIMSK2,TOIE2); // enable Timer2 Interrupt
}
void loop(void)
{
while ((ref1 == 0) and (ref2 == 0))
{
digitalWrite(ledPin, HIGH); // turn the LED on (HIGH is the voltage level)
ref1 = analogRead(A0);
Serial.print (ref1);
delay(700000); // wait for a second
digitalWrite(ledPin, LOW); // turn the LED off by making the voltage LOW
delay(500000);
digitalWrite(ledPin, HIGH);
ref2 = analogRead(A0);
Serial.print (ref2);
delay(500000);
}
if (ref2 != 0)
{
p = analogRead(A0);
n = map (p, ref1, ref2, 0, 11) ; // Ell array solo tiene 12 notas
double ValorFr= tono[n]*0.4;
dfreq=ValorFr; // initial output frequency = 1000.o Hz
tword_m=pow(2,32)*dfreq/refclk; // calulate DDS new tuning word
}
if (timerTick = 1)
{
dac.setVoltage(sineVal, false);
timerTick = 0;
}
}
//******************************************************************
// Timer2 Interrupt Service at 31372,550 KHz = 32uSec
// this is the timebase REFCLOCK for the DDS generator
// FOUT = (M (REFCLK)) / (2 exp 32)
// runtime : 8 microseconds ( inclusive push and pop)
// 125 ticks = 4 milliseconds
ISR(TIMER2_OVF_vect)
{
phaccu=phaccu+tword_m; // soft DDS, phase accu with 32 bits
icnt=phaccu >> 24; // use upper 8 bits for phase accu as frequency information
sineVal = (pgm_read_byte_near(sine256 + icnt))<<4;
timerTick = 1;
}
//******************************************************************
// timer2 setup
// set prscaler to 1, PWM mode to phase correct PWM, 16000000/510 = 31372.55 Hz clock
void Setup_timer2() {
// Timer1 Clock Prescaler to : 1
sbi (TCCR2B, CS20);
cbi (TCCR2B, CS21);
cbi (TCCR2B, CS22);
// Timer1 PWM Mode set to Phase Correct PWM
cbi (TCCR2A, COM2A0); // clear Compare Match
cbi (TCCR2A, COM2A1);
cbi (TCCR2A, WGM20); // Mode 1 / Phase Correct PWM
cbi (TCCR2A, WGM21);
cbi (TCCR2B, WGM22);
}
/**************************************************************************/
/**************************************************************************/