cancel
Showing results for 
Search instead for 
Did you mean: 

Bare Metal ADC on NUCLEO-H745ZI-Q

zirogravity
Associate III

Hello,

I am troubleshooting a similar problem as described in this post TM32H7 ADC Baremetal - Cannot get ADC module ready

I am using PF10 on ADC3 as it is already routed to the CN10 header. Intentionally ignoring CMSIS header/defines in the spirit of learning. Using the default RCC /64MHz HSI / clock and have basic uart functionality on usart3. Using CM4 and ignoring CM7.

Initializing as follows:

//Enable clock access to GPIOF
RCC->AHB4ENR |=GPIOFEN;

//Set PF10 mode to analog
//The reset state for the pins is analog mode
//GPIOF->MODER |=(1U<<20);
//GPIOF->MODER |=(1U<<21);

//Enable clock access to ADC
RCC->AHB4ENR |=ADC3EN;

//Set ADC Kernel Clock Source to Peripheral Clock
RCC->D3CCIPR &=~(1U<<16);
RCC->D3CCIPR |= (1U<<17);

//Reset the ADC peripheral
RCC->AHB4RSTR |= (1U<<24);
RCC->AHB4RSTR &=~(1U<<24);

//DISABLE THE ADC BEFORE CONFIGURATION
ADC3->CR &=~ADEN;
while(ADC3->CR & ADEN){};

//ADC channel pre-selection register
ADC3->PCSEL  |=(1U<<6);

//Set ADC Continous Conversion Mode
ADC3->CFGR |=(1U<<13);

//ADC Channel Sequence
//Channel 6 of ADC3 so we need ADC_SQR1: SQ[4:0]=6 =0b110
//Only one channel ADC_SQR1:  L[3:0]=0 (length 1)
ADC3->SQR1 &= ~(1U<<0);
ADC3->SQR1 |=  (1U<<7);
ADC3->SQR1 |=  (1U<<8);

//Set ADC Clock Mode
//00: CK_ADCx (x=1 to 23) (Asynchronous clock mode), generated at product level (refer to
//Section Reset and Clock Control (RCC))
//01: adc_sclk/1 (Synchronous clock mode).
//10: adc_sclk/2 (Synchronous clock mode)
//11: adc_sclk/4 (Synchronous clock mode)
ADC3_COMMON->CCR |=((1U<<16)|(1U<<17));

//Clear the Deep Power Down Bit
ADC3->CR &=~DEEPPWD;

//Enable the ADC VREG
ADC3->CR |= ADVREGEN;

//Wait for ADC LDO Ready Flag
while(!(ADC3->ISR & LDORDY));

//1. Clear the ADRDY bit in the ADC_ISR register by writing ‘1’
ADC3->ISR |=ISR_ADRDY;

//2. Set ADEN=1.
ADC3->CR |= ADEN;

//3. Wait until ADRDY=1 (ADRDY is set after the ADC startup time). 
while(!(ADC3->ISR & ISR_ADRDY)){};

//4. Clear the ADRDY bit in the ADC_ISR register by writing ‘1’ (optional).
//ADC3->ISR |=ISR_ADRDY;

 It appears that I get a single ADC reading on "reset" but nothing after that. I can GND or apply 3.3V on PF10 and report an ADC count value via uart.

Screenshot from 2024-11-26 18-02-05.png

My adc read function and from what I can tell EOC never gets set/reset after each read.

uint32_t adc_read(void){

	//RM0399 Rev 4 page 1034
	//Wait for conversion completion
	 while(!(ADC3->ISR & ISR_EOC)){};

	 //Wait for sequence completion
	 //while(!(ADC3->ISR & ISR_EOS));

	//Read converted results
	return (ADC3->DR);
}

I have tried various insertions of delays in the form of:

for(int i=0; i<10000;i++);

 in various section of the code per the RM but that does not seem to be it. My overall main() is:

uint32_t adc3_ch6_count;

int main(void){

	usart3_tx_init();

	pf10_adc_init();

	start_conversion(); //Start ADC Conversion ADC3->CR |=  ADSTART;
}

	while(1){

		//for (int i=0;i<10000;i++);
		adc3_ch6_count =adc_read();
		printf("\nADC3_CH6 Count: %lu\r", adc3_ch6_count);
		for(int i=0; i<10000;i++);

	}
}

 

5 REPLIES 5
zirogravity
Associate III

Hi,

I am just noticing that the LDORDY bit field is not shown in the SFR debug view for STM32CubeIDE. Bit 12 is being set correctly. This should be OK I suppose and unrelated to why my code does not get past a reset/single read?stm32LDORDYMarkUp.png

stm32LDORDYMarkUp2.png

krotti42
Associate III

Hello @zirogravity !

 

NOTE

You have written, you use the default RCC/PWR setup. So the peripheral kernel clock is 64MHz.

The maximum allowed frequency for the ADC's is 40MHz (VOS3), 60MHz (VOS2) and 160MHz (VOS1 and VOS0).  Have you changed the voltage scaling (VOS) to VOS1 with the power controller (PWR)? The default is VOS3.

 

 

 

ADC maximum allowed frequencyADC maximum allowed frequency

 

 

 

Hi @krotti42 ,

Thank you for this insight. I will have a look today, make some changes, and report back. Perhaps I misunderstood but I thought when CKMODE[1:0] is set to 01 or 11 the prescaler would be either 2 or 4 which gets further divided by 2 according to Figure 142 ADC Clock Scheme diagram. The information shared is very helpful and I will test out. I have not changed or set VOS at all.


Screenshot from 2024-11-28 08-02-52.png

zirogravity
Associate III

Hello,

I am still stuck on this as I navigate the PWR registers.

In the spirit of trying to operate ADC3 in VOS3 (default) seems like it should be possible so long as ADC clock is 40MHz or less. I set the HSI clock to 32MHz by enabling  HSIDIV to 2. I am not using any other clocks i.e. PLL etc... so I am not switching clocks and I dont believe I have to worry about peripheral clock gating but rather just selecting the correct ADC3 clock source i.e. adc_sclk vs adc_ker_clk_input.

From what I gather this will be further divided by 2 at the ADC level which is why I thought the default 64MHz HSI clock should be OK. So reducing the HSI to 32MHz has not changed the behavior I was seeing before.

The first initial sample samples OK on MCU reset but then my code gets stuck in the while loop below right after the very first sample coming out of reset. Do I actually need to enable interrupts or anything like that for the EOC to work? My understanding from the RM that ths ADC3 ISR EOC is a hardware generated interrupt thus I do not need to configure it maybe?

 

 

uint32_t adc_read(void){

	//printf("begin adc_read();\n\r");
	//RM0399 Rev 4 page 1034
	//Wait for conversion completion
	while(!(ADC3->ISR & ISR_EOC)){};

	 //Wait for sequence completion
	 //while(!(ADC3->ISR & ISR_EOS));

	//Read converted results
	return (ADC3->DR);
}

 

 

Hello again,

I managed to make progress but with little understanding of why? I forcefully clear the ADC OVERUN bit just before the EOC while() check.

I went back to setting the HSI to its default 64MHz. Either way the ADC "works" be it HSI 64MHz or 32MHz. The validity of the ADC data is a different question.

 

 

 

 

uint32_t adc_read(void){


//printf("begin adc_read();\n\r");

//RM0399 Rev 4 page 1034

//Wait for conversion completion


//Clear overrun bit but why?

//What would cause or result in an overrun?

ADC3->ISR |= ADC_ISR_OVR;


while(!(ADC3->ISR & ADC_ISR_EOC)){};


//Wait for sequence completion

//while(!(ADC3->ISR & ISR_EOS));


//Read/return converted results -->This should HW trigger EOC bit correct?

return (ADC3->DR);

}

 

 

 


Feeding a function generator output into the ADC with a 3.3V Pk-Pk sin() I can send the values over UART and observe the values changing.

Screenshot from 2024-12-29 19-02-11.png

I am unclear on what I am doing wrong that may cause the ADC to overrun? I dont think clearing the overun is the correct approach but at least it hopefully narrows down the errors of my way?

EDIT / ADD

I discovered this "AUTDLY" bit in the ADC_CFGR register which if enabled seems to prevent the ADC overun thus not requiring the clearing of the ADC overrun bit but I am still back at why?

According to the RM Rev 4 page 1001:

The ADC implements an auto-delayed conversion mode controlled by the AUTDLY
configuration bit. Auto-delayed conversions are useful to simplify the software as well as to
optimize performance of an application clocked at low frequency where there would be risk
of encountering an ADC overrun.

  1. What would be considered "clocked at low frequency"?
  2. And in this context is the application frequency with respect to the system clock i.e. 64MHz default HSI is low or is it with respect to the ADC? I suspect system clock given the ADC is seeing 32MHz which is close to its 40MHz limit in VOS3?
  3. Basically is there some documentation on this somewhere?