The STM32F1xx has some neat features that allow you to cross-couple timers in interesting ways.
In the last post on timers, I synchronized 2 timers by using a master-slave configuration where the master resets the slave on every cycle. This time I use 2 cross-coupled channels of one timer in order to measure a PWM train frequency and mark:space ratio.
The trick to measure PWM is to use one counter to measure the period and the second to measure mark time, the latter reset by the first period counter on every cycle:
Here’s how it works:
- The pulse train is applied to the pin of Timer 1 channel 1 (T1C1)
- T1C1 edge detector outputs rising and falling edge signals as shown below.
- The MUXs are cleverly designed so that you can use the edge signals from one channel to trigger the input of another channel. In this case, falling edge on channel 1 starts counter 2. This is set up in the CCER register.
- The CCMR1 register is finally used to pick which channel’s edge causes the counter to start. In this case, we want the falling edge of timer1 C1 to start counter 2.
- The rising edge of the next pulse causes the SMCR block to reset both counters. The value in Counter 1 will be the period and the value in counter 2 the mark. I use the SYSTICK timer to generate an interrupt every 2 seconds for reading the counter values.
Here is code for the STM32F372 that implements PWM detection. The approach for STM32F100 is the same strategy, though there are differences in register lay out:
RCC->AHBENR=RCC->;AHBENR | RCC_AHBENR_GPIOAEN; // Switch on GPIO port A
RCC->APB1ENR = RCC->APB1ENR
| RCC_APB1ENR_TIM5EN; // Switch on TIM2, TIM3 and TIM5
| (0x2<<2) | (0x2); // Set Alt Func mode for timer pins and general mode for GPIO GPIOA->AFR = GPIOA->AFR
| (0x2<<4) | (0x1); // Select AF1 for PA0, AF2 for PA1, AF2 for PA6 // Use counter to measure period and duty cycle TIM3->CCMR1=0x201; // Map channel 1 and channel 2 inputs to IC1
TIM3->PSC=0x84; // Pre-scale 8MHz clock down to 16.625uS into counter
TIM3->CCER=0x31; // Channel 1 and 2 capture enable. C1 count after rising edge, C2 after falling
TIM3->SMCR=0x54; // Trigger reset on rising edge TI1FP1
// Use counter to measure period
TIM3->CCMR1=0x1; // Map channel 1 input to IC1
TIM3->PSC=0x84; // Pre-scale 8MHz clock down to 16.625mS into counter
TIM3->CCER=(TIM3->CCER & 0x4) | 0x1; // Channel 1 capture enable
TIM3->SMCR=TIM3->SMCR | 0x84;
| 0x1; // Start capture
SysTick_Config(0xffffff); // Generate interrupt every ~2 secs
static int count=0;
counterValue[count++]=TIM3->CCR1; // Period
counterValue[count++]=TIM3->CCR2; // Low period