2024-07-30 10:09 AM
Hi,
I need to use the STM32C011F4 multichannel ADC like I used to 20 years ago with other microprocessors. That is:
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
Solved! Go to Solution.
2024-07-30 01:21 PM
#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
}
}
hth
KnarfB
2024-07-30 10:16 AM
2024-07-30 10:21 AM - edited 2024-07-30 10:23 AM
And how do you select the channel you want to convert? For example:
2024-07-30 11:35 AM
You can see an example project to convert a channel in polling mode here:
2024-07-30 12:18 PM
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...
2024-07-30 01:21 PM
#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
}
}
hth
KnarfB
2024-07-31 12:39 AM