2023-04-02 03:53 AM
We are using STM32G051 to measure duty cycle of the actuator PWM signal.
TIM1 is configured in slave reset mode, with CH1 direct capture on rising edge and CH2 indirect capture on falling edge. Two DMA channels transfer results into two memory buffers. This works perfectly when there are incoming pulses.
I also added CC IRQ required for processing of special cases 0% and 100% duty.
The problem is, when TIM1_CC_IRQHandler(void) is called I cannot tell which channel caused it, as both LL_TIM_IsActiveFlag_CC1(TIM1) and LL_TIM_IsActiveFlag_CC2(TIM1) return false. On the other hand, LL_TIM_IsActiveFlag_UPDATE(TIM1) always returns true, even though the update interrupt is explicitly disabled and furthermore, this is dedicated capture/compare handler and it should not be called on update anyway.
UPDATE
To clarify what I am trying to do, here are some code snippets and explanation.
I also tried to run TIM3 in slave reset mode triggered by ITR0 from TIM1. This did not work probably for the same reason - the trigger events were coming from TIM1 all the time.
Finally, I programmed TIM1 Capture IRQ to directly set TIM3 counter register to 1 and TIM3 to call IRQ on update. Theoretically TIM3 counter should not reach ARR while it is constantly reset to 1, then call IRQ when there are no pulses on TIM1. This works for now, but in debug I see TIM3 IRQ called from time to time even if there are no pulses. Don't know why and would prefer more reliable solution.
Here is how I start the timers:
void PWMCaptureInit()
{
// DMA_CH5 for TIM1_CH1
LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_5, (uint32_t)&TIM1->CCR1);
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_5, (uint32_t)&period_buf);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_5, 1);
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_5);
LL_TIM_EnableDMAReq_CC1(TIM1);
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1);
// DMA_CH6 for TIM1_CH2
LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_6, (uint32_t)&TIM1->CCR2);
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_6, (uint32_t)&duty_buf);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_6, 1);
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_6);
LL_TIM_EnableDMAReq_CC2(TIM1);
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH2);
// TIM1
LL_TIM_EnableIT_CC1(TIM1);
LL_TIM_DisableIT_CC2(TIM1);
LL_TIM_DisableIT_CC3(TIM1);
LL_TIM_DisableIT_CC4(TIM1);
LL_TIM_DisableIT_UPDATE(TIM1);
LL_TIM_EnableCounter(TIM1);
// TIM3
LL_TIM_DisableIT_CC1(TIM3);
LL_TIM_DisableIT_CC2(TIM3);
LL_TIM_DisableIT_CC3(TIM3);
LL_TIM_DisableIT_CC4(TIM3);
LL_TIM_SetUpdateSource(TIM3, LL_TIM_UPDATESOURCE_COUNTER);
LL_TIM_EnableIT_UPDATE(TIM3);
LL_TIM_EnableCounter(TIM3);
}
And here are IRQ handlers:
void TIM1_CC_IRQHandler(void)
{
LL_TIM_SetCounter(TIM3, 1);
if (LL_TIM_IsActiveFlag_UPDATE(TIM1)) LL_TIM_ClearFlag_UPDATE(TIM1);
if (LL_TIM_IsActiveFlag_CC1(TIM1)) LL_TIM_ClearFlag_CC1(TIM1);
if (LL_TIM_IsActiveFlag_CC2(TIM1)) LL_TIM_ClearFlag_CC2(TIM1);
if (LL_TIM_IsActiveFlag_CC3(TIM1)) LL_TIM_ClearFlag_CC3(TIM1);
if (LL_TIM_IsActiveFlag_CC4(TIM1)) LL_TIM_ClearFlag_CC4(TIM1);
if (LL_TIM_IsActiveFlag_CC5(TIM1)) LL_TIM_ClearFlag_CC5(TIM1);
if (LL_TIM_IsActiveFlag_CC6(TIM1)) LL_TIM_ClearFlag_CC6(TIM1);
if (LL_TIM_IsActiveFlag_TRIG(TIM1)) LL_TIM_ClearFlag_TRIG(TIM1);
}
void TIM3_IRQHandler(void)
{
if (LL_TIM_IsActiveFlag_UPDATE(TIM3)) {
LL_TIM_ClearFlag_UPDATE(TIM3);
period_buf = 0; // THIS IS WHERE "NO PULSES" CONDITION HANDLED
}
if (LL_TIM_IsActiveFlag_CC1(TIM3)) LL_TIM_ClearFlag_CC1(TIM3);
if (LL_TIM_IsActiveFlag_CC2(TIM3)) LL_TIM_ClearFlag_CC2(TIM3);
if (LL_TIM_IsActiveFlag_CC3(TIM3)) LL_TIM_ClearFlag_CC3(TIM3);
if (LL_TIM_IsActiveFlag_CC4(TIM3)) LL_TIM_ClearFlag_CC4(TIM3);
}
Problems:
When TIM1_CC_IRQHandler is called the TIM1->SR register is always 0x30059 (110000000001011001), which means UPDATE and CC3-CC6 are set, even though they are explicitly disabled. On the other hand CC1 and CC2 are never set, even though this is Capture IRQ handler specifically for these events.
When TIM3_IRQHandler is called the TIM3->SR register is always 0x1f (11111), which means UPDATE and CC1-CC4 are set, even though they are explicitly disabled. Also note that URS is set to 1 ("Only counter overflow/underflow generates an update interrupt") and CCR2..CCR4 are bigger than ARR. Since I reset counter in TIM1, this handler should not be called at all while there are incoming pulses, but it does.
2023-04-02 05:47 AM
> On the other hand, LL_TIM_IsActiveFlag_UPDATE(TIM1) always returns true, even though the update interrupt is explicitly disabled
I don't know what that Cube/LL function exactly does, but the TIMx_SR.UIF flag is set upon each Update event, regardless of whether TIMx_DIER.UIE is set or not.
If you're in doubts, read out in the ISR and check/post the TIM registers content.
JW
2023-04-02 09:51 AM
sorry, wrote an answer instead of reply. this can be deleted
2023-04-03 03:37 AM
Those functions do exactly that. They are just simple macros:
return ((READ_BIT(TIMx->SR, TIM_SR_CC1IF) == (TIM_SR_CC1IF)) ? 1UL : 0UL);
return ((READ_BIT(TIMx->SR, TIM_SR_UIF) == (TIM_SR_UIF)) ? 1UL : 0UL);
My point is - there should NOT be an update event when Capture happens, since timer is configured in slave reset mode. The rising edge of the pulse should reset counter to 0 way before it reaches ARR. At least this is my understanding of how it supposed to work, but I can be mistaken, of course.
Here is what I see:
2023-04-03 04:24 AM
> however within TIM1_CC_IRQHandler neither TIM_SR_CC1IF nor TIM_SR_CC2IF
> are set in the SR register.
Read out and check/post content of TIM registers at the beginning of TIM1_CC_IRQHandler(). Also post code of TIM1_CC_IRQHandler().
> My point is - there should NOT be an update event when Capture happens, since timer is configured in slave reset mode.
JW
2023-04-12 10:30 AM
I've added code snippets to the original question and included content of SR registers. I can add other registers if it helps.
I did not interpret "updates registers" as an update event generation. It would explain why UIF flag is set. But it does not explain why CC1IF/CC2IF flags are NOT set.
In any case, with the addition of TIM3 the code works somehow, but not very reliable, because TIM3 handler is called frequently even if there are pulses and I don't know why. I feel like I am missing something very basic.