For this demo I used 3 of the counter timers on the STM32F372 to generate pulse trains of differing frequencies, made more interesting by adding 2 things:
(1) Chaining of timers using synchronization. Surprisingly, even when 2 timers driven by the onboard clock of the F372 are started at the same time, within 30 secs they have often drifted apart by as much as 500mS. This could be caused by differing path lengths on the SoC. The problem can be solved by using the on-board triggering mechanism which allows one timer to generate triggers that will drive another at selectable events. I used this feature to reset the second timer to the first on every cycle, thereby minimizing drift.
(2) Generating a periodic interrupt using the CortexM3 core SYSTICK clock. The SYSTICK clock only has a couple of configuration registers and is much simpler than the CTC Timers. Easy.
The programmers’ models for the timers used in the STM32F1xx and F37x series are similar. I guess ST reused the same IP blocks… There are some register differences, though minor. (BDTR register, channel numbers and especially the trigger mechanism for synchronizing timers). So, much of the code will work on the F100 series.
There are 3 different pulse trains:
- Slow 1 sec square pulse train using the counter in PWM output with a 50:50 duty cycle (Timer 2)
- Double rate 500mS pulse (Timer 5)
- Interrupt-driven pulse (SYStick)
The timer chaining works like this:
Timer 2 is set as the Master. An “Update Event” (UEV) occurs at times such as counter overspill (when the counter gets to its target value and resets). The Master Mode Selection (MMS) bits of the TIM2_CR2 register allow you to create a trigger output when a UEV happens.
Timer 5 is the slave. In its Slave Mode Control register:
- Set the Trigger Select (TS) bits to 0. Makes Timer5 react to trigger source ITR0 (which is TIM2)
- Set the Slave Mode (SMS) bits to 100. This causes the timer to reset itself when the trigger selected by TS occurs.
There are only certain combinations of timers that can trigger each other in this way:
The SYSTick part is even easier. There are only a couple of registers to set, but this needs to be done in a Privilege mode. Happily there is a CMSIS function for this where you just supply the count value. However the limitation of the supplied functions is that they dont let you tinker with the other register settings.
The only extra thing needed is an ISR for SYSTICK. Easy…
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 Alternate Function 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 TIM2->CCMR1=0x60; // Output on channel 1 and set to PWM mode1
TIM2->CCER=0x1; // Channel 1 output enable
TIM2->PSC=0xfffe; // Pre-scale to 65535
TIM2->ARR=0x100; // ARR:CCR1 ratio 80/40
TIM2->CR2=0x20; // Master-slave mode send a trigger on each UEV
TIM5->CCMR1=0x60<<8; // Output on channel 2 and set to PWM mode1 TIM5->CCER=0x1<<4; // Channel 2 output enable TIM5->PSC=0xfffe; // Pre-scale to 65535
TIM5->ARR=0x40; // ARR:CCR1 ratio 40/20 is fast
TIM5->SMCR=0x4; // TIM5 in slave mode responding to triggers from TIM2 with reset
| TIM_CR1_ARPE; // Start TIM2
| TIM_CR1_ARPE; // Start TIM5
SysTick_Config(0xffffff); // Generate interrupt every ~2 secs
static char state=0;
static int count=0;
if (state==0x4) state=0;