Skip to main content
ZJing
Associate III
October 20, 2022
Question

Interruption, but corresponding interrupt flag is not set?

  • October 20, 2022
  • 10 replies
  • 2465 views

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!

This topic has been closed for replies.

10 replies

Tesla DeLorean
Guru
October 20, 2022

>>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 (See Profile) Up vote any posts that you find helpful, it shows what's working..
ZJing
ZJingAuthor
Associate III
October 21, 2022

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.

Pavel A.
October 20, 2022

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

ZJing
ZJingAuthor
Associate III
October 21, 2022

Hi Pavel,

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

Pavel A.
October 21, 2022

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.

waclawek.jan
Super User
October 22, 2022

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
ZJingAuthor
Associate III
October 24, 2022

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)

waclawek.jan
Super User
October 24, 2022

@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

ZJing
ZJingAuthor
Associate III
October 24, 2022

Hi JW,

I see. Well, is using if(LL_ADC_IsActiveFlag_JEOC(ADC1)) to guard the ISR very safe? That is, will the situation that the NVIC is not cleared (so program enters ISR for the second time), and the JEOC flag does not get cleared either, ever happen?

I guess the real question is, how much time does it take to clear the interrupt specific JEOC flag, compared to the general interrupt flag (NVIC?)

Regarding the APB bus and ADC clocks: I assume that the APB bus should not add extra delay to this interrupt flag clearing process, since it is the same as main clock, but the ADC clock will add to the delay, since it is asynchronous to the main clock? Is this correct?

Again, thank you very much for helping!