cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F0 input capture interrupt quirk

alister
Lead

Bare-metal STM32F030C8.

Using input capture to receive encodes.

Encodes are delimited by idle.

I need to decode only the last 10 or so transitions of each encode.

I want to perform the decode during the idle.

So I’m using circular DMA and I want the timer interrupt to

  1. Fire on the first update following captures (on timer overflow), where I’ll signal the app do the decode and change the interrupt to capture, so it fires on the start of the next encode
  2. Fire on the first capture, where I’ll change the interrupt to update, i.e. two interrupts per received encode.

But what I’m observing in the interrupt is

  1. In the capture interrupt, I switch to interrupt to update successfully, i.e. I see only one capture interrupt.
  2. But I get two or three update interrupts in succession.

This is the instrumented IRQ handler.

EDIT replaced Cube TIM_FLAG macros with "CMSIS-mandated" TIM_SR macros.

void TIMx_IRQHandler(void)
{
  TIM_TypeDef *htimxInstance_p = _htimxInstance_p;
  uint16_t sr = htimxInstance_p->SR;
  // for testing
  uint16_t debugDier = htimxInstance_p->DIER;
 
  /* Clear the SR bits as early as possible.
   * Observe interrupts with SR = 0. So appears there’s latency from clearing SR to the
   * peripheral's clearing its interrupt. */
  htimxInstance_p->SR = 0;
 
  /* TIMx Capture event. */
  if (sr & TIM_SR_CC1IF)
  {
    /* Switch the interrupt to update.
     * The next interrupt will be after the encode has completed. */
    htimxInstance_p->DIER = TIM_DIER_CC1DE | TIM_DIER_UIE;
  }
 
  /* TIM Update event, i.e., the timer counter has overflowed. */
  else if (sr & TIM_SR_UIF)
  {
    /* This interrupt indicates the encode is completed.
     * Switch the interrupt to capture.
     * The next interrupt will be when the next encode has started.
     * Incrementing encodeCount signals the app to do the decode. */
    htimxInstance_p->DIER = TIM_DIER_CC1DE | TIM_DIER_CC1IE;
    encodeCount++;
  }
 
  // for testing
  debugSr[debugSrIdx].dier = debugDier;
  debugSr[debugSrIdx].sr = sr;
  if (++debugSrIdx >= ARRAY_SIZE(debugSr))
    debugSrIdx = 0;
}

Perhaps I might work-around it by adding state to the interrupt to signal to the app on the first update following capture.

But I’d like to fix it if possible. Any clues please?

45 REPLIES 45

TIM15_CR1->URS alone doesn't fix my problem.

When I'd first added "TIM15->CR1 |= TIM_CR1_URS" to my init I observed the number of spurious timer interrupts reduce to only two or three per encode-capture cycle.

But I soon found removing or adding instrumentation code increases the spurious interrupts significantly, and in one case adding instrumenting causes the timer interrupt to always throw SR = 0x44 while DIER = 0x202, and the predominate spurious interrupt state in the other cases too.

So still investigating.

By "instrumenting" I mean saving some state to a ring-buffer for post-analysis.

It's probably time to ask you to produce a minimal but complete compilable example exhibiting the problem, so that it could be reproduced elsewhere.

JW

berendi
Principal

@Community member​ The update interrupt gets indeed enabled at the first capture. Then the capture and update events occur at the same time, and the code handles only the capture event when both are present in the flags.

@alister​ Does it work with URS set?

Using a timer with multiple DMA requests, i.e. TIM1 or TIM3, you can update DIER through DMA requests on update and on CH2 capture set to TI1 (CC2S = 0b10: CC2 channel is configured as input, IC2 is mapped on TI1). This would of course consume a significant portion of the few DMA channel resources of the MCU.

berendi
Principal

Or take a STM32G0 instead, which supports Combined reset + trigger mode. Used in conjunction with one-pulse mode, I think it's exactly what you are trying to do.

Hi @berendi​ ,

Thanks for the comment.

> Then the capture and update events occur at the same time

I don't think so. Most of the records are like this:

debugSr[4] debugSr_t {...}

dier uint16_t 0x202

sr uint16_t 0x45

i.e. CC1 enabled in DIER, but in SR only Update is set (and CC2 but that's probably just left at default ie. compare at 0; and Trigger).

My concern is, that while DIER AND SR == 0, which is the case in most of the records, interrupt should not happen at all... Am I overlooking something?

Jan

It is not yet clear to me what exactly happens, but consider this scenario:

  1. TIM_DIER_CC1IE is set
  2. Capture interrupt is triggered
  3. SR is cleared (=0)
  4. Next capture occurs
  5. DIER is changed, TIM_DIER_CC1IE cleared, TIM_DIER_UIE set
  6. Interrupt handler is finished, and immediately entered again
  7. Oops, capture flag is set in SR, but cleared in DIER.

But this is just an idea I'm going to verify now.

>My concern is, that while DIER AND SR == 0, which is the case in most of the records, interrupt should not happen at all...

DIER is 0x0201 when I'm waiting for the encode to finish, or 0x202 when I'm waiting for the next encode to start. The intention of the DIER cycle is to _reduce_ interrupts.

The transitions are captured by circular DMA. This should work well as I don't care how many transitions there are in the encode. I just want the last ones, so I dimension the DMA buffer for that.

The interrupt is just to delineate the end of the encode and then signal the app to do the decode.

I want to turn the timer OFF there too, because the idle between encodes may be short and the next encode's DMA may corrupt the capture buffer.

I'd turn it back ON after the decode's finished. It shouldn't matter if it's turned ON late, again I only care about the encode's tail.

> capture flag is set in SR, but cleared in DIER.

> But this is just an idea I'm going to verify now.

Thanks!

Frustratingly, I had _only_ two or three spurious TIM interrupts per cycle earlier today and didn't commit because I was sure I was wrapping it up.

Now it's more and if I change what I instrument, I can make then all spurious!

I'll post details shortly.

alister
Lead

This is most of the code.

There's some Cube before ProcInit that initialises PSC = 0, up-counting, ARR = 65535 (later I want this much lower), CR1->CKD = 0, CR1->ARPE = 0, RCR = 0.

#define _TIMx           TIM15
#define _TIM_CH         1
#define _DMAx           DMA1
#define _DMAxCHy        DMA1_Channel5
#define _DMA_CH         5
#define TIMx_IRQHandler TIM15_IRQHandler
 
void ProcInit(void)
{
  TIM_TypeDef *timx_p = _TIMx;
  DMA_TypeDef *dmax_p = _DMAx;
  DMA_Channel_TypeDef *dmaxChy_p = _DMAxCHy;
 
  /* Disable the TIM peripheral and prevent the Slave Controller's Update interrupts.
   * Alister> Observed in testing these operate independent of TIM_DIER_UIE and cannot be masked by
   * TIMx_DIER. */
  timx_p->CR1 = TIM_CR1_URS;
 
  /* Enable Slave Reset-Mode to clear the TIM counter after each capture.
   * From https://community.st.com/s/question/0D50X00009XkiAZSAZ/reset-timer-counter-on-input-capture. */
  timx_p->SMCR = (TIM_TS_TI1FP1 | TIM_SLAVEMODE_RESET);
 
  /* Start The TIM Input Capture in DMA mode. */
  /* Clear all DMA flags */
  dmax_p->IFCR = (DMA_IFCR_CGIF1 << ((_DMA_CH - 1) * 4));
 
  /* Configure DMA Channel data length */
  dmaxChy_p->CNDTR = ARRAY_SIZE(_captureBuff);
 
  /* Configure DMA Channel source address (peripheral) */
  dmaxChy_p->CPAR = (uint32_t)&timx_p->CCR1;
 
  /* Configure DMA Channel destination address (memory) */
  dmaxChy_p->CMAR = (uint32_t)_captureBuff;
 
  /* Disable DMA interrupts */
  dmaxChy_p->CCR &= ~(DMA_CCR_TEIE | DMA_CCR_HTIE | DMA_CCR_TCIE);
 
  /* Enable the Peripheral */
  dmaxChy_p->CCR |= DMA_CCR_EN;
 
  /* Enable the Input Capture */
  timx_p->CCER |= (TIM_CCER_CC1E << ((_TIM_CH - 1) * 4));
 
  /* Enable the TIM Capture 1 DMA request and Update interrupt. */
  timx_p->DIER = (TIM_DIER_CC1DE | TIM_DIER_UIE);
 
  /* Enable the TIM peripheral */
  timx_p->CR1 |= TIM_CR1_CEN;
}

This is the interrupt.

// for testing
uint32_t captureCount, otherCount;
typedef struct
{
  uint16_t dier;
  uint16_t sr;
} debugSr_t;
debugSr_t debugSr[32];
int debugSrIdx;
 
void TIMx_IRQHandler(void)
{
  TIM_TypeDef *timx_p = _TIMx;
  uint16_t dier = timx_p->DIER;
  uint16_t sr = timx_p->SR;
  uint16_t srValid = dier & sr;
 
  /* Clear the SR bits as early as possible.
   * Observing interrupts with SR = 0.
   * Must be latency between clearing SR to the peripheral's clearing its interrupt. */
  timx_p->SR = 0;
 
  /* TIMx Capture event. */
  if (srValid & TIM_SR_CC1IF)
  {
    /* Switch the interrupt to update.
     * The next interrupt _should_ be after the encode has completed. */
    timx_p->DIER = TIM_DIER_CC1DE | TIM_DIER_UIE;
    captureCount++; // for testing
  }
 
  /* TIM Update event, i.e., the timer counter has overflowed. */
  else if (srValid & TIM_SR_UIF)
  {
    /* This interrupt indicates the encode is completed.
     * Switch the interrupt to capture.
     * The next interrupt _should_ be when the next encode starts.
     * Increment "updateCount" to signal ProcProcess to do the decode. */
    timx_p->DIER = TIM_DIER_CC1DE | TIM_DIER_CC1IE;
    updateCount++;
  }
 
  // for testing
  else
  {
    otherCount++;
  }
 
  // for testing
  debugSr[debugSrIdx].dier = dier;
  debugSr[debugSrIdx].sr = sr;
  if (++debugSrIdx >= ARRAY_SIZE(debugSr))
    debugSrIdx = 0;
}

This is what's instrumented:

captureCount	uint32_t	0x6	        <-- total encodes observed
updateCount	volatile uint16_t	0x7	  <-- total decodes stared (first is spurious update)
otherCount	uint32_t	0x36	        <-- total spurious
debugSr	debugSr_t [32]	0x20000078	
	debugSr[0]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x46	          <-- encode started
	debugSr[1]	debugSr_t	{...}	
		dier	uint16_t	0x201	
		sr	uint16_t	0x44	
	debugSr[2]	debugSr_t	{...}	
		dier	uint16_t	0x201	
		sr	uint16_t	0x45            <-- start decode
	debugSr[3]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x44	
	debugSr[4]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x44	
	debugSr[5]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x44	
	debugSr[6]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x44	
	debugSr[7]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x46	          <-- encode started
	debugSr[8]	debugSr_t	{...}	
		dier	uint16_t	0x201	
		sr	uint16_t	0x44	
	debugSr[9]	debugSr_t	{...}	
		dier	uint16_t	0x201	
		sr	uint16_t	0x45	          <-- start decode
	debugSr[10]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[11]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x44	
	debugSr[12]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x44	
	debugSr[13]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x44	
	debugSr[14]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x44	
	debugSr[15]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x44	
	debugSr[16]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x46	          <-- encode started
	debugSr[17]	debugSr_t	{...}	
		dier	uint16_t	0x201	
		sr	uint16_t	0x44	
	debugSr[18]	debugSr_t	{...}	
		dier	uint16_t	0x201	
		sr	uint16_t	0x45	          <-- start decode
<snip>

I'd observed too, several times, if I added one more member to the debugSr_t struct, I'd never see a TIM_SR_UIF, only SR = 0x44s. But it's not manifesting for me now.

alister
Lead

In reply to @berendi​ 's earlier question

> Does it work with URS set?

This is what's instrumented by the above code posted today except without CR1->URS:

captureCount	uint32_t	0xb	        <-- total encodes observed
updateCount	volatile uint16_t	0xc	  <-- total decodes stared (first is spurious update)
otherCount	uint32_t	0x74	        <-- total spurious
debugSr	debugSr_t [32]	0x20000078	
	debugSr[0]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[1]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[2]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[3]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[4]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[5]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[6]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[7]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[8]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[9]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[10]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[11]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[12]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[13]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[14]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[15]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[16]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[17]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x47	          <-- encode started
	debugSr[18]	debugSr_t	{...}	
		dier	uint16_t	0x201	
		sr	uint16_t	0x45	          <-- start decode
	debugSr[19]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[20]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[21]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[22]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[23]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[24]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[25]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x47	          <-- encode started
	debugSr[26]	debugSr_t	{...}	
		dier	uint16_t	0x201	
		sr	uint16_t	0x45	          <-- start decode
	debugSr[27]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x47	          <-- encode started
	debugSr[28]	debugSr_t	{...}	
		dier	uint16_t	0x201	
		sr	uint16_t	0x45	          <-- start decode
	debugSr[29]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[30]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
	debugSr[31]	debugSr_t	{...}	
		dier	uint16_t	0x202	
		sr	uint16_t	0x45	
debugSrIdx	int	11 (Decimal)