Estoy tratando de recuperar los valores del ADC en mi MSP430F5529 y enviarlos a mi computadora a través de USB, pero estoy empezando poco a poco. Todo lo que tengo en este momento es algo que recupera el valor de ADC y lo almacena en ADCResults, si el valor de lectura es más de la mitad de Vcc, entonces se enciende un LED.
Tengo el pin 6.0 conectado a un sensor de fuerza para poder verlo apagarse y encenderse cuando pongo el dedo sobre él o lo suelto.
El programa funciona perfectamente cuando lo ejecuto en modo de depuración, pero cuando intento ejecutarlo fuera del modo de depuración (simplemente encendiendo la placa desde la computadora después de descargar el código), no sucede nada cuando pongo el dedo en el sensor de fuerza.
Lo que es extremadamente extraño es que si mantengo presionado el botón de reinicio mientras pongo el dedo en el sensor de fuerza (poner el dedo hacia abajo hace que el LED se encienda) y suelto el botón de reinicio, el LED permanece encendido hasta que vuelva a pulsar el botón de reinicio sin quitar el dedo. por lo que parece que el reinicio está causando un problema, pero no estoy seguro de cómo.
Al principio pensé que el reinicio se estaba jalando constantemente alto (o bajo, lo que reinicia el dispositivo), pero eso no puede ser cierto porque entonces el programa debería funcionar si mantuve presionado el reinicio, ¡pero no es así!
Aquí está mi código:
#include "driverlib.h"
volatile uint16_t ADCResults = 0;
void main(void)
{
//Stop Watchdog Timer
WDT_A_hold(WDT_A_BASE);
//P6.0 ADC option select
GPIO_setAsPeripheralModuleFunctionOutputPin(
GPIO_PORT_P6,
GPIO_PIN0
);
GPIO_setAsOutputPin(
GPIO_PORT_P1,
GPIO_PIN0
);
//Initialize the ADC12_A_A Module
/*
* Base address of ADC12_A_A Module
* Use internal ADC12_A_A bit as sample/hold signal to start conversion
* USE MODOSC 5MHZ Digital Oscillator as clock source
* Use default clock divider of 1
*/
ADC12_A_init(ADC12_A_BASE,
ADC12_A_SAMPLEHOLDSOURCE_SC,
ADC12_A_CLOCKSOURCE_ADC12OSC,
ADC12_A_CLOCKDIVIDER_1);
ADC12_A_enable(ADC12_A_BASE);
/*
* Base address of ADC12_A_A Module
* For memory buffers 0-7 sample/hold for 64 clock cycles
* For memory buffers 8-15 sample/hold for 4 clock cycles (default)
* Disable Multiple Sampling
*/
ADC12_A_setupSamplingTimer(ADC12_A_BASE,
ADC12_A_CYCLEHOLD_64_CYCLES,
ADC12_A_CYCLEHOLD_4_CYCLES,
ADC12_A_MULTIPLESAMPLESDISABLE);
//Configure Memory Buffer
/*
* Base address of the ADC12_A_A Module
* Configure memory buffer 0
* Map input A0 to memory buffer 0
* Vref+ = AVcc
* Vr- = AVss
* Memory buffer 0 is not the end of a sequence
*/
ADC12_A_configureMemoryParam param = {0};
param.memoryBufferControlIndex = ADC12_A_MEMORY_0;
param.inputSourceSelect = ADC12_A_INPUT_A0;
param.positiveRefVoltageSourceSelect = ADC12_A_VREFPOS_AVCC;
param.negativeRefVoltageSourceSelect = ADC12_A_VREFNEG_AVSS;
param.endOfSequence = ADC12_A_NOTENDOFSEQUENCE;
ADC12_A_configureMemory(ADC12_A_BASE,¶m);
//Enable memory buffer 0 interrupt
ADC12_A_clearInterrupt(ADC12_A_BASE,
ADC12IFG0);
ADC12_A_enableInterrupt(ADC12_A_BASE,
ADC12IE0);
while(1)
{
//Enable/Start sampling and conversion
/*
* Base address of ADC12_A_A Module
* Start the conversion into memory buffer 0
* Use the single-channel, single-conversion mode
*/
ADC12_A_startConversion(ADC12_A_BASE,
ADC12_A_MEMORY_0,
ADC12_A_SINGLECHANNEL);
//LPM0, ADC12_A_ISR will force exit
__bis_SR_register(LPM0_bits + GIE);
//for Debugger
__no_operation();
}
}
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=ADC12_VECTOR
__interrupt
#elif defined(__GNUC__)
__attribute__((interrupt(ADC12_VECTOR)))
#endif
void ADC12_A_ISR(void)
{
switch(__even_in_range(ADC12IV,34))
{
case 0: break; //Vector 0: No interrupt
case 2: break; //Vector 2: ADC overflow
case 4: break; //Vector 4: ADC timing overflow
case 6: //Vector 6: ADC12IFG0
//Is Memory Buffer 0 = A0 > 0.5AVcc?
ADCResults = ADC12_A_getResults(ADC12_A_BASE,
ADC12_A_MEMORY_0);
if(ADCResults
>= 0x7ff)
{
//set P1.0
GPIO_setOutputHighOnPin(
GPIO_PORT_P1,
GPIO_PIN0
);
}
else
{
//Clear P1.0 LED off
GPIO_setOutputLowOnPin(
GPIO_PORT_P1,
GPIO_PIN0
);
}
//Exit active CPU
__bic_SR_register_on_exit(LPM0_bits);
case 8: break; //Vector 8: ADC12IFG1
case 10: break; //Vector 10: ADC12IFG2
case 12: break; //Vector 12: ADC12IFG3
case 14: break; //Vector 14: ADC12IFG4
case 16: break; //Vector 16: ADC12IFG5
case 18: break; //Vector 18: ADC12IFG6
case 20: break; //Vector 20: ADC12IFG7
case 22: break; //Vector 22: ADC12IFG8
case 24: break; //Vector 24: ADC12IFG9
case 26: break; //Vector 26: ADC12IFG10
case 28: break; //Vector 28: ADC12IFG11
case 30: break; //Vector 30: ADC12IFG12
case 32: break; //Vector 32: ADC12IFG13
case 34: break; //Vector 34: ADC12IFG14
default: break;
}
}
ACTUALIZAR
He intentado hacer la misma funcionalidad sin usar la biblioteca de controladores periféricos y parece funcionar perfectamente fuera del depurador. Esto me lleva a creer que algo anda mal con la biblioteca de controladores periféricos de Texas Instruments.
Aquí está el código que parecía funcionar bien fuera del depurador y no usa la biblioteca de controladores periféricos.
#include <msp430.h>
int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
ADC12CTL0 = ADC12SHT02 + ADC12ON; // Sampling time, ADC12 on
ADC12CTL1 = ADC12SHP; // Use sampling timer
ADC12IE = 0x01; // Enable interrupt
ADC12CTL0 |= ADC12ENC;
P6SEL |= 0x01; // P6.0 ADC option select
P1DIR |= 0x01; // P1.0 output
while (1)
{
ADC12CTL0 |= ADC12SC; // Start sampling/conversion
__bis_SR_register(LPM0_bits + GIE); // LPM0, ADC12_ISR will force exit
__no_operation(); // For debugger
}
}
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = ADC12_VECTOR
__interrupt void ADC12_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(ADC12_VECTOR))) ADC12_ISR (void)
#else
#error Compiler not supported!
#endif
{
switch(__even_in_range(ADC12IV,34))
{
case 0: break; // Vector 0: No interrupt
case 2: break; // Vector 2: ADC overflow
case 4: break; // Vector 4: ADC timing overflow
case 6: // Vector 6: ADC12IFG0
if (ADC12MEM0 >= 0x7ff) // ADC12MEM = A0 > 0.5AVcc?
P1OUT |= BIT0; // P1.0 = 1
else
P1OUT &= ~BIT0; // P1.0 = 0
__bic_SR_register_on_exit(LPM0_bits); // Exit active CPU
case 8: break; // Vector 8: ADC12IFG1
case 10: break; // Vector 10: ADC12IFG2
case 12: break; // Vector 12: ADC12IFG3
case 14: break; // Vector 14: ADC12IFG4
case 16: break; // Vector 16: ADC12IFG5
case 18: break; // Vector 18: ADC12IFG6
case 20: break; // Vector 20: ADC12IFG7
case 22: break; // Vector 22: ADC12IFG8
case 24: break; // Vector 24: ADC12IFG9
case 26: break; // Vector 26: ADC12IFG10
case 28: break; // Vector 28: ADC12IFG11
case 30: break; // Vector 30: ADC12IFG12
case 32: break; // Vector 32: ADC12IFG13
case 34: break; // Vector 34: ADC12IFG14
default: break;
}
}
A veces, el motivo de este comportamiento es que la configuración de optimización es diferente en el modo de depuración, y alguna variable que el compilador cree que no es necesaria se optimiza inmediatamente.
Las soluciones para esto son agregar calificadores "volátiles" a tales variables, o desactivar la optimización (o al menos rechazarla).
No sé si esta es su respuesta (el hilo se convirtió en TL; DR), pero este dato ciertamente debería aparecer como una posible solución para los motores de búsqueda.
volatile
calificador estaba allí en el primer código, por lo que aunque a menudo es un problema, en este caso noDescargo de responsabilidad: no soy un experto en MSP430.
Sugiero el uso de
ADC12_A_disableConversions()
después
ADC12_A_setupSamplingTimer()
Extracto deMSP430 DriverLib for MSP430F5xx_6xx Devices
Esta función habilita/inicia el proceso de conversión del ADC. Si la fuente de señal de muestreo/retención elegida durante la inicialización fue ADC12OSC, la conversión se inicia inmediatamente; de lo contrario, la fuente de señal de muestreo/retención elegida inicia la conversión con un flanco ascendente de la señal. Al seleccionar los modos de conversión, tenga en cuenta que para los modos secuenciados y/o repetidos, para que el proceso de muestra/mantener y convertir continúe sin un disparador de la fuente de señal de muestra/mantenimiento, las muestras múltiples deben habilitarse usando el ADC12_A_setupSamplingTimer( ) función. Tenga en cuenta que después de llamar a esta función, ADC12_A_disableConversions()debe llamarse para reinicializar el ADC, reconfigurar un control de búfer de memoria, habilitar/deshabilitar el temporizador de muestreo o cambiar el voltaje de referencia interno.
Nota: También hay algunos buenos cursos en línea gratuitos para aprender el diseño de sistemas integrados. Uno de ellos usa MSP430. Tengo una lista de algunos de ellos a continuación.
Referencias:
Me pregunto por qué funciona en modo de depuración, ha pasado un tiempo desde que trabajé con MSP430 y no estoy familiarizado con driverlib. Pero:
GPIO_setAsPeripheralModuleFunctionOutputPin(
GPIO_PORT_P6,
GPIO_PIN0
);
Seguramente no es la función que desea usar para cambiar este pin a una entrada analógica, ¿o sí? intentaría:
GPIO_setAsPeripheralModuleFunctionIntputPin(
GPIO_PORT_P6,
GPIO_PIN0
);
Pero como se ve en la descripción de las funciones del pin (gracias @CL.) Queda claro que configurar el pin en la función periférica será suficiente y se ignorará la dirección. Por lo tanto, es engañoso, pero no un factor decisivo.
Luego, hay una pequeña cosa param.endOfSequence = ADC12_A_NOTENDOFSEQUENCE;
que probablemente debería ser param.endOfSequence = ADC12_A_ENDOFSEQUENCE;
, pero como solo realiza una conversión de un solo canal, no debería importar (es solo un poco más claro). Y probablemente cambie ADC12IFG0
y ADC12IE0
a ADC12_A_IFG0
y ADC12_A_IE0
(pero solo se definen para los otros valores, por lo que no hay problema funcional)
Y le falta un break;
después de su caso en la tabla de vectores de interrupción, pero eso tampoco afectará mucho al programa, solo una fuente para futuros errores.
Entonces, desde la perspectiva del firmware, solo tengo pequeños detalles.
Basado en los comentarios, y leyendo la hoja de erratas , me hace preguntarme si una sola __no_operation();
después __bic_SR_register_on_exit(LPM0_bits);
de la ISR resolvería el problema. La fe de erratas no menciona explícitamente el caso presente aquí, pero hay problemas relacionados con la configuración de modos de bajo consumo, la salida de modos de bajo consumo y la corrupción del contador del programa. Así que tal vez sea otro caso. Es posible que esos efectos no estén presentes durante la depuración, ya que el módulo de emulación interfiere con la ejecución normal del núcleo.
Pero también mencionó que si borra la interrupción global, su programa también funciona. Lo que me lleva a pensar que su programa se atasca en una interrupción pero no en el ADC. No tiene que borrar el indicador de interrupción del ADC, ya que eso se hace automáticamente al leer la memoria del ADC.
Solo otra nota sobre la programación, obtendría el análisis del valor ADC del ISR. Manténgalos lo más pequeños posible, simplemente lea el valor en su variable global y deje el modo de bajo consumo al salir. Haga todas las demás cosas en su aplicación principal. Hay casos en los que necesitará una latencia de interrupción lo más corta posible y si hace cosas dentro del ISR bloqueará otras interrupciones (excepto que habilite interrupciones anidadas, pero esas también son malas).
DigitalNinja
Asmyldof
Aarón
Aarón
Asmyldof
DigitalNinja
DigitalNinja
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
para detener el perro guardián.Mahendra Gunawardena
Transeúnte
Transeúnte
Mahendra Gunawardena
Aarón
CL.
ADC12_A_clearInterrupt()
yADC12_A_enableInterrupt()
, no está usando los símbolos que la documentación dice que debe usar, pero esto en realidad no importa.CL.
Aarón
Aarón
Aarón
Aarón
ADC12_A_getResults
que se llame. Lo enciendo sin importar qué (lo que significa que no es parte de la instrucción if), y se enciende bien Entonces sé que al menos está activando el ISRAarón
DigitalNinja
__bis_SR_register(LPM0_bits + GIE);
está haciendo esta línea. Sin embargo, ¿no necesita borrar la interrupción antes de iniciar otra conversión? Como en, llamarADC12_A_clearInterrupt(ADC12_A_BASE,ADC12IFG0);
después de que se haya completado la conversión. Tal vez es por eso que solo lee el ADC una vez directamente después del reinicio.Aarón
__bis_SR_register(LPM0_bits + GIE)
está haciendo exactamente, ya que ya estaba en el código de ejemplo, pero creo que configura la MCU en modo de bajo consumo y habilita las interrupciones para sacarlo del modo de bajo consumo. Curiosamente, el código ingresa al ISR fuera del modo de depuración, pruebo esto haciendo que un LED cambie cada vez que ingresa al ISR y poniendo algunos ciclos de retraso para que pueda verlo parpadear. ¡Esto hace que todo el programa funcione correctamente!Aarón
Aarón
+ GIE
en el argumento de la llamada__bic_SR_register_on_exit
que se llama justo antes de que salga el ISR, ¡todo el programa funciona bien! Simplemente no sé qué están haciendo estas funciones.DigitalNinja
GIE
que las interrupciones globales están habilitadas. Entonces parece que sin el+ GIE
en esa línea (__bic_SR_register_on_exit
), las interrupciones se deshabilitan. Agregarlo habilita las interrupciones y, por lo tanto, obtiene la siguiente interrupción de ADC al volver a ingresar al bucle principal.Aarón
+ GIE
que el programa funcionara, siempre que retrasara el ISRDigitalNinja
+ GIE
no está en esa línea en el ISR, se llamaría en el ciclo principal (__bis_SR_register(LPM0_bits + GIE)
) y volvería a habilitar las interrupciones globales si se hubieran deshabilitado. Entonces, en realidad es solo la demora lo que lo arregló, como acabas de señalar. ¿Dónde pusiste el retraso en el ISR y cuánto es el retraso?DigitalNinja
Aarón
__bis_SR_register(LPM0_bits + GIE)
línea después de que regresó el ISR y, por lo tanto, configuró el GIE, ¿por qué la demora haría que main lo volviera a habilitar pero no tuviera una causa de demora? main para no volver a habilitarlo. El retraso se ubicó directamente antes de laif(ADCResults >= 0x7ff)
declaración dentro del ISRAarón
CL.
ADC12IV
registro.Aarón
DigitalNinja
__bis_SR_register(LPM0_bits + GIE)
habilita interrupciones globales en su ciclo principal, independientemente del retraso o de la otra línea en el ISR. No sé por qué un retraso allí lo soluciona. No creo que debas necesitarlo. Entonces, algo más está sucediendo, tal vez con la configuración de ADC. ¿Ha buscado ayuda en los foros de TI o ha cambiado su código para que coincida exactamente con un ejemplo?CL.
DigitalNinja
ADC12_A_CYCLEHOLD_64_CYCLES
paraADC12_A_setupSamplingTimer
que también seaADC12_A_CYCLEHOLD_4_CYCLES
y elimine su código de retraso.Aarón
Aarón
+ GIE
llamada de ISR de__bic_SR_register_on_exit
. Entonces sé que las bibliotecas adicionales no están causando el problema, simplemente no puedo entender por qué la demora tiene el mismo efecto que agregar+GIE
Aarón
CL.
DigitalNinja
Aarón
Transeúnte
__bis_SR_register(LPM0_bits + GIE)
establece el modo de bajo consumo 0 y los bits de activación de interrupción global en el registro especial que controla el modo de suspensión y las interrupciones globales. El retraso después de configurar la interrupción y el sueño es para evitar que se atasque en el sueño.