Skip to main content
alister
Senior III
April 6, 2020
Solved

STM32F0 input capture interrupt quirk

  • April 6, 2020
  • 45 replies
  • 8637 views

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?

This topic has been closed for replies.
Best answer by waclawek.jan

> I _do_ see a CC1IF

That's coincidental - you don't have DMA set to circular, or there are several edges on the input signal too close to each other, or something else I didn't consider.

Set CC2 to capture the *other* channel (i.e. that it captures the same signal on the same pin, TIM15_CCMR1.CC2S=0b10), and for interrupt use CC2 instead of CC1 (both in DIER and then in all handling within the ISR).

JW

45 replies

waclawek.jan
Super User
April 6, 2020

Humm.

Is this a free-running timer, or do you have any slave-mode reset imposed?

And what do you see in debugSr[]?

[stylistics]

> TIM_FLAG_UPDATE

I suspected where does this come from and went directly to the Cube/HAL TIM header.

IMHO you should get rid of the whole Cube burden once for all and stick to the CMSIS-mandated device header, or this might bite you in future when the cubers decide to change meaning/content of their haphazardly invented constants.

[/stylistics]

JW

alister
alisterAuthor
Senior III
April 6, 2020

It's free-running.

I'm doing a slave-mode reset lifted from another of your posts on Community.

Half expected a comment about cube :)

Acknowledge that risk.

waclawek.jan
Super User
April 6, 2020

> TIMx_IRQHandler()

How is this inserted to the vector table, btw?

JW

alister
alisterAuthor
Senior III
April 6, 2020

It's TIM15_IRQHandler on a board I'm using for prototyping.

#if PROTO_ON_MOX
#define _TIMx htim15
#define TIMx_IRQHandler TIM15_IRQHandler
#else
#define _TIMx htim1
#define TIMx_IRQHandler TIM1_CC_IRQHandler
#endif

waclawek.jan
Super User
April 6, 2020

> I'm doing a slave-mode reset

So, those Updates are probably caused by that slave-mode reset - from RM:

 Reset Mode - Rising edge of the selected trigger input (TRGI) reinitializes the counter

and generates an update of the registers.

You should perform an AND on the captured status register with the current DIER, before evaluating which was the source of interrupt.

> lifted from another of your posts on Community.

So, the blame is on me? ;)

> Half expected a comment about cube

Glad I did not disappoint :D

Jan

alister
alisterAuthor
Senior III
April 6, 2020

Thanks, yes, that evaluates the interrupt source correctly.

Neither blaming or disappointed.

But the slave-mode updates are interrupting almost every capture. Aside, the interrupt numbers are curious... for each 32 transition encode, by this corrected method there is 1 interrupt for CC1IF, 1 interrupt for UIF and between 22 and 26 interrupts for slave-mode update and some small number of these are spurious because SR = 0.

Do you or other readers know a less-cycles method to delineate the first update following a run of captures (i.e. the end of my encode)?

If not, I'll dispense with slave-mode reset.

Thanks!

waclawek.jan
Super User
April 6, 2020

It's still not OK that the interrupts happen, of course, as they are not enabled; and I don't have an explanation for that.

@berendi​ , could you please comment?

JW

alister
alisterAuthor
Senior III
April 7, 2020

The TIM15_CR1->URS... from the RM:

URS: Update request source

This bit is set and cleared by software to select the UEV event sources.

0: Any of the following events generate an update interrupt if enabled. These events can be:

– Counter overflow/underflow

– Setting the UG bit

– Update generation through the slave mode controller

1: Only counter overflow/underflow generates an update interrupt if enabled.

alister
alisterAuthor
Senior III
April 7, 2020

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.

waclawek.jan
Super User
April 7, 2020

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
April 7, 2020

@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.

waclawek.jan
Super User
April 7, 2020

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

berendi
Principal
April 7, 2020

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.

berendi
Principal
April 7, 2020

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.

alister
alisterAuthor
Senior III
April 7, 2020

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
alisterAuthor
Senior III
April 7, 2020

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)	

berendi
Principal
April 7, 2020

What are the constraints? Can you use TIM1 or TIM3 instead? Or maybe one of them slaved to TIM15 when the pinout is fixed?

What are the parameters of the input signal? Min/max time between edges during the signal burst, and min/max idle time?

What do the timer registers contain? The setup in ProcInit() is not going to capture anything on its own without setting something in the CC1S bits of CCMR1 first.

alister
alisterAuthor
Senior III
April 8, 2020

>What do the timer registers contain? 

It was a Cube project.

The ProcInit() below is refactored to include the entire TIM/DMA init, with TIM_CR1_URS.

Its interrupt behaviour is identical to what I'd described at the end of my earlier post that listed ProcInit().

#define D_IN_PIN 2
#define D_IN_GPIOx GPIOA
#define _TIMx TIM15
#define _TIM_CH 1
#define _DMAx DMA1
#define _DMAxCHy DMA1_Channel5
#define _DMA_CH 5
#define TIMx_IRQHandler TIM15_IRQHandler
 
/* _captureBuff captures the transition-times of the last 8 bits of an encode.
 * Dimensioned 15 because there's no transition between its last bit and the inter-encode idle. */
static uint16_t _captureBuff[15];
 
/**
 * Initialise the capture.
 */
void ProcInit(void)
{
 TIM_TypeDef *timx_p = _TIMx;
 DMA_TypeDef *dmax_p = _DMAx;
 DMA_Channel_TypeDef *dmaxChy_p = _DMAxCHy;
 uint32_t tmp32;
 
 /* GPIO clock enable for TIM15_CH1 pin */
 __HAL_RCC_GPIOA_CLK_ENABLE();
 
 /* TIM clock enable */
 __HAL_RCC_TIM15_CLK_ENABLE();
 
 /* DMA clock enable */
 __HAL_RCC_DMA1_CLK_ENABLE();
 
 /* GPIO init of TIMx_CHy pin */
 /* Configure alternate function (AF0) */
 tmp32 = D_IN_GPIOx->AFR[D_IN_PIN >> 3];
 tmp32 &= ~(GPIO_AFRL_AFSEL0 << ((D_IN_PIN & 0x7) * 4));
 tmp32 |= (0 << ((D_IN_PIN & 0x7) * 4));
 D_IN_GPIOx->AFR[D_IN_PIN >> 3] = tmp32;
 
 /* Configure mode (alternate) */
 tmp32 = D_IN_GPIOx->MODER;
 tmp32 &= ~(GPIO_MODER_MODER0 << (D_IN_PIN * 2));
 tmp32 |= (GPIO_MODER_MODER0_1 << (D_IN_PIN * 2));
 D_IN_GPIOx->MODER = tmp32;
 
 /* Configure the IO Speed (medium) */
 tmp32 = D_IN_GPIOx->OSPEEDR;
 tmp32 &= ~(GPIO_OSPEEDR_OSPEEDR0 << (D_IN_PIN * 2));
 tmp32 |= (GPIO_OSPEEDR_OSPEEDR0_0 << (D_IN_PIN * 2));
 D_IN_GPIOx->OSPEEDR = tmp32;
 
 /* Configure the IO Output Type (output push/pull) */
 tmp32 = D_IN_GPIOx->OTYPER;
 tmp32 &= ~(GPIO_OTYPER_OT_0 << D_IN_PIN) ;
 tmp32 |= (0 << D_IN_PIN);
 D_IN_GPIOx->OTYPER = tmp32;
 
 /* Configure Pull-up or Pull-down (no pull-up, pull-down) */
 tmp32 = D_IN_GPIOx->PUPDR;
 tmp32 &= ~(GPIO_PUPDR_PUPDR0 << (D_IN_PIN * 2));
 tmp32 |= (0 << (D_IN_PIN * 2));
 D_IN_GPIOx->PUPDR = tmp32;
 
 /* DMA init */
 /* Medium priority, 16-bit memory, 16-bit peripheral, memory inc, no peripheral inc,
 * circular, read from peripheral, no interrupts, DMA disabled. */
 dmaxChy_p->CCR = (DMA_CCR_PL_0
 | (/* memory inc */1 << DMA_CCR_MINC_Pos)
 | DMA_CCR_PSIZE_0 | DMA_CCR_MSIZE_0 | DMA_CCR_CIRC);
 
 /* 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;
 
 /* TIM init */
 /* No clock division, no auto-reload preload, edge-aligned, up-counter,
 * prevent Slave Controller update interrupts, TIM disabled.
 * Alister> Observed in testing the slave Controller's TIM_SR_UIF cannot be masked by TIM_DIER_UIE. */
 timx_p->CR1 = TIM_CR1_URS;
 
 /* Master-mode-select = reset, DMA request on CCx event */
 timx_p->CR2 = 0;
 
 /* Set the Autoreload value */
 timx_p->ARR = 65535;
 
 /* No Prescaler */
 timx_p->PSC = 0;
 
 /* No Repetition Counter */
 timx_p->RCR = 0;
 
 /* Generate an update event to reload Prescaler and Repetition Counter immediately */
 timx_p->EGR = TIM_EGR_UG;
 
 /* Clock source internal, 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);
 
 /* Input capture filter: N = 4xCK_INT, no IC1 prescaler, CC1 input/IC1 mapped on TI1 */
 timx_p->CCMR1 = TIM_CCMR1_IC1F_1 | TIM_CCMR1_CC1S_0;
 
 /* CC1NP/CC1P = non-inverted both edges, capture disabled */
 timx_p->CCER = ((TIM_CCER_CC1NP | TIM_CCER_CC1P) << ((_TIM_CH - 1) * 4));
 
 /* Enable the TIM Capture 1 DMA request and Update interrupt, clear all interrupt flags */
 timx_p->DIER = ((TIM_DIER_CC1DE << (_TIM_CH - 1)) | TIM_DIER_UIE);
 timx_p->SR = 0;
 
 /* TIM interrupt init */
 HAL_NVIC_SetPriority(TIM15_IRQn, 1, 0);
 HAL_NVIC_EnableIRQ(TIM15_IRQn);
 
 /* Enable the Input Capture */
 timx_p->CCER |= (TIM_CCER_CC1E << ((_TIM_CH - 1) * 4));
 
 /* Enable the DMA */
 dmaxChy_p->CCR |= DMA_CCR_EN;
 
 /* Enable the TIM */
 timx_p->CR1 |= TIM_CR1_CEN;
}

>What are the constraints? Can you use TIM1 or TIM3 instead? Or maybe one of them slaved to TIM15 when the pinout is fixed?

I'm protyping on a custom board of a different product fitted with a STM32F030C8. I'm using TIM15_CH1 as it was readily accessible.

We'd planned on using a STM32F030K6 and TIM1_CH1.

So yes, we could change MCU or pin(s) if necessary.

>What are the parameters of the input signal? Min/max time between edges during the signal burst, and min/max idle time?

The objective is to monitor a code embedded in the last 8 bits of an encode.

The encode details

  • NRZ, two transitions per bit, bit state given by high/low ratio. 
  • Bit-rate is fixed during an encode, but can range 400kHz to 2MHz.
  • Bit-length can range 18 to 1008 bits.
  • Inter-encode "idle" time can range 3us to 1s.

alister
alisterAuthor
Senior III
April 8, 2020

The core, peripheral and timer clocks are all 48MHz.