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 12:13 AM
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.
2020-04-07 12:22 AM
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
2020-04-07 01:36 AM
@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.
2020-04-07 02:08 AM
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.
2020-04-07 04:23 AM
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
2020-04-07 05:07 AM
It is not yet clear to me what exactly happens, but consider this scenario:
But this is just an idea I'm going to verify now.
2020-04-07 05:24 AM
>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.
2020-04-07 05:33 AM
> 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.
2020-04-07 06:10 AM
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.
2020-04-07 07:04 AM
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)