One led is blinking too frequently using the hardware timer (stm32f103vdh6)

i recently started my internship using embedded C and i'm having some trouble with one of the tasks: Two leds should be blinking at 1Hz, one using the hardware timer, and the other one using the RTX RTOS os_dly_wait() function. The led using the RTX RTOS task is working fine, meanwhile the other led is blinking at a higher frequency (i estimate around every ~0.9 seconds). The microcontroller is operating at 72 MHz, so i'm using the prescaler value of 7200 and using a period of 10000. I'm using the TIM2 timer. I will also include a Clock tree of the microcontroller. What could be causing this?

#include <RTL.h>
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include <rtl.h>

#define     GPS_LED_PIN  GPIO_Pin_9
#define     NAV_LED_PIN  GPIO_Pin_2

void InitLED(void) {
GPIO_InitTypeDef  GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE | RCC_APB2Periph_GPIOB
| RCC_APB2Periph_GPIOD, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOE, GPIO_Pin_2);
}

void TurnOnLeds (void) {
GPIO_SetBits(GPIOD, NAV_LED_PIN);
GPIO_ResetBits(GPIOB, GPS_LED_PIN);
}
void InitializeTimer()
{
TIM_TimeBaseInitTypeDef timerInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
timerInitStructure.TIM_Prescaler = 7200;
timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
timerInitStructure.TIM_Period = 10000;
timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
timerInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &timerInitStructure);
TIM_Cmd(TIM2, ENABLE);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
}
for (;;) {
os_dly_wait(5);
GPIO_SetBits(GPIOB, GPS_LED_PIN);
os_dly_wait(95);
GPIO_ResetBits(GPIOB, GPS_LED_PIN);
}
}
for(;;)
{
int timerValue = TIM_GetCounter(TIM2);
if (timerValue == 0)
GPIO_SetBits(GPIOD, NAV_LED_PIN);
else if (timerValue == 1000)
GPIO_ResetBits(GPIOD, NAV_LED_PIN);
}
}
/*-----------------------------MAIN---------------------------------------------*/
int main(void) {

InitLED();
TurnOnLeds();
InitializeTimer();
}

• How do you know it's operating at 72MHz? May 7, 2019 at 14:48
• @Hearth it says so in the description from the datasheet. You reckon it's different? Datasheet: eu.mouser.com/datasheet/2/389/stm32f103rc-956261.pdf May 7, 2019 at 14:56
• 72 MHz is the maximum frequency. Go through the initialization code and figure out: Which clock source is selected (HSI or HSE) and what is it's frequency? Is the PLL enabled and what multiple? What is the AHB Prescaler set to? What is the APB1 Prescaler set to? May 7, 2019 at 15:53
• If you're still unsure you can enable SYSCLK to be output through the MCO pin and then probe it with an oscilloscope and measure what the frequency really is. May 7, 2019 at 16:00
• Thank you for the comments, i will try to fix the issue when i get to work tomorrow. May 7, 2019 at 16:17

if (timerValue == 0)
GPIO_SetBits(GPIOD, NAV_LED_PIN);
else if (timerValue == 1000)
GPIO_ResetBits(GPIOD, NAV_LED_PIN);


This is NOT how you use hardware timer to switch outputs.

To begin with, you assume that your task3 will check timer value when it is exactly equal to either 0 or 1000. You cannot guarantee this happening all the time in RTOS. It is possible that one check is done at, say 999, while the next one at 3, resulting in missed trigger time. Which is what you are seeing as a lower blinking frequency.

If you really want to use hardware timer, then you should configure it for PWM generation. Set OCC register to the desired LED ON duration, and the output pin will be active while counter is below this value and inactive for the rest of the cycle. (I am using "active" and "inactive" instead of high and low here, because polarity is configurable).

This way your hardware output does not depend on the RTOS task being called just in time for a switch.

However if you still wish to do this in a task, at least change exact equality to "greater than" (or "less then"). You will have much more consistent results. Something like this:

if (timerValue < 10)
GPIO_SetBits(GPIOD, NAV_LED_PIN);
else
GPIO_ResetBits(GPIOD, NAV_LED_PIN);


Just because it says it says 72MHz on the datasheet does not mean it will always run at 72MHz, it is the maximum it can run at. You don't show any initialization code so we can't tell if it has been correctly set to run at 72MHz. Inside the chip it uses much lower internal or external frequency source, which may be inaccurate.

But since you are using some kind of real-time OS, and using the delay function it provides, the delays are not exact. It just waits until the current millisecond count has been increased by the amount you requested. As the thing is completely asynchronous, assuming that you have 1ms system timer tick, there can be anything between 0 to 1ms before the next tick happens, so requesting 5ms will be in the range of 4-5 ms, so statistically speaking delay is always 0.5ms less.

I don't see why the led driven by TIMER2 counter even works. It will be awfully hard with a RTOS to hit to a point when counter is exactly 0 or exactly 1000. One count being 72MHz divided by the 7200 prescaler, so it's 0.1ms !