Indicador STM32 ADC EOC nunca establecido: Uso de CMSIS Core

Estoy usando Keil uvision 5 para mi microcontrolador STM32F103C8. Estoy depurando el código para el ejemplo de sondeo de un solo canal ADC usando CMSIS Core. No estoy usando ninguna biblioteca periférica estándar o HAL, pero usé la forma de STD lib haciendo el proceso de inicialización del ADC.

El proceso de calibración de mi ADC funciona correctamente y el código no se atasca en el ciclo while al realizar el proceso de calibración. Pero cuando trato de iniciar la conversión, no convierte el valor y mi código se atasca en este bucle while

while((ADC1->SR & ADC_SR_EOC)==0);

Este es mi proceso de inicialización para el ADC1

uint32_t tmpreg1 = 0;


RCC->CFGR |= RCC_CFGR_ADCPRE_DIV8;  //LINE#1772
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN | RCC_APB2ENR_IOPAEN;    //clock for ADC1 and GPIOA is active        
GPIOA->CRL &= ~(GPIO_CRL_CNF0);     //CNF0=0 will make analog input mode
GPIOA->CRL &= ~(GPIO_CRL_MODE0);    //mode bits remains zero in input mode 

tmpreg1 = ADC1->CR1;
tmpreg1 &= 0xFFF0FEFF;      //clear SCAN AND DUALMODE bits
ADC1->CR1 = tmpreg1;

/* Clear CONT, ALIGN and EXTSEL bits */
tmpreg1 = ADC1->CR2;
tmpreg1 &=0xFFF1F7FD;
/* Write to ADCx CR2 */
ADC1->CR2 = tmpreg1;

tmpreg1 = ADC1->SQR1;
/* Clear L bits */
tmpreg1 &=0xFF0FFFFF;
ADC1->SQR1 = tmpreg1;
ADC1->CR2 |= ADC_CR2_ADON;
/*
=====   
ADC1->CR2 |= ADC_CR2_ADON;  
delay_i(100);                       
ADC1->CR2 &= ~ADC_CR2_CONT;     //single conversion mode. continues disabled        
ADC1->CR2 &= ~ADC_CR2_EXTTRIG;  //DISABLE EXTERNAL TRIGGER
ADC1->CR2 &= ~ADC_CR2_ALIGN;    //MAKE RIGHT ALIGN 
=====
*/
//ADC1->CR2 = 1;
//ADC1->CR1 |= ADC_CR1_DISCEN;

/*
These bits are written by software 
to define the total number of conversions in 
the regular channel conversion sequence 
*/

//ADC1->SQR1 &= ~ADC_SQR1_L;        //ONLY ONE CONVERSION WILL BE DONE

//Calibration reset
ADC1->CR2 |= ADC_CR2_RSTCAL;    //RESET CALIBRATION 
while((ADC1->CR2 & ADC_CR2_RSTCAL)!=0); //WAIT FOR CALIBRATION RESET

//Calibrate 
ADC1->CR2 |= ADC_CR2_CAL;   
while((ADC1->CR2 & ADC_CR2_CAL)!=0);

adc_channel_config();
ADC1->CR2 &= ~ADC_CR2_ADON;

como se puede ver en el código comentado. Probé la configuración de muchas maneras diferentes, pero parece que no hay problema en esto. En algunos foros, la gente sugirió apagar el ADC antes de comenzar una nueva conversión, así que también probé esto en mi función de lectura de adc , pero no tuve suerte. Aquí está mi función de lectura adc después de la última modificación que probé.

int readADC(){


ADC1->CR2 |= ADC_CR2_ADON;
delay_i(100);
//select channel zero
//ADC1->SMPR2 |= ADC_SMPR2_SMP0;    //239.5 Cycles for channel zero 

ADC1->CR2 |= 0x00500000;    //START CONVERSION
/*now wait for end of conversion*/
while((ADC1->SR & ADC_SR_EOC)==0);  //WHILE IT is not set it mean no conversion done
/*Congratulations Conversion is complete: Now read it*/ 
ADC1->CR2 &= ~ADC_CR2_ADON;
return ADC1->DR;    
}

Una cosa que noté es que durante el proceso de depuración, el valor del registro ADC1->DR cambia cuando realizo el proceso de calibración. cambia de 0x00000000 a 0x00000073

Y también probé SWSTART (BIT:22) ** en el registro **ADC1->CR2 así como EXTTRIG (BIT:20) ** del registro **ADC1->CR2 pero los resultados son los mismos. También intenté habilitar el indicador SWSTART manualmente en el proceso de depuración, pero los resultados son los mismos. Se borró de inmediato, que es la indicación de que la conversión se inició de acuerdo con el manual de referencia que describe este bit como
Este bit lo establece el software para iniciar la conversión y lo borra el hardware tan pronto como comienza la conversión. Así que me quedé atascado y tengo que publicarlo aquí. Por cierto, aquí está la función adc_channel_config.

void adc_channel_config(){
uint32_t tmpreg1 = 0;   
/* Get the old register value */
tmpreg1 = ADC1->SMPR2;
/* Clear the old channel sample time */
tmpreg1 &= ~ADC_SMPR2_SMP0;
/* Set the new channel sample time */
tmpreg1 |= ADC_SMPR2_SMP0;
/* Store the new register value */
ADC1->SMPR2 = tmpreg1;
tmpreg1 = ADC1->SQR3;
tmpreg1 &= ~(0x0000001F);
ADC1->SQR3=tmpreg1;

}
¿Alguna vez ha intentado escribir solo ADC1->CR2=ADC_CR2_ADON; para iniciar una conversión? (tenga en cuenta el = y no |=) En cuanto a su segunda forma, debería ser 0x005E0000 y no 0x00500000. Tienes que seleccionar el SWSTART como fuente externa (por extraño que parezca).
En realidad, elimine esa primera parte del comentario, el ADC debería comenzar si ADON está configurado y escribe el mismo valor en el registro CR2 nuevamente. El ADC del F103 parece un poco extraño en este sentido. Así que justo después de hacer ADC1->CR2 |= ADC_CR2_ADON; debería iniciar la conversión.
Aunque no había probado ADC->CR2 = ADC_CR2_ADON porque había leído en el manual de referencia que si cualquier otro bit escribe al mismo tiempo que ADON, ¿no activará el ADC? Puede ser que entendí mal la oración, también intentaré esto.
Sí, me equivoqué, creo. Simplemente escribir todo el registro mientras ADON ya está configurado debería iniciar una conversión, que es lo que hace en la primera línea de readADC()
@Arsenal ¡Tenías razón!. Necesito seleccionar el SWSTART como fuente externa. Cuando lo hago con 0x005E0000, afecta el indicador EOC y el indicador pasa de 0 a 1. Pero ahora estoy notando un comportamiento extraño. Tan pronto como leo el registro ADC1->SR, la bandera vuelve a cero. Incluso traté de poner el valor del registro SR en la variable, el valor que lee la variable es 0x10; Pero antes de leer el registro, el depurador muestra que el registro SR contiene el valor de 0x12;
No estoy seguro, pero tenga en cuenta que si su depurador está leyendo ADC1->DR, restablecerá el indicador EOC. Entonces, tal vez intente detenerse solo después de estar seguro de que el ADC ha hecho su trabajo por completo o desactive la vista de registro del ADC hasta que el µC haya leído el valor de SR.
@Arsenal ¡Sí! Tenías razón otra vez. Gracias Deberías publicar esto como una respuesta. Resolvió el problema que estaba enfrentando. Todo lo que tenía que hacer era usar 0x005E0000 y luego eliminar el registro SR de la lista de observación en el depurador y el problema está resuelto.
Gracias por la respuesta. Intentaré convertir esto en una respuesta útil. No soy tan firme con el F103 como con algunas otras series de ST...

Respuestas (1)

Para iniciar una conversión, debería ser posible hacerlo en el software con:

  1. Escribiendo el valor del ADC1->CR2registro con un ADONbit establecido dos veces (la primera escritura establecerá la configuración y la segunda escritura activará la conversión). Como ya está haciendo esto, no estoy seguro de por qué no funciona.
  2. Si desea utilizar el SWSTARTbit en el registro CR2, debe seleccionarlo como fuente de activación externa con los bits EXTSEL (valor de 0b111) (resultados combinados en 0x005E0000)

En otras familias STM32, la interfaz ADC (y también otros periféricos) se mejoró para que sea más fácil de usar.


Al depurar módulos de hardware, debe pensar en cómo interactúa el depurador con el módulo.

Por lo general, el depurador usa el mismo bus para acceder a los registros del módulo de hardware (periférico), este es especialmente el caso si solo se mapea en memoria como en el STM32.

Los módulos de hardware generalmente no conocen el depurador y, por lo tanto, reaccionarán a las lecturas y escrituras como lo harían desde el procesador. Si tiene a la vista los registros en el depurador (ya sea con una ventana de observación o como una vista periférica, dependiendo de la herramienta), generalmente se actualizará a cierta velocidad o cuando realice una acción (nuevamente, depende de la herramienta).

Como consecuencia, mientras revisa su código e intenta ver qué está haciendo el módulo de hardware, la lectura de los registros puede alterar el comportamiento del módulo, ya que algunos bits se borran en una lectura de registros o la máquina de estado del módulo cambiará a otro estado. Esto interferirá con la lógica de su software.

En su caso particular, si el depurador está leyendo el registro DR del ADC, restablecerá el indicador EOC en el registro de estado, por lo que es posible que se pierda que realmente se configura.


Lo que suelo hacer si me enfrento a estos problemas es introducir variables para mantener los valores de los registros en ciertos puntos de la ejecución del programa y luego dejar que el software se ejecute hasta el final de mi método y luego empezar a mirar los valores intermedios en el variables Tenga en cuenta que aún debe preocuparse por los cambios de estado si realiza lecturas adicionales, pero generalmente funciona mejor que recorrer paso a paso el código.

Los problemas de tiempo pueden ser una bestia completamente diferente para abordar.

También tenga en cuenta que la familia STM32 generalmente tiene la capacidad de detener algunos periféricos si está deteniendo el dispositivo con un depurador. Eche un vistazo al DBGMCU_CRregistro en la sección Soporte de depuración del manual de referencia.