Uso del temporizador MSP430G2452 + ADC

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.

== Contexto ==

Ahora, mi concepto de detección de tono se basa libremente en cruces por cero. Estoy contando la cantidad de picos que obtengo al realizar un seguimiento de 3 muestras a la vez. Si la muestra del medio es mayor que las dos de 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).

Probé 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 la velocidad deseada de, por ejemplo, 2 kHz. Para esto, estoy usando una interrupción de temporizador que habilita e inicia la conversión de ADC. La interrupción del 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, he notado que el temporizador no funciona como se esperaba para conteos muy bajos.

Digamos que tengo el reloj temporizador a 12kHz (VLOCLK) y la cuenta como 6 para obtener una interrupción cada 1/2000 seg. Configuré un LED para alternar la interrupción del temporizador para poder verlo. Naturalmente, la frecuencia es tan rápida que el LED debería parecer completamente brillante. Pero en cambio, el LED parpadea una vez a los 4 o 5 segundos. Parece como si el temporizador estuviera contando hasta el valor más alto y volviendo 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 aproximado 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 muy 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é fue tener todo dentro de un ciclo while. Algo como a continuación:

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 arriba de uno. Pero lo he puesto en caso de que alguno de ustedes pueda detectar algún defecto o indicarme una forma mejor de hacerlo.

Respuestas (2)

El MSP430G2452 tiene tres errores del temporizador A en ambas revisiones, cualquiera de los cuales podría estar afectándolo. Se pueden leer en la Errata del chip. Creo que tal vez el error TA12:

Se pierde la interrupción (ACLK lento):
el contador Timer_A está funcionando con reloj lento (TACLK o ACLK externo) en comparación con MCLK. El modo de comparación se selecciona para el canal de captura/comparación y el registro CCRx se incrementa en uno con la interrupción de comparación que ocurre (si TAR = CCRx). Debido al MCLK rápido, el incremento del registro CCRx (CCRx = CCRx+1) ocurre antes de que el contador Timer_A haya incrementado nuevamente. Por lo tanto, la próxima interrupción de comparación debe ocurrir inmediatamente con el siguiente incremento del contador Timer_A (si TAR = CCRx + 1). Esta interrupción se pierde.

Solución alternativa: cambie el modo de captura/comparación al modo de captura antes de que aumente el registro CCRx. Vuelva al modo de comparación después.

Incluso si este no es el caso, puede solucionarlo fácilmente. Está utilizando el reloj maestro predeterminado de 1 MHz. Puede cambiar esto a 2 MHz o 4 MHz con un simple cambio de código, momento en el cual el mismo divisor produciría un VLO de 24KHz o 48Khz. Esto le permitiría configurar un contador de tiempo un poco más alto, de nuevo 12 o 24, evitando el problema de la cuenta más baja. Por supuesto, se necesitan pruebas para estar seguros.

Su plan alternativo también funciona. Si no está demasiado preocupado por la baja potencia, los retrasos simples funcionarían. Por supuesto, como esto se ejecuta en el reloj maestro de 1 Mhz, deberá calcular un retraso apropiado y darse cuenta de que habrá una ligera desviación.

Parece que está poniendo el temporizador en modo continuo ( MC_2), en cuyo caso el temporizador contará hasta 65535 antes de volver a 0. Interrumpirá cada vez que llegue a 6 (o 12000 o cualquiera que sea el valor de TA0CCR0), pero no restablecerá el contador a 0 en ese caso, lo que significa que la primera interrupción puede ocurrir en 6 tics, pero el período de la interrupción será 65536.

Creo que desea el modo Up ( MC_1), que restablecerá el contador cuando alcance el valor objetivo.

También es posible que desee borrar el temporizador ( TACLR) al encenderlo, solo para saber que el temporizador comienza desde cero.