2024-01-21 08:53 AM
I have an Arduino Giga (STM32H7) and I'm using the timers to generate PWM's. Everything's working fine, programming is no problem, but I cannot come up with a way to implement what I want. I included a simple visual to describe what I want the output to look like.
TIM1 is the master timer, it generates a PWM, set as active high (for CNT<CCR). I have a slave TIM8, which should do the following: when TIM1 starts over again/goes high, TIM8 should generate N pulses. The time it takes for N pulses to complete is shorter than the period of TIM1, I want them to retrigger each time TIM1 goes high again. Here's the catch: The pulses should immediately start high, however, once all the pulses have fired, the output should stay low.
If it weren't for the last part, the solution would be simple: TIM1 uses UEV as output trigger, TIM8 takes an internal trigger input from TIM1, set TIM8 to One-Pulse Mode, set the repetition counter to N, and voila, you get bursts of N pulses on each TIM1 high (yes technically the first time it won't trigger, doesn't matter).
However, this will cause the pulses of TIM8 to either start 'delayed', since for CNT<CCR, the output will be low in OPM, or if we flip the polarity, it will start high, but after having finished N pulses, TIM8->CNT=0 (because of OPM), which causes the output to be high...
Can anyone think of a good solution/setup? Again, programming is no issue, it's just finding a way to get this behaviour, if that's even possible... Also, I would prefer not to use interrupts handlers for reasons related to the rest of my program.
Solved! Go to Solution.
2024-01-21 09:05 AM
Very doable with a number of methods
Perhaps the easiest is to use the scheme that almost works except use PWM mode 2 and set it so output is low at CNT=0, high at CNT=1... X, and then low again until it reaches ARR.
If you need them to exactly match, you could use TIM8 in gated slave mode to get exactly 4 pulses at the start of each TIM1 period. Can describe this more if you need.
2024-01-21 09:05 AM
Very doable with a number of methods
Perhaps the easiest is to use the scheme that almost works except use PWM mode 2 and set it so output is low at CNT=0, high at CNT=1... X, and then low again until it reaches ARR.
If you need them to exactly match, you could use TIM8 in gated slave mode to get exactly 4 pulses at the start of each TIM1 period. Can describe this more if you need.
2024-01-21 09:55 AM
Ah the combined mode, I hadn't considered that but I'm sure that will work. The single count offset is no problem. Thanks a lot!
2024-08-12 08:59 AM - edited 2024-08-12 09:01 AM
Very late response, but it's not working as I would like. I use TIM1 as a master timer, and TIM8 in slave mode.
// Enable use of Timers.
RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // for TIM1
RCC->APB2ENR |= RCC_APB2ENR_TIM8EN; // for TIM8
// Enable Capture/Compare for Timers_Channels.
TIM1->BDTR |= TIM_BDTR_MOE; // Enables Main Output for TIM1. Only needed for TIM1 and TIM8 (see Reference Manual)
TIM1->CCER |= TIM_CCER_CC2E; // Enable channel 2
TIM8->BDTR |= TIM_BDTR_MOE; // Enables Main Output for TIM8. Only needed for TIM1 and TIM8 (see Reference Manual)
TIM8->CCER |= TIM_CCER_CC1E; // Enable channel 1
TIM8->CCER |= TIM_CCER_CC2E; // Enable channel 2
// Set Output Compare Mode for Timers_Channels.
// 6: PWM mode 1 - In upcounting, channel is active as long as TIMx_CNT<TIMx_CCR1, else inactive.
// 7: PWM mode 2 - Inversed, channel is INactive as long as TIMx_CNT<TIMx_CCR1, else active.
// Channels 1, 2 are in CCMR1. Channels 3,4 are in CCMR2, etc...
TIM1->CCMR1 &= ~TIM_CCMR1_OC2M_Msk; // for TIM1_CH2
TIM1->CCMR1 |= (0x6UL << TIM_CCMR1_OC2M_Pos);
// Basler: The Basler camera is triggered by a combined PWM mode, to ensure a low level at the end of the frame triggers,
// while still (almost, at CNT=1) starting high.
// 12: Combined PWM mode 1 - OC1REF has the same behavior as in PWM mode 1. OC1REFC is the logical OR between OC1REF and OC2REF.
// 13: Combined PWM mode 2 - OC1REF has the same behavior as in PWM mode 2. OC1REFC is the logical AND between OC1REF and OC2REF.
TIM8->CCMR1 &= ~TIM_CCMR1_OC1M_Msk; // for TIM8_CH1 - AND
TIM8->CCMR1 |= (0x13UL << TIM_CCMR1_OC1M_Pos); //
TIM8->CCMR1 &= ~TIM_CCMR1_OC2M_Msk; // for TIM8_CH2 - OR
TIM8->CCMR1 |= (0x12UL << TIM_CCMR1_OC2M_Pos); //
//TIM8->CCER |= TIM_CCER_CC1P; // Flip output polarity on TIM8_CH1. Should not be necessary... FIXME
// Set Master and Slave Timers.
TIM1->CR2 &= ~TIM_CR2_MMS_Msk;
TIM1->CR2 |= (0x2UL << TIM_CR2_MMS_Pos); // Set the Update Event as Trigger Output (TRGO)
// Set slaves to One Pulse Mode. This stops the counter on the next Update Event.
TIM8->CR1 |= TIM_CR1_OPM;
// "Mode 6: Trigger Mode - The counter starts at a rising edge of the trigger TRGI
// (but it is not reset). Only the start of the counter is controlled."
// Note: We are in One Pulse Mode, so counter will be stopped at 0 (after reaching ARR) anyways...
TIM8->SMCR &= ~TIM_SMCR_SMS_Msk;
TIM8->SMCR |= (0x6UL << TIM_SMCR_SMS_Pos);
TIM8->SMCR &= ~TIM_SMCR_TS_Msk; // Use Internal Trigger 0, corresponding to TIM1
TIM8->SMCR |= (0x0UL << TIM_SMCR_TS_Pos);
TIM8->RCR = 1;
TIM8->PSC = 24000-1; // Prescaler: Set so that 1 tick is 0.1 microseconds (at 240 MHz)
TIM8->ARR = 10*50;
TIM8->CCR1 = 10*30;
TIM8->CCR2 = 10*40;
(TIM1 is set to a much longer ARR)
I expected to see a 10 ms pulse, which starts after 30 ms and goes down after 40ms. Why is this not working? I do not get any output when using the combined PWM mode. When just set the normal PWM mode for TIM8 channel2 I see the normal PWM, so the output and stuff is set correctly. It's just the combined mode that is not working.
2024-08-12 10:24 AM
// Basler: The Basler camera is triggered by a combined PWM mode, to ensure a low level at the end of the frame triggers,
// while still (almost, at CNT=1) starting high.
// 12: Combined PWM mode 1 - OC1REF has the same behavior as in PWM mode 1. OC1REFC is the logical OR between OC1REF and OC2REF.
// 13: Combined PWM mode 2 - OC1REF has the same behavior as in PWM mode 2. OC1REFC is the logical AND between OC1REF and OC2REF.
TIM8->CCMR1 &= ~TIM_CCMR1_OC1M_Msk; // for TIM8_CH1 - AND
TIM8->CCMR1 |= (0x13UL << TIM_CCMR1_OC1M_Pos); //
TIM8->CCMR1 &= ~TIM_CCMR1_OC2M_Msk; // for TIM8_CH2 - OR
TIM8->CCMR1 |= (0x12UL << TIM_CCMR1_OC2M_Pos); //
1. 0x12/0x13 != 12/13
2. TIMx_CCMRx.OCxM bits don't form a continuous bitfield (for historical reasons)
JW