cancel
Showing results for 
Search instead for 
Did you mean: 

how to use the ADC the old-fashioned way.

Gaston
Senior

Hi,

I need to use the STM32C011F4 multichannel ADC like I used to 20 years ago with other microprocessors. That is:

  • Select ADC channel (0, 1, 5 or 6)
  • Start ADC conversion
  • Wait for EOC
  • Read ADC value
  • Stop ADC

In this application I don't want to use DMA or Interrupts. Can someone please help me with this? I'm not able to find any example of this basic operation.

gaston

1 ACCEPTED SOLUTION

Accepted Solutions
KnarfB
Principal III
#include <stm32c011xx.h> // global variables can be read by cortex-debug live watch or STM32CubeMonitor uint32_t adc_data_raw; // raw 12-bit ADC result uint32_t adc_data_mV; // in millivolt int main(void) { RCC->IOPENR |= RCC_IOPENR_GPIOAEN; // enable peripheral clock (void)RCC->IOPENR; // read back to make sure that clock is on // Set PA8 to analog input mode (mode 3) for ADC, other regs defaults are okay GPIOA->MODER = (GPIOA->MODER & ~GPIO_MODER_MODE8_Msk) | (3 << GPIO_MODER_MODE8_Pos); // let ADC (digital block) be clocked by: SYSCLK RCC->CCIPR = (RCC->CCIPR &~RCC_CCIPR_ADCSEL_Msk) | (0<<RCC_CCIPR_ADCSEL_Pos); RCC->APBENR2 |= RCC_APBENR2_ADCEN; // turn ADC clock on (void)RCC->APBENR2; // read back to make sure that clock is on ADC1->CR |= ADC_CR_ADVREGEN; // power up ADC voltage regulator // wait t_ADCVREG_STUP (ADC voltage regulator start-up time), for(volatile int i=0; i<12*20; ++i); // min 20 µs see data sheet // do self calibration ADC1->CR |= ADC_CR_ADCAL; while(ADC1->CR & ADC_CR_ADCAL); // wait for calibration to finish uint8_t calibration_factor = ADC1->DR; ADC1->CFGR1 = 0; // default config after reset ADC1->CFGR2 = 0; // default config after reset ADC1->SMPR = 0; // sampling time register, default after reset // "enable the ADC" procedure from RM0490 Rev 3: ADC1->ISR |= ADC_ISR_ADRDY; // Clear the ADRDY bit in ADC_ISR register ADC1->CR |= ADC_CR_ADEN; // Set ADEN = 1 in the ADC_CR register. while(!(ADC1->ISR & ADC_ISR_ADRDY)); // Wait until ADRDY = 1 in the ADC_ISR register ADC1->CALFACT = calibration_factor; // above: CHSELRMOD = 0 in ADC_CFGR1, so every channel has a bit. set bit to activate that channel ADC1->CHSELR = ADC_CHSELR_CHSEL8; // select channel ADC_IN8 which is PA8 connected to joystick while(!(ADC1->ISR & ADC_ISR_CCRDY)); // wait until channel configuration update is applied uint32_t Vdda_mV = 3300; // there are better ways to estimate Vdda while(1) { ADC1->CR |= ADC_CR_ADSTART; // start ADC conversion while(!(ADC1->ISR & ADC_ISR_EOC)); // wait for end of conversion adc_data_raw = ADC1->DR; // conversion done. store result adc_data_mV = (adc_data_raw * Vdda_mV) / 4095; // Vdda == 4095 digital reading } }
View more

hth

KnarfB

View solution in original post

6 REPLIES 6

@Gaston wrote:

In this application I don't want to use DMA or Interrupts


So "Polling", then:

AndrewNeil_0-1722359756741.png

 

And how do you select the channel you want to convert? For example:

  • Select ADC channel 0
  • Start ADC conversion
  • Wait for EOC
  • Read ADC value
  • Stop ADC
  • Select ADC channel 4
  • Start ADC conversion
  • Wait for EOC
  • Read ADC value
  • Stop ADC

 

TDK
Guru

You can see an example project to convert a channel in polling mode here:

STM32CubeF4/Projects/STM32F413ZH-Nucleo/Examples/ADC/ADC_RegularConversion_Polling/Src/main.c at d7cb2bb74395b7f27e137da8adb5ad25f0aa0e78 · STMicroelectronics/STM32CubeF4 (github.com)

 

If you feel a post has answered your question, please click "Accept as Solution".

Hi,

just - how i would do it (kiss - Keep it Short and Simple :) )

setup with Cube the ADCx (adc1 if you want) regular conversion , set ch0 and sample time (7.5) , one conversion only.

(short "sequence" ) - gen. code , look at the program:  

HAL_ADC_Start(&hadc1, ...)  will convert this channel, ch0 , thats what you want.

 

But now you want ch4 : so just look, how Cube made the adc_init :

 

/** Configure Regular Channel */ sConfig.Channel = ADC_CHANNEL_0; sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SingleDiff = ADC_SINGLE_ENDED; sConfig.SamplingTime = ADC_SAMPLETIME_7CYCLES_5; sConfig.OffsetNumber = ADC_OFFSET_NONE; sConfig.Offset = 0; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); }

 

For ch4 : so just look, how Cube made the adc_init, copy it, and change it to ch4 .

 

ADC_ChannelConfTypeDef sConfig = {0}; /** Configure Regular Channel */ sConfig.Channel = ADC_CHANNEL_4; sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SingleDiff = ADC_SINGLE_ENDED; sConfig.SamplingTime = ADC_SAMPLETIME_7CYCLES_5; sConfig.OffsetNumber = ADC_OFFSET_NONE; sConfig.Offset = 0; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); }

 

call/run this then...ch4 is now the"sequence" .

HAL_ADC_Start(&hadc1, ...)  will convert this channel, ch4 , thats what you want now.

And so on...

If you feel a post has answered your question, please click "Accept as Solution".
KnarfB
Principal III
#include <stm32c011xx.h> // global variables can be read by cortex-debug live watch or STM32CubeMonitor uint32_t adc_data_raw; // raw 12-bit ADC result uint32_t adc_data_mV; // in millivolt int main(void) { RCC->IOPENR |= RCC_IOPENR_GPIOAEN; // enable peripheral clock (void)RCC->IOPENR; // read back to make sure that clock is on // Set PA8 to analog input mode (mode 3) for ADC, other regs defaults are okay GPIOA->MODER = (GPIOA->MODER & ~GPIO_MODER_MODE8_Msk) | (3 << GPIO_MODER_MODE8_Pos); // let ADC (digital block) be clocked by: SYSCLK RCC->CCIPR = (RCC->CCIPR &~RCC_CCIPR_ADCSEL_Msk) | (0<<RCC_CCIPR_ADCSEL_Pos); RCC->APBENR2 |= RCC_APBENR2_ADCEN; // turn ADC clock on (void)RCC->APBENR2; // read back to make sure that clock is on ADC1->CR |= ADC_CR_ADVREGEN; // power up ADC voltage regulator // wait t_ADCVREG_STUP (ADC voltage regulator start-up time), for(volatile int i=0; i<12*20; ++i); // min 20 µs see data sheet // do self calibration ADC1->CR |= ADC_CR_ADCAL; while(ADC1->CR & ADC_CR_ADCAL); // wait for calibration to finish uint8_t calibration_factor = ADC1->DR; ADC1->CFGR1 = 0; // default config after reset ADC1->CFGR2 = 0; // default config after reset ADC1->SMPR = 0; // sampling time register, default after reset // "enable the ADC" procedure from RM0490 Rev 3: ADC1->ISR |= ADC_ISR_ADRDY; // Clear the ADRDY bit in ADC_ISR register ADC1->CR |= ADC_CR_ADEN; // Set ADEN = 1 in the ADC_CR register. while(!(ADC1->ISR & ADC_ISR_ADRDY)); // Wait until ADRDY = 1 in the ADC_ISR register ADC1->CALFACT = calibration_factor; // above: CHSELRMOD = 0 in ADC_CFGR1, so every channel has a bit. set bit to activate that channel ADC1->CHSELR = ADC_CHSELR_CHSEL8; // select channel ADC_IN8 which is PA8 connected to joystick while(!(ADC1->ISR & ADC_ISR_CCRDY)); // wait until channel configuration update is applied uint32_t Vdda_mV = 3300; // there are better ways to estimate Vdda while(1) { ADC1->CR |= ADC_CR_ADSTART; // start ADC conversion while(!(ADC1->ISR & ADC_ISR_EOC)); // wait for end of conversion adc_data_raw = ADC1->DR; // conversion done. store result adc_data_mV = (adc_data_raw * Vdda_mV) / 4095; // Vdda == 4095 digital reading } }
View more

hth

KnarfB

Great! This is just what I needed. Many thanks also to AScha.3 and TDK

Gaston