cancel
Showing results for 
Search instead for 
Did you mean: 

Interruption, but corresponding interrupt flag is not set?

ZJing
Associate III

Dear community,

I am having a strange problem with the STM32L431.

The ADC is set up for use with both the DMA and interruption. This is to de-interleave the adc results from 2 different channels, since there was problem with interleaved ADC results when only using DMA -- The order of ADC results in the DMA buffer would get messed up after running for a while for some unknown reason, so it was impossible to tell which result is from which channel. Therefore, I changed my code and started to use the ADC with DMA and interruption together.

Part of current ADC initializing code:

	adc_reg_config.Channel = ADC_CHANNEL_1;
	adc_reg_config.Rank = ADC_REGULAR_RANK_1;
	adc_reg_config.SingleDiff = ADC_DIFFERENTIAL_ENDED;
	adc_reg_config.SamplingTime = ADC_SAMPLETIME_24CYCLES_5;
 
	HAL_ADC_ConfigChannel(p_adc_handle, &adc_reg_config);
 
	adc_inj_config.InjectedChannel = ADC_CHANNEL_VREFINT;
	adc_inj_config.InjectedRank = ADC_INJECTED_RANK_1;
	adc_inj_config.InjectedSamplingTime = ADC_SAMPLETIME_24CYCLES_5;
	adc_inj_config.InjectedSingleDiff = ADC_SINGLE_ENDED;
	adc_inj_config.InjectedNbrOfConversion = 1;
	adc_inj_config.AutoInjectedConv = ENABLE;
	adc_inj_config.ExternalTrigInjecConv = ADC_INJECTED_SOFTWARE_START;
	adc_inj_config.InjecOversamplingMode = ENABLE;
	adc_inj_config.InjecOversampling = adc_inj_osp_config;
	
	HAL_ADCEx_InjectedConfigChannel(p_adc_handle, &adc_inj_config);
	
	HAL_ADCEx_Calibration_Start(p_adc_handle, ADC_DIFFERENTIAL_ENDED);
	HAL_ADCEx_Calibration_Start(p_adc_handle, ADC_SINGLE_ENDED);
 
	LL_ADC_EnableIT_JEOC(p_adc_handle->Instance);
	LL_ADC_EnableIT_OVR(p_adc_handle->Instance);
	LL_ADC_DisableIT_JEOS(p_adc_handle->Instance);
	LL_ADC_DisableIT_JQOVF(p_adc_handle->Instance);
	LL_ADC_DisableIT_EOC(p_adc_handle->Instance);
	LL_ADC_DisableIT_EOS(p_adc_handle->Instance);
	LL_ADC_DisableIT_EOSMP(p_adc_handle->Instance);
	LL_ADC_DisableIT_ADRDY(p_adc_handle->Instance);
	
	HAL_NVIC_EnableIRQ(ADC1_IRQn);
 
	HAL_ADC_Start_DMA(p_adc_handle, (uint32_t*)reg_buf, BL);

Problem: I get interrupt, but none of the two enabled ADC interrupt flag (JEOC and OVR) is set.

Enabled interrupts:0693W00000UoSQbQAN.pngThis breakpoint is reached once in a while (which means that there is ADC interruption, but neither the JEOC nor the OVR flag is set, right?)

0693W00000UoSQqQAN.png 

Please help! Thanks in advance!

10 REPLIES 10

>>This breakpoint is reached once in a while (which means that there is ADC interruption, but neither the JEOC nor the OVR flag is set, right?)

Well, it is possible to re-enter the IRQHandler once it exits if the Write Buffers, and NVIC haven't cleared, and the MCU is trying to decide where to go next in the tail-chaining process. This is a hazard on a pipe-lined processor with deferred write completion. Perhaps have more meat in there, or use a __DSB() to fence the situation.

I'd probably just increment a count, and leave. Read the ADC1->ISR into a variable.

I think reading JDR1 should clear the interrupt source there. Doing nothing about the overrun is probably not going to clear that condition.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Pavel A.
Evangelist III

What if you just return if no enabled flag is set? Will the program continue or loop in ADC1_IRQHandler again?

Hi Tesla,

Thanks for the detailed explaination of what happens behind the scene, it is very useful knowledge.

I followed your advice of using a __DSB() line, because I am not understanding the other option of having more meat (add more code into the interrupt function so it takes longer to execute?) Could you explain what you would do with ADC1_IRQHandler() with code?

I googled __DSB() and added it into my code(not sure if i'm using it correctly):

void ADC1_IRQHandler() 
{
	if(LL_ADC_IsActiveFlag_JEOC(ADC1))
	{
		inj_buf[BL - DMA1_Channel1->CNDTR] = ADC1->JDR1;
		__DSB();
	}
	else if(LL_ADC_IsActiveFlag_OVR(ADC1))
	{
		LL_ADC_ClearFlag_OVR(ADC1);
		__DSB();
	}
	else
	{
		__NOP();
	}
}

However, the same problem persists.

Hi Pavel,

It works fine if I do that. However, I do want to find out what the root cause it and solve it.

TL;DR the root cause is that a __DSB is not enough. Or not always enough.

Clearing the interrupt reliably can be complicated, or add more delay in average.

If the repeated interrupt occurs less than, say, in 10% and does not cause other problems, IMHO it isn't worth chasing it.

The root problem is, that the time it takes to propagate the ADC flag clear (here: indirect through register read, which may involve extra time/clocks) into NVIC may be longer than the time it takes for processor to start to exit the ISR and re-evaluate pending interrupts upon that exit (in order to tail-chain interrupts, as Clive said above).

This may be more pronounced if the APB bus where ADC sits has a clock different. In the particular case of ADC, as it's a dual-clock device, maybe in some cases also the choice of ADC-core clock influences this issue. In both cases, clock resynchronizing circuits may be in the way of interrupt signal from ADC to NVIC. I am curious: can you please share how are these two things (APB divider, ADC clock selection) set in your case?

JW

ZJing
Associate III

Hello JW,

Thanks for your reply! The article is really helpful, too.

Below is my APB divider and ADC clock selection:

  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;

I have another question though:

If by the time my program exits ISR, and re-enters ISR because the ADC flag is not cleared yet, how is the flag to the specific interrupt already cleared? (In this case JEOC)

ZJing
Associate III

Hi Pavel,

I'm going to do that and just use the if statement to guard it. Thanks for your answer!

@ZJing​ , hi,

Thanks for letting me know of your setup.

The flag (JEOC) *is* cleared at that point, as the test (if(LL_ADC_IsActiveFlag_JEOC(ADC1))

) proves.

The problem is, that it takes several machine cycles until this clear propagates from ADC to NVIC. And as NVIC at the moment of determining pending interrupt still "sees" the old status of ADC interrupt line, it restarts the ISR again.

JW