cancel
Showing results for 
Search instead for 
Did you mean: 

STM32G0 ADC AWD2/AWD3 does not work

mabe_rs
Associate II

Hello everyone,

we have a STM32G070CBT6 microcontroller and we want to use the ADC in the following configuration:

  • Channels: 1,2,3
  • Continuous Measurement, automatically restart at channel 1 after measuring channel 3
  • Transfer measured analog values via DMA to an array
  • Monitor Channel 1 with Analog Window Watchdog 1
  • Monitor Channel 2 and 3 with Analog Window Watchdog 2 or 3

Since I wasn't able to configure Scan conversion mode in combination with continuous conversion in STM32CubeMX (CubeMX always disables continuous conversion mode and enables dicontinuous mode as soon as I enable Scan conversion mode), I configured the ADC myself with the following code:

static void ADC_Init(void)
{
	/** Configure the ADC with CMSIS since the HAL can not configure the ADC as intended**/
	//Reset ADC
	RCC->APBRSTR2 |= RCC_APBRSTR2_ADCRST;
	RCC->APBRSTR2 &= ~RCC_APBRSTR2_ADCRST;
 
	//Activate ADC Clock
	RCC->APBENR2 |= RCC_APBENR2_ADCEN;
 
	//Run calibration
	ADC1->CR |= ADC_CR_ADCAL; //Start calibration
	while((ADC1->CR & ADC_CR_ADCAL) == ADC_CR_ADCAL); //Wait for completion
 
	//After reset, the resolution is already set to 12 bit
	//After reset, the ADC uses the asynchronous clock with a prescaler of 1.
 
	//Configure the DMA Requests from the ADC
	ADC1->CFGR1 |= ADC_CFGR1_DMACFG; //Configure DMA in circular mode
	ADC1->CFGR1 |= ADC_CFGR1_DMAEN; //Enable DMA Requests
 
	//Enable the ADC
	ADC1->ISR |= ADC_ISR_ADRDY; //Clear ADRDY-Bit
	ADC1->CR |= ADC_CR_ADEN; //Activate ADC
	while((ADC1->ISR & ADC_ISR_ADRDY) != ADC_ISR_ADRDY); //Wait until ADC is ready
 
	//Configure the Channels and the sequencer
	ADC1->ISR |= ADC_ISR_CCRDY; //Clear CCRDY Flag
	ADC1->CFGR1 |= ADC_CFGR1_CHSELRMOD; //Enable the sequencer
	ADC1->CHSELR = 0xF321; //Scan direction: CH1->CH2->CH3
	while((ADC1->ISR & ADC_ISR_CCRDY) != ADC_ISR_CCRDY); //Wait until new configuration is established
 
	//Configure the ADC for continuous conversion
	ADC1->CFGR1 |= ADC_CFGR1_CONT;
 
	/** Configure the Analog Watchdogs**/
	//Analog Watchdog 1 monitors Channel 1
	ADC1->CFGR1 |= ADC_CFGR1_AWD1SGL; //Monitor only a single channel
	ADC1->CFGR1 |= 1<<ADC_CFGR1_AWD1CH_Pos; //Monitor Channel 1
	ADC1->CFGR1 |= ADC_CFGR1_AWD1EN; //Enable the Analog Window Watchdog
	ADC1->IER |= ADC_IER_AWD1IE; //Enable Analog Window Watchdog 1 interrupt
	ADC1->AWD1TR = 2000<<ADC_AWD1TR_HT1_Pos; //Program upper threshold
	//Lower threshold stays at reset value (0)
 
	//Analog Watchdog 2 monitors Channel 2 and 3
	ADC1->AWD2CR = ADC_AWD2CR_AWD2CH_2 | ADC_AWD2CR_AWD2CH_3; //The second watchdog monitors Channels 2 and 3
	ADC1->IER |= ADC_IER_AWD2IE; //Enable Analog Watchdog 2 interrupt
	ADC1->AWD2TR = 700<<ADC_AWD2TR_HT2_Pos; //Program upper threshold
	//Lower threshold stays at reset value (0)
 
	NVIC_EnableIRQ(ADC1_IRQn); //Enable ADC interrupts in the NVIC
}

I have separate functions for initilaizing the DMA and setting up the GPIO pins, but since everything works except for the AWD2, I am not showing this part of the code.

If I initialize the ADC with the code above, everything works except for the Analog Watchdog 2. I have the following code in my ISR:

void ADC1_IRQHandler(void)
{
	adc_isr_flag = 1;
 
	if((ADC1->ISR & ADC_ISR_AWD1) == ADC_ISR_AWD1)
	{
		HAL_GPIO_WritePin(STATUS_LED_1_GPIO_Port, STATUS_LED_1_Pin, GPIO_PIN_SET); //Turn on an LED to indicate that the upper threshold is reached
		ADC1->IER &= ~ADC_IER_AWD1IE; //Disable the interrupt to avoid calling the ISR repeatedly
		ADC1->ISR |= ADC_ISR_AWD1; //Clear Interrupt flag
	}
	else if((ADC1->ISR & ADC_ISR_AWD2) == ADC_ISR_AWD2)
	{
		HAL_GPIO_WritePin(STATUS_LED_2_GPIO_Port, STATUS_LED_2_Pin, GPIO_PIN_SET); //Turn on an LED to indicate that the upper threshold is reached
		ADC1->AWD2CR = 0x00000000; //Disable the AWD2 to avoid calling the ISR repeatedly
		ADC1->ISR |= ADC_ISR_AWD2; //Clear Interrupt flag
	}
}

When I connect at potentiometer to the analog input 1, than the Status LED 1 turns on if I turn the potentiometer beyond a certain position. This shows that the AWD1 and the ISR are working as expected.

If I however connect the same potentiometer to channels 2 and 3 and turn it, the LED never turns off, even if I turn the potentiometer from one end to the other.

I already tried polling the AWD2 flag in the main loop and disabling ADC interrupts, but this did not work either. It seems like the flag is never set. I also tried sending a message over UART instead of turning on LEDs, but this did not change the situation.

I checked the HAL driver to see how the initilization is done there, but the only difference is that the HAL resets the AWD2 flag after setting up the channels. I tried resetting the flag as well after setting up the channels, but this did not work.

I also created a separate test project in the STM32CubeIDE. I configured a single channel, enabled continuous measurement and set up the AWD2. What I find strange is that I can not specify the channel to be monitored by the AWD2. I still tried it but it did not work as well. After checking the code, I realized that CubeMX does not call the HAL_ADC_AnalogWDGConfig() function, which should configure the Analog Watchdog, thus the AWD does not get configured. If I check the AWD2CR register, I can see that it only contains zeros (reset value), thus the AWD does not monitor any channels.

I even tried using the AWD3 instead of the AWD2, but still no luck.

At this point, I do not know what the problem is. Does anyone have a test code for the AWD2 or was able to configure it correctly using CubeMX?

We need the AWD in our application to react to a voltage change as fast as possible.

According to the Errata, there are no known limitations regarding the AWD2.

I would appreciate any help. Thanks in advance.

Best Regards

2 REPLIES 2
mabe_rs
Associate II

I noticed today that there was an update available for the CubeG0-Package (V1.6). I installed the update and also updated STM32CubeIDE from 1.9 to 1.10. Now the configuration for the ADC is working properly (I can configure the ADC in continuous and scan conversion mode at the same time) and the AWD2 is also working. The problem seems to be solved.

mabe_rs
Associate II

After some some experimentation I realized that the ADC and AWD2 might work correctly with the new updated version of the G0-Package/CubeIDE, but I had issues with the DMA interrupts. I could not disable the unwanted interrupts (Half Trasfer Complete/Transfer Complete) which lead to the microcontroller being stuck in the DMA interrupt handler all the time.

So I went back to my bare-metal intialization function for the ADC and DMA. After a while of trying I found the reason why the AWD2 was not working. You have to initilize the AWD2 beforce calibrating or enabling the ADC! This is not mentioned anywhere in the reference manual, but I found it out after desparate trying.

Here is the complete initilization function which now intilizes the AWD2 correctly:

    static void ADC_Init(void)
    {
    	/** Configure the ADC with CMSIS since the HAL can not configure the ADC as intended**/
    	//Reset ADC
    	RCC->APBRSTR2 |= RCC_APBRSTR2_ADCRST;
    	RCC->APBRSTR2 &= ~RCC_APBRSTR2_ADCRST;
     
    	//Activate ADC Clock
    	RCC->APBENR2 |= RCC_APBENR2_ADCEN;
     
//Analog Watchdog 2 monitors Channel 2 and 3
    	ADC1->AWD2CR = ADC_AWD2CR_AWD2CH_2 | ADC_AWD2CR_AWD2CH_3; //The second watchdog monitors Channels 2 and 3
    	ADC1->IER |= ADC_IER_AWD2IE; //Enable Analog Watchdog 2 interrupt
    	ADC1->AWD2TR = 700<<ADC_AWD2TR_HT2_Pos; //Program upper threshold
    	//Lower threshold stays at reset value (0)
 
    	//Run calibration
    	ADC1->CR |= ADC_CR_ADCAL; //Start calibration
    	while((ADC1->CR & ADC_CR_ADCAL) == ADC_CR_ADCAL); //Wait for completion
     
    	//After reset, the resolution is already set to 12 bit
    	//After reset, the ADC uses the asynchronous clock with a prescaler of 1.
     
    	//Configure the DMA Requests from the ADC
    	ADC1->CFGR1 |= ADC_CFGR1_DMACFG; //Configure DMA in circular mode
    	ADC1->CFGR1 |= ADC_CFGR1_DMAEN; //Enable DMA Requests
     
    	//Enable the ADC
    	ADC1->ISR |= ADC_ISR_ADRDY; //Clear ADRDY-Bit
    	ADC1->CR |= ADC_CR_ADEN; //Activate ADC
    	while((ADC1->ISR & ADC_ISR_ADRDY) != ADC_ISR_ADRDY); //Wait until ADC is ready
     
    	//Configure the Channels and the sequencer
    	ADC1->ISR |= ADC_ISR_CCRDY; //Clear CCRDY Flag
    	ADC1->CFGR1 |= ADC_CFGR1_CHSELRMOD; //Enable the sequencer
    	ADC1->CHSELR = 0xF321; //Scan direction: CH1->CH2->CH3
    	while((ADC1->ISR & ADC_ISR_CCRDY) != ADC_ISR_CCRDY); //Wait until new configuration is established
     
    	//Configure the ADC for continuous conversion
    	ADC1->CFGR1 |= ADC_CFGR1_CONT;
     
    	/** Configure the Analog Watchdogs**/
    	//Analog Watchdog 1 monitors Channel 1
    	ADC1->CFGR1 |= ADC_CFGR1_AWD1SGL; //Monitor only a single channel
    	ADC1->CFGR1 |= 1<<ADC_CFGR1_AWD1CH_Pos; //Monitor Channel 1
    	ADC1->CFGR1 |= ADC_CFGR1_AWD1EN; //Enable the Analog Window Watchdog
    	ADC1->IER |= ADC_IER_AWD1IE; //Enable Analog Window Watchdog 1 interrupt
    	ADC1->AWD1TR = 2000<<ADC_AWD1TR_HT1_Pos; //Program upper threshold
    	//Lower threshold stays at reset value (0)
     
    	NVIC_EnableIRQ(ADC1_IRQn); //Enable ADC interrupts in the NVIC
    }

@ST, please mention this in the reference manual!

I hope that this information is useful to someone.

Regards