Is it possible to mix modes within one timer but on different channels?
Yes, but they will share the counter and reload register. It means that if the PWMs are using a specific frequency, i.e. not just counting up to 65535 (which without a prescaler would give ~1 kHz @ 64 or 72 MHz), then the capture registers would get timestamps only in that range.
Each timer has only one counter, which is shared by its 4 channels. It can either count some clock events (APB clock, which usually equals the system clock), or one external trigger input. To generate a PWM signal, the counter must supply the PWM frequency. To count pulses on other input lines, DMA channels or interrupt routines are necessary to supply additional counters.
Counting with timer interrupts
You can set up 2 channels in PWM Mode and another 2 in Input capture mode, as described in the respective chapters of the Reference Manual. Enable interrupts on the input capture channels. In the interrupt handler, check the timer status register to see which channel has caused the interrupt, and do the counting. Be careful when resetting the interrupt bit in the status register, don't accidentally reset event bits that you haven't processed. This should work:
uint32_t t1c3, t1c4;
void TIM1_Handler(void) {
if(TIM1->SR & TIM_SR_CC3IF) {
t1c3++;
TIM1->SR = ~TIM_SR_CC3IF;
}
if(TIM1->SR & TIM_SR_CC4IF) {
t1c4++;
TIM1->SR = ~TIM_SR_CC4IF;
}
}
You can also read the timestamp values from the capture/compare registers if you need the exact elapsed time between two events.
Counting with EXTI interrupts
Any I/O pin can be an EXTI interrupt source, with the constraint that two pins with the same number can't be mapped as EXTI interrupt source at the same time, i.e. PA0,PA1,PB2,PC3,PA4
is OK, but PA0,PB0
isn't. Setting up EXTI interrupts might be easier than timer capture channels, the drawback is that there are no timestamps and no input filters.
Counting with DMA
Counting 6 channels with at most 2 kHz, i.e. the signals are at least 500 us apart, should be no problem for the MCU even with interrupts. However if the MCI has a lot of other things to do, and you have plenty of free DMA channels, you can set up the capture channels to generate DMA requests instead of interrupts.
Note that TIM4_CH4
has no DMA channel associated with, therefore you have to rearrange pins a bit, or use this channel in interrupt mode.
If you don't need the exact timings between events, then set up the DMA channels as peripheral to memory, 8 bits, circular mode, and disable both peripheral and memory address increment. Both source and destination addresses should be valid and byte adressable, but the values are not important. Put some large value in CNDTR
, e.g. 0x8000
(to make overflow handling simpler). It will count the timer capture events (downwards) while copying the same byte over and over. You can check the counter in the control loop whenever it's needed.
This has the advantage of not using the MCU core at all, it can be put to sleep to conserve power, the peripheral blocks will do the counting autonomously. The disadvantage is of course that it uses 6 DMA channels (out of 7 e.g. on a STM32F103C8
).
If you need the timestamps, you can let DMA copy them from the CCR
registers to a real memory buffer, with memory increment turned on.