cancel
Showing results for 
Search instead for 
Did you mean: 

Reference value and sequence sampling in STM32U585. ADC problem?

MariuszG
Associate II

Hello everyone,

I'm having some trouble using the ADC in the STM32U585. I developed my own PCB where the MCU's VDD and VDDA are connected to 1.8V. I need to measure about ten analog signals. I have two main problems.

First:

VREF+ = VDDA = VDD = 1.8V. When I measure the voltage in single conversion mode, I get the wrong internal reference value. When there is no oversampling and shift(OVSR, OVSS bits) the reference value is around 80dec. When OVSR and OVSS are set to 64 samples and 6-bit shift the voltage reference is approximately 1102dec. When I set the SMPR clock delay for the reference channel, it gives me a value of 0dec. An intermediate solution can be a setting that turns on VREFBUFF and sets the reference to 1.8V and separte AVDD and VREF+ pins . This procedure brings a good result, however, if I am not mistaken, it will require measuring the reference voltage and entering this value into the program to get the highest possible precision of measurements. The code below shows the measurement in single converted mode.

static void ADC1_Init(void){
 
	RCC -> AHB2ENR1 |= RCC_AHB2ENR1_ADC12EN;	//enable ADC clock
	RCC -> AHB3ENR |= RCC_AHB3ENR_ADC4EN;
	RCC -> APB3ENR |= RCC_APB3ENR_VREFEN;
	PWR -> SVMCR |= PWR_SVMCR_AVM1EN;	//enable supply voltage monitoring
 
	while((PWR -> SVMSR & PWR_SVMSR_VDDA1RDY) == 0){}
 
	PWR ->SVMCR |= PWR_SVMCR_ASV;
 
	ADC1 -> CR &= ~(ADC_CR_DEEPPWD);	//deep power down disable
	ADC1 -> CR |= ADC_CR_ADVREGEN;		//LDO regulator enable
 
	while((ADC1 -> ISR & ADC_ISR_LDORDY) == 0){}	//wait until LDO is ready
 
	ADC4_COMMON -> CCR |= ADC_CCR_VREFEN | ADC_CCR_PRESC_2;
 
	ADC1 -> CFGR2 |= ADC_CFGR2_OVSR_6 | ADC_CFGR2_ROVSE;	//x64 oversampling
	ADC1 -> CFGR2 |= ADC_CFGR2_OVSS_2 | ADC_CFGR2_OVSS_1;	//6-bit right shift
 
	ADC1 -> CR |= ADC_CR_ADCALLIN;	//calibrate offset and linear
	ADC1 -> CR |= ADC_CR_ADCAL;		//start calibration procedure
 
	while((ADC1 -> CR & ADC_CR_ADCAL) != 0){}//wait until calibration is pending
 
	ADC1 ->IER |= ADC_IER_EOCIE;	//EOC interrupt enable
 
	ADC1 -> CR |= ADC_CR_ADEN;		//enable ADC
 
	while((ADC1 -> CR & ADC_ISR_ADRDY) == 0){}	//wait until ADC is ready
 
	HAL_NVIC_SetPriority(ADC1_IRQn, 0, 0);
 
}
uint32_t ADC_Sample(uint8_t channel){
 
	ADC1 -> PCSEL = (1 << channel);
	ADC1 -> SQR1 = (channel << 6)  | 0x0000;
	HAL_NVIC_EnableIRQ(ADC1_IRQn);
	ADC1 -> CR |= ADC_CR_ADSTART;
 
	return 0;
}
void ADC1_IRQHandler(void)
{
	if((ADC1 -> ISR & ADC_ISR_EOC) == ADC_ISR_EOC){
			ADC_tab[sample_counter++] = ADC1 -> DR;
	}
}

My question is whether it is possible to correctly measure VREFINT (ADC channel 0) with the power supply VDDA=VREF+= 1.8V? If so what am I doing wrong?

Second:

VDDA=VDD=VREF+=1.8V

AHB = 128MHz, ADC clock = AHB/4 (ADC_CCR_PRESC_2 bit)

In regular sequence mode, the problem is that my ten-element table is populated completely randomly. The values ​​of the measured voltages appear in random fields. In the debugger mode, an interrupt is always called, but the

if((ADC1 -> ISR & ADC_ISR_EOC) == ADC_ISR_EOC)

condition is not always met, even though the EOC bit is set in the ADC1-ISR register view. It's look like the debugger sees a different EOC state than "if" condition. Even if I set 10-11 conversion:

ADC1 -> PCSEL |= 0x00000FBF;

ADC1 -> SQR1 |= 0x0308100A;

ADC1 -> SQR2 |= 0x09207144;

ADC1 -> SQR3 |= 0x000002CA;

ADC_ISR_EOS bit is set randomly after 2-4 conversions (EOC

static void ADC1_Init(void){
 
	RCC -> AHB2ENR1 |= RCC_AHB2ENR1_ADC12EN;//enable ADC clock
	RCC -> AHB3ENR |= RCC_AHB3ENR_ADC4EN;
	RCC -> APB3ENR |= RCC_APB3ENR_VREFEN;
	PWR -> SVMCR |= PWR_SVMCR_AVM1EN;//enable supply voltage monitoring
 
	while((PWR -> SVMSR & PWR_SVMSR_VDDA1RDY) == 0){}
 
	PWR ->SVMCR |= PWR_SVMCR_ASV;
 
	VREFBUF -> CSR = VREFBUF_CSR_ENVR | VREFBUF_CSR_VRS_0;
 
	ADC1 -> CR &= ~(ADC_CR_DEEPPWD);//deep power down disable
	ADC1 -> CR |= ADC_CR_ADVREGEN;	//LDO regulator enable
 
	while((ADC1 -> ISR & ADC_ISR_LDORDY) == 0){}//wait until LDO is ready
 
	ADC4_COMMON -> CCR |= ADC_CCR_VREFEN | ADC_CCR_PRESC_2;
 
	ADC1 -> CFGR1 |= ADC_CFGR1_CONT;//CONT = 1
 
	ADC1 -> CFGR2 |= ADC_CFGR2_OVSR_6 | ADC_CFGR2_ROVSE;	//x64 oversampling
	ADC1 -> CFGR2 |= ADC_CFGR2_OVSS_2 | ADC_CFGR2_OVSS_1;	//6-bit right shift
	ADC1 -> SMPR1 |= 0x2DB6DB6D;//sample time 0x110 68ADC clock cycles
	ADC1 -> SMPR2 = 0x2DB6DB6D;	//sample time 0x110 68ADC clock cycles
 
	ADC1 -> CR |= ADC_CR_ADCALLIN;//calibrate offset and linear
	ADC1 -> CR |= ADC_CR_ADCAL;//start calibration procedure
 
	while((ADC1 -> CR & ADC_CR_ADCAL) != 0){}//wait until calibration is pending
 
	ADC1 ->IER |= ADC_IER_EOCIE;	//EOC interrupt enable
 
	ADC1 -> CR |= ADC_CR_ADEN;	//enable ADC
 
	while((ADC1 -> CR & ADC_ISR_ADRDY) == 0){}	//wait until ADC is ready
 
	HAL_NVIC_SetPriority(ADC1_IRQn, 0, 0);
 
}
 
uint32_t ADC_Sample(uint8_t channel){
 
	ADC1 -> PCSEL |= 0x00000FBF;
	ADC1 -> SQR1 |= 0x0308100A; 
	ADC1 -> SQR2 |= 0x09207144;
	ADC1 -> SQR3 |= 0x000002CA;
	HAL_NVIC_EnableIRQ(ADC1_IRQn);
	ADC1 -> CR |= ADC_CR_ADSTART;
 
	return 0;
 
}
 
void ADC1_IRQHandler(void)
{
	if((ADC1 -> ISR & ADC_ISR_EOC) == ADC_ISR_EOC){
			ADC_tab[sample_counter++] = ADC1 -> DR;
			if(sample_counter == 10){
				HAL_NVIC_DisableIRQ(ADC1_IRQn);
				ADC1 -> CR |= (ADC_CR_ADSTP);
				ADC_stat = 1;
			}
	}
 
	ADC1 -> ISR = 0x10;//OVR bit, only one conversion wihout this line
}

Thank you in advance for any suggestion.

1 REPLY 1
Mohamed Aymen HZAMI
ST Employee

Hello @MariuszG​ and welcome to the community,

Make sure that the Vref pin is connected to 1v8 (when Vrefbuff is not used)

Aymen