0
\$\begingroup\$

I am using Keil uvision 5 for my STM32F103C8 microcontroller. I am debugging the code for ADC single channel polling example using CMSIS Core. I am not using any Standard peripheral library or HAL but I had used the way of STD lib doing the initialization process of the ADC.

My ADC Calibration process properly work and code do not stuck into while loop when doing the calibration process. But when I try to start conversion it do not convert the value and my code stuck in this while loop

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

This is my initialization process for the 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;

as you can see in the commented code. I had tried the settings in bunch of different ways but that seems no issue in this. In few Forums people suggested turn of The ADC before starting a new conversion so I also tried this in my adc read function but no luck. Here is my adc read function after the last modification I tried.

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;    
}

One thing that I had noticed that during the debugging process the value of ADC1->DR register changes when I do the calibration process. it change from 0x00000000 to 0x00000073

And also I had tried SWSTART (BIT:22) ** in **ADC1->CR2 register as well as EXTTRIG (BIT:20) ** of the **ADC1->CR2 register but results are the same. I also tried enabling SWSTART flag manually in debug process but results are the same. It got cleared immediately which is the indication that conversion is started according to the reference manual which describes this bit as
This bit is set by software to start conversion and cleared by hardware as soon as conversion starts. So any I got stuck and have to post it here. By the way here is the adc_channel_config function.

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;

}
\$\endgroup\$
8
  • 1
    \$\begingroup\$ Have you ever tried only writing ADC1->CR2=ADC_CR2_ADON; to start a conversion? (note the = and not |=) As for your second way, it should be 0x005E0000 and not 0x00500000. You have to select the SWSTART as external source (strangely enough). \$\endgroup\$
    – Arsenal
    May 7, 2020 at 14:15
  • \$\begingroup\$ Actually scrap that first part of the comment, the ADC should start if ADON is set and you write the same value to the CR2 register again. The ADC of the F103 seems a bit strange in this regard. So right after you do ADC1->CR2 |= ADC_CR2_ADON; it should start the conversion. \$\endgroup\$
    – Arsenal
    May 7, 2020 at 14:23
  • \$\begingroup\$ Although I had not tried ADC->CR2 = ADC_CR2_ADON because I had read it in reference manual that if any other bit write the same time the ADON the will not trigger the ADC? May be I misunderstood the sentence I will try this as well. \$\endgroup\$ May 7, 2020 at 16:16
  • \$\begingroup\$ Yeah, I got that wrong I think. Just writing the whole register back while ADON is already set should start a conversion, which is what you do in the first line of readADC() \$\endgroup\$
    – Arsenal
    May 8, 2020 at 6:59
  • \$\begingroup\$ @Arsenal You were right!. I need to select the SWSTART as external source. When I do it with 0x005E0000 it does effect the EOC flag and the flag go to 1 from 0. But now I am noticing a strange behavior. As soon as I read ADC1->SR register the flag goes back to zero. Even I tried to put value of SR register into variable the value the variable read is 0x10; But before reading the register, debugger shows that SR register holds the value of 0x12; \$\endgroup\$ May 8, 2020 at 11:36

1 Answer 1

1
\$\begingroup\$

To start a conversion it should be either possible to do this in software with:

  1. Writing the value of the ADC1->CR2 register with a set ADON bit twice (first write will set the settings and the second write will trigger the conversion). As you are already doing this, I'm not sure why it doesn't work.
  2. If you want to use the SWSTART bit in the CR2 register, you have to select is as the external trigger source with the EXTSEL bits (value of 0b111) (combined results in 0x005E0000)

In other STM32 families the ADC interface (and other peripherals as well) was improved to make it easier to use.


While debugging hardware modules, you have to think about how the debugger interacts with the module.

Usually the debugger uses the same bus to access the registers of the hardware module (peripheral), this is especially the case if it is just memory mapped like in the STM32.

Hardware modules usually don't know about the debugger and thus will react to reads and writes like they would from the processor. If you have the registers in the debugger in view (either with a watch window or as a peripheral view, depending on the tool) it will usually update at a certain rate or when you perform an action (again depends on the tool).

As a consequence while you step through your code and try to see what the hardware module is doing the read of the registers can alter the behavior of the module as some bits get cleared on a read of registers or the state machine of the module will switch to another state. This will interfere with the logic in your software.

In your particular case if the debugger is reading the DR register of the ADC, it will reset the EOC flag in the status register, so you might miss that it actually gets set.


What I usually do if I face this issues is to introduce variables to hold the values of the registers at certain points of the program execution and then let the software run until the end of my method and then start to look at the intermediate values in the variables. Note that you still have to worry about state changes if you do additional reads, but it usually works out better than stepping through the code.

Timing issues can be a completely different beast to tackle.

Also note, that the STM32 family usually has the ability to stop some peripherals if you are stopping the device with a debugger. Have a look at the DBGMCU_CR register in the Debug Support section of the reference manual.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.