2020-04-06 01:08 AM
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
But what I’m observing in the interrupt is
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?
Solved! Go to Solution.
2020-04-07 08:28 AM
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.
2020-04-08 12:28 AM
>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
2020-04-08 01:34 AM
It appears to me as if the CC2 (SR.CC2IF) or trigger (SR.TIF) triggers an interrupt even if they are not enabled in DIER (i.e. I suspect a silicon bug).
Can you please set CC2 to Input Capture (without assigning a pin, that should result in it never happening)? That would rule out the former.
Can you please try TIM3? TIM1 is tricky as it has several different interrupt vectors.
Note, that the 'F030K6 is a different die than the 'F030C8 (0x444 vs. 0x440), so if it indeed is a silicon bug, it may or may not behave in the same way.
I unfortunately don't have the 'F030x8 nor 'F05x (i.e. DEV_ID = 0x440) at hand to try myself...
JW
2020-04-08 02:09 AM
The core, peripheral and timer clocks are all 48MHz.
2020-04-08 02:12 AM
I'm using PA2/TIM15_CH1. Still advise trying TIM3 over TIM15? Will investigate CC2. Thanks!
2020-04-08 03:20 AM
> Still advise trying TIM3 over TIM15?
Well, you can at least try it as an experiment, if it's not too complicated to move the input pin physically on the existing hardware.
I don't think the core frequency here plays a role.
JW
2020-04-08 04:15 AM
Experiment: Leave DMA CH CCR disabled in ProcInit(). No other change.
Observations:
Thoughts?
updateCount volatile uint16_t 0xa <-- total encodes observed
captureCount uint32_t 0x9 <-- total decodes stared (first is spurious update)
otherCount uint32_t 0x0 <-- total spurious
debugSr debugSr_t [32] 0x2000007c
debugSr[0] debugSr_t {...}
dier uint16_t 0x201
sr uint16_t 0x5 <-- first spurius update
debugSr[1] debugSr_t {...}
dier uint16_t 0x202
sr uint16_t 0x247 <-- encode started
debugSr[2] debugSr_t {...}
dier uint16_t 0x201
sr uint16_t 0x247 <-- start decode
debugSr[3] debugSr_t {...}
dier uint16_t 0x202
sr uint16_t 0x247 <-- encode started
debugSr[4] debugSr_t {...}
dier uint16_t 0x201
sr uint16_t 0x247 <-- start decode
<snip>
2020-04-08 04:35 AM
Oh, @#$%^&* how stupid I am!!!
Bit 1 CC1IF: Capture/Compare 1 interrupt flag
If channel CC1 is configured as input: This bit is set by hardware on a capture. It is cleared
by software or by reading the TIMx_CCR1 register.
DMA *reads* CCR1, that's why CC1IF is cleared by the time by the time you read it in the ISR!
JW
2020-04-08 04:44 AM
>It appears to me as if the CC2 (SR.CC2IF) or trigger (SR.TIF) triggers an interrupt even if they are not enabled in DIER
>set CC2 to Input Capture (without assigning a pin, that should result in it never happening)? That would rule out the former.
Delta to ProcInit():
timx_p->CCMR1 = ((/* IC1 filter N = 4 */TIM_CCMR1_IC1F_1 | /* IC1 mapped on TI1 */TIM_CCMR1_CC1S_0) << ((_TIM_CH - 1) * 8))
| (/* IC2 filter N = 4 */TIM_CCMR1_IC2F_1 | /* IC2 mapped on TI2 */TIM_CCMR1_CC2S_0);
timx_p->CCER = ((TIM_CCER_CC1NP | TIM_CCER_CC1P) << ((_TIM_CH - 1) * 4))
| (TIM_CCER_CC2NP | TIM_CCER_CC2P);
timx_p->CCER |= (TIM_CCER_CC1E << ((_TIM_CH - 1) * 4))
| (TIM_CCER_CC2E);
Observations:
updateCount volatile uint16_t 0x5 <-- total encodes observed
captureCount uint32_t 0x4 <-- total decodes stared (first is spurious update)
otherCount uint32_t 0x36 <-- total spurious
<snip>
debugSr[8] debugSr_t {...}
dier uint16_t 0x202
sr uint16_t 0x40
debugSr[9] debugSr_t {...}
dier uint16_t 0x202
sr uint16_t 0x40
debugSr[10] debugSr_t {...}
dier uint16_t 0x202
sr uint16_t 0x42 <-- encode started
debugSr[11] debugSr_t {...}
dier uint16_t 0x201
sr uint16_t 0x40
debugSr[12] debugSr_t {...}
dier uint16_t 0x201
sr uint16_t 0x41 <-- start decode
debugSr[13] debugSr_t {...}
dier uint16_t 0x202
sr uint16_t 0x41
debugSr[14] debugSr_t {...}
dier uint16_t 0x202
sr uint16_t 0x40
debugSr[15] debugSr_t {...}
dier uint16_t 0x202
sr uint16_t 0x40
debugSr[16] debugSr_t {...}
dier uint16_t 0x202
sr uint16_t 0x40
debugSr[17] debugSr_t {...}
dier uint16_t 0x202
sr uint16_t 0x40
debugSr[18] debugSr_t {...}
dier uint16_t 0x202
sr uint16_t 0x40
debugSr[19] debugSr_t {...}
dier uint16_t 0x202
sr uint16_t 0x42 <-- encode started
debugSr[20] debugSr_t {...}
dier uint16_t 0x201
sr uint16_t 0x40
debugSr[21] debugSr_t {...}
dier uint16_t 0x201
sr uint16_t 0x41 <-- start decode
debugSr[22] debugSr_t {...}
dier uint16_t 0x202
sr uint16_t 0x41
debugSr[23] debugSr_t {...}
dier uint16_t 0x202
sr uint16_t 0x40
<snip>
2020-04-08 05:01 AM
Yes this was just a blind shot, proven wrong.
See my post at bottom of thread (can't give link, courtesy of #worst_forum_software_ever )
JW