Estoy tratando de construir un afinador de guitarra simple usando MSP430G2452. La señal de la guitarra vendrá a través de sus pastillas, a través de un amplificador y un filtro de 1kHz que construí usando LM358.
== Context ==
Ahora, mi concepto de detección de tono se basa libremente en cero cruces. Estoy contando la cantidad de picos que obtengo al hacer un seguimiento de 3 muestras a la vez. Si la muestra del medio es mayor que las dos en los lados, es un pico. De esta manera, cuento la ocurrencia de los picos y encuentro el tono (que puede ser el fundamental o uno de los armónicos).
He probado esto usando notas de guitarra grabadas y Python en mi PC, y parece funcionar en teoría.
== Ahora, mi problema ==
Quiero que el ADC se active a mi velocidad deseada, por ejemplo, 2kHz. Para esto, estoy usando una interrupción de temporizador que habilita e inicia la conversión de ADC. La interrupción ADC procesa mi muestra y vuelve a configurar el temporizador. Se supone que este proceso cíclico me da una muestra cada 1/2000 segundos.
Sin embargo, me he dado cuenta de que el temporizador no funciona como se espera para conteos muy bajos.
Digamos que tengo el reloj del temporizador a 12kHz (VLOCLK) y el recuento como 6 para obtener una interrupción cada 1/2000 seg. Configuré un LED para alternar en la interrupción del temporizador para poder verlo. Naturalmente, la frecuencia es tan rápida que el LED parece estar completamente encendido. Pero en cambio, el LED parpadea una vez a los 4 o 5 segundos. Parece que el temporizador está contando todo el camino hasta el valor más alto, y regresando al 6, y solo entonces se genera una interrupción.
Esto solo sucede si el valor del contador es demasiado bajo. Tengo un código en bruto a continuación. Esta es una versión un poco más antigua que la actual que tengo. Pero debería dar una buena idea de lo que estoy tratando de lograr. (En el código a continuación, el temporizador se ha configurado para interrumpir una vez por segundo, pero no funciona bien para los intervalos bajos que quiero)
#include <msp430g2452.h>
#define TIMER_COUNT 12000
volatile unsigned int value = 0;
void configClocks();
void configTimer();
void configADC();
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; //Stop Watchdog
P1DIR = 0xFF;
P1DIR &= ~BIT5;
P1SEL |= BIT5;
configClocks();
configTimer();
configADC();
TACCR0 = TIMER_COUNT;
TACTL |= MC_2;
__enable_interrupt();
while (1)
{
}
}
void configClocks()
{
//Set basic clock
DCOCTL = CALDCO_1MHZ; //DCO 1mhz
BCSCTL1 = CALBC1_1MHZ; //Aclk 1mhz
BCSCTL2 = SELM_0 + DIVM_0 + SELS + DIVS_0; //Master 1 Mhz, SMCLK = VLO = 12
}
void configTimer()
{
TACTL = TASSEL_2 + ID_0 + MC_0; // SMLCK, No div, Stopped
TACCTL0 = CCIE;
}
void configADC()
{
ADC10CTL1 = INCH_5 + ADC10SSEL_1;
ADC10CTL0 = SREF_0 + ADC10SHT_3 + ADC10ON + ADC10IE;
ADC10AE0 |= BIT5;
}
// Timer A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A (void)
{
P1OUT ^= 0x01;
ADC10CTL0 |= ENC + ADC10SC;
}
//ADC10 interrupt routine
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR (void)
{
value = ADC10MEM;
P1OUT ^= BIT6;
TACCR0 += TIMER_COUNT; //Set timer count
//__bic_SR_register_on_exit(CPUOFF); // Return to active mode
}
== Otro enfoque que estoy pensando ==
Podría haber algo que estoy pasando por alto, o mi idea en sí misma podría ser fundamentalmente defectuosa. El otro plan que pensé era tener todo dentro de un bucle while. Algo como abajo:
while (1)
{
//delay for 1/2000 time
//enable ADC
//SLEEP (will be woken up by ADC interrupt
value = ADC10MEM
//do something meaningful with the value
}
Tengo el intento de encima de uno. Pero lo he puesto en el caso de que alguno de ustedes pueda capturar cualquier falla en el mismo, o indicarme una mejor manera de hacerlo.