1

I'm trying to set up a 1s led blink on STM32F69I discovery board with STM32 HAL libraries. I've set up my timer the following way:

__TIM2_CLK_ENABLE();
s_TimerInstance.Init.Prescaler = (216000);
s_TimerInstance.Init.CounterMode = TIM_COUNTERMODE_UP;
s_TimerInstance.Init.Period = (1000);
s_TimerInstance.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
s_TimerInstance.Init.RepetitionCounter = 0;
HAL_TIM_Base_Init(&s_TimerInstance);
HAL_TIM_Base_Start(&s_TimerInstance);

And then I toggle my LED's with:

int main() {
    for (;;) {
        int timerValue = __HAL_TIM_GET_COUNTER(&s_TimerInstance);
        if (timerValue == 800)
            HAL_GPIO_WritePin(GPIOJ, (LED_GREEN | LED_RED), GPIO_PIN_SET);
        if (timerValue == 1000)
            HAL_GPIO_WritePin(GPIOJ, (LED_GREEN | LED_RED), GPIO_PIN_RESET);
        }
}

The MCU clock is supposed to be 216 mHz. My logic is, that if I set up the prescaler to 216000, it will mean that my timer will make 216000000/216000 = 1000 increments each second. Meaning, if I set my period to 1000, my LED's should blink every one second. However, they blink once like every 1.1-1.2 seconds.

Is my understanding of the prescaler and period completely wrong or what am I missing?

EDIT: 216000-1 && 1000-1 doesn't make any visible difference. In all honesty I am a complete beginner at this, so I am confirming this by seeing it differ from a normal "wall" clock, because I have no idea how to debug this. There is no rest of the code, this is all there is. I am running on 216mHz because it says on the STM website that the STM32F769I's MCU runs on 216, so I assumed my timer is also. Couldn't really decipher anywhere if that is the case, or not. I also didn't find any setting in the timer struct where I could set the clock freq.

xtrinch
  • 2,232
  • 1
  • 24
  • 46
  • try 216000 - 1 instead of 216000 – old_timer Jul 27 '17 at 18:24
  • shouldnt account for a whole tenth or two, but see what you see. how are you confirming 1.1 seconds and what reference clock are you using? the internal rc or an external crystal based? – old_timer Jul 27 '17 at 18:26
  • and should your timer value be 1000 or 999 when used like that? – old_timer Jul 27 '17 at 18:27
  • you may have other latencies depending on what the rest of your code looks like, what if you run in a loop and take the delta between the now time and the last timestamp you changed the led. when >= 1000 then toggle and move the last time to last time + 1000 (NOT to now time) and let that go for a bit. – old_timer Jul 27 '17 at 18:28
  • you running 216MHz just for fun or for a reason? – old_timer Jul 27 '17 at 18:30
  • If you only want to blink the LED, and don't actually need a timer, you should consider using `HAL_delay(time_in_ms)` – jacoblaw Jul 27 '17 at 18:46
  • @old_timer updated my question with additional info – xtrinch Jul 27 '17 at 18:47
  • @jacoblaw wanted to learn about interrupts at a later stage, got stuck here – xtrinch Jul 27 '17 at 18:47
  • definitely start with polling before messing with interrupts. – old_timer Jul 27 '17 at 18:48
  • @jacoblaw how do you do a delay without a timer? – old_timer Jul 27 '17 at 18:48
  • @old_timer isn't this version of the code a sort of polling? I am checking continuously if the value is a certain number – xtrinch Jul 27 '17 at 18:49
  • @old_timer `HAL_delay()` is pre-initialized, but I don't know off of the top of my head which timer it's using. It's out-of-the-box and ready to use for delays – jacoblaw Jul 27 '17 at 18:50
  • the part can run UP to 216mhz, but probably boots at like 8mhz off an internal oscillator. and then there is some action perhaps by the hal or by you that bumps it up to that. Being a cortex-m7 and using the hal and a discovery board there is most likely a crystal you are using so that is not the cause of your drift, my assumption is that if you are timing from when the led comes on once to when it goes off once you may be adding in some init time from something – old_timer Jul 27 '17 at 18:50
  • @old_timer init time from what? there is litterally nothing else to the code than that – xtrinch Jul 27 '17 at 18:52
  • @jacoblaw this is not a PIC it would be using a timer, which timer, would have to see the code, but it uses a timer. and is not going to be an exact thing will have some overhead so one led pulse may be fine but take a 1000 of them and it is going to be well over 1000 seconds rather than right at 1000 seconds which you can do using a timer directly rather than indirectly and only have latency per cycle rather than accumulate. – old_timer Jul 27 '17 at 18:52
  • are you toggling on, off, on, off, on, off and timing one of those state changes or periods? – old_timer Jul 27 '17 at 18:52
  • yes you are polling – old_timer Jul 27 '17 at 18:52
  • @old_timer No I'm not really timing any state changes, that would probably add additional overhead? – xtrinch Jul 27 '17 at 18:56
  • @old_timer I know this isn't a PIC, I have worked with this family of chips quite a bit. Obviously it has to use a timer for timing. OP is clearly new to this, not sure if he knew there was a function that's ready-to-go that could be used for a blinky program. It doesn't suite his needs though so I'm no advocating for it after he mentioned that. – jacoblaw Jul 27 '17 at 18:56
  • your comment was you dont need a timer use this delay function, the delay function uses a timer is my point, sure it is buried in the code and not obvious like using the timer api functions in this question...so I can see your comment being interpreted two different ways, dont use timer api calls use this non-timer-api-call function or dont use a timer use a timer, the latter being confusing. – old_timer Jul 27 '17 at 18:59
  • @Nirri are you toggling on/off/on/off...forever and each state change is 1.1 to 1.2 seconds? the api calls imply this timer setup is rolling around from 0 to 1000 or 1 to 1000 as the count value? and you are hoping to exactly hit two values for on/off? – old_timer Jul 27 '17 at 19:01
  • @old_timer I suppose I am, yes. Probably wouldn't use that in any real setup – xtrinch Jul 27 '17 at 19:03
  • 2
    I dont use the apis the peripherals are generally easier to program directly IMO (others disagree)(I recommend trying both and choosing your own preference). so i would set the timer up to free-run and roll over at max count then probably pick some bit and if set set the led, of clear clear the led, get my calculator out (yep with real buttons and everything) and do the math to confirm that it makes sense. Then get the timer to count to some value and roll around there, poll the rollover/interrupt bit, repeat the experiment, then eventually work into an interrupt. – old_timer Jul 27 '17 at 19:03
  • 1
    blinking an led is the printf("hello world") of bare metal. an exercise like this is throwaway code, but you for example find out if they have a pre-scaler between the system clock and the peripherals, or not. If so you dig around in the docs to see if it is documented and if it is programmable or not, and if there are rules (no reason to assume the peripherals can run at 216Mhz, perhaps only the arm core can run that fast), and on and on and on...programming an n vs n-1 with a long enough led blink rate (like a full minute) against a stopwatch you can often figure out the n vs n-1. – old_timer Jul 27 '17 at 19:06
  • 1
    your error is a bit large for a one second timeout 216mhz or not...so it is very curious but probably something very simple and once you find it it will make sense. nothing stands out as obvious yet thus all the questions. – old_timer Jul 27 '17 at 19:07
  • 1
    basic questions. so are one of your clocks wrong, the one on the chip the one in your hand, how are you using the one in your hand is it possible you are off by a tenth of a second (would not be consistent though and would answer that question). is it the clock on the chip? probably not, is it the setup, probably,...so working through the whys – old_timer Jul 27 '17 at 19:09
  • 1
    I assume this is a 32 bit timer you are using and not a 16? one would hope the init calls would complain if you sent a 216000 to a 16 bit or less prescaler or a prescaler that doesnt accept specific values but only powers of two for example. do these init calls have return values and what if you check those return values are any in error? – old_timer Jul 27 '17 at 19:10
  • @old_timer alright, will dig around a bit, very good advice – xtrinch Jul 27 '17 at 19:12
  • 1
    you are on the right track though yes you can if allowed prescale to 216000000 and every timer tick toggle the led. Or prescale to 216000 and every 1000 timer ticks toggle or 21600 and every 10000 toggle, or 72000000 and every 3 timer ticks toggle, etc. – old_timer Jul 27 '17 at 19:23
  • 1
    with that huge of a prescaler you are safe but in general you dont want to do the if current timer count is exactly equal to some number thing, with a 216000 cpu clocks to timer ticks you have plenty of instructions to sample that exact value, but if your prescaler was say 4 and even if writing hand tuned assembly language you might miss one, somewhere between a really small number and a really large number the risk falls of. so greater than or equal is better than equal. – old_timer Jul 27 '17 at 19:25
  • they do not necessary run on 216MHz. That is the max speed and you can configure many others. F7 uC have a very "rich" clock configuration so because we do not know your clock configuration it can be anything. – 0___________ Jul 27 '17 at 20:11
  • 1
    the timer is 32 bit but the prescaller is 16. So the value > 65535 is too large :) - of course if I understand those ridiculous HAL structures – 0___________ Jul 27 '17 at 20:31
  • 1
    Don't test for equality with a timer value in a loop like that, you have a race condition. There's a good chance the timer can increment over that value before or after the test. – Colin Jul 28 '17 at 08:27

2 Answers2

3

The STM32F769NI can run at a maximum of 216 MHz but on reset it selects the 16 MHz internal RC oscillator by default. See section 2.15 of the datasheet.

TIM2 has a 16-bit prescaler. See section 2.23.2 of the datasheet.

216000 (0x34BC0) is too large to fit in the 16-bit prescaler. In your program it is getting truncated to 0x4BC0 or 19392.

16 MHz / 19392 = 825.1 Hz or 0.001212 ticks per second.

0.001212 ticks per second multiplied by 1000 ticks = 1.212 seconds.

Change your prescaler to (16000 - 1) since your clock is actually running at 16 MHz, not 216 MHz.

kkrambo
  • 6,643
  • 1
  • 17
  • 30
  • You have just copied my last two comments: _'the timer is 32 bit but the prescaller is 16. So the value > 65535 is too large :)'_, 2; _'they do not necessary run on 216MHz. That is the max speed and you can configure many others.'_. Leave something for the OPs brains. – 0___________ Jul 28 '17 at 07:17
  • So if I wanted to change the clock I would find a function in HAL with which to increase the MCU clock? Where would I look for that? I found constants HSE_VALUE and HSI_VALUE in system_stm32f7xx, should I simply redefine those? – xtrinch Jul 28 '17 at 09:17
  • Another question, why 16000-1? (Specifically, why the -1)? I understand that I should probably subtract 1 from 1000, because the counter starts at 0 and interrupts at overflow, but why subtract one from the prescaler? – xtrinch Jul 28 '17 at 09:19
  • One more question, how come the prescaler integer is truncated from the left, not the right? – xtrinch Jul 28 '17 at 09:46
  • 2
    @Nirri: -1 because the [reference manual](http://www.st.com/resource/en/reference_manual/dm00224583.pdf) says so: "The counter clock frequency CK_CNT is equal to fCK_PSC / (PSC[15:0] + 1)." It's just another counter that goes from the preset value down to 0, and advances the main counter at overflow, effectively dividing the input frequency by x+1. When the prescaler is 0, it's dividing by 1, not 0. – followed Monica to Codidact Jul 28 '17 at 09:48
  • 2
    @Nirri: truncating that way is standard behavior in C, [see e.g. here](https://stackoverflow.com/questions/5881895/truncating-an-int-to-char-is-it-defined) – followed Monica to Codidact Jul 28 '17 at 09:50
  • @Nirri: changing the clock speed would involve more than just redefining a few constants, you should rather look at the examples that came with HAL, or set it using the graphical interface in STM32CubeMX, and let it generate the startup code for you. – followed Monica to Codidact Jul 28 '17 at 09:54
  • 2
    @PeterJ: It's a complete and plausible answer, addressing the question and other issues in the original post. It's the very purpose of SO to get these kinds of answers that one can **upvote** when correct, instead of having to wade through 30+ comments to find some hints that may work. – followed Monica to Codidact Jul 28 '17 at 10:00
  • 1
    @berendi alright, it seems there is more info about changing clock speed in the STM32F7Cube manual, thanks – xtrinch Jul 28 '17 at 11:54
  • @berendi I can't find any examples in the STM32CubeF7 directory, where do I look for them? – xtrinch Jul 29 '17 at 08:54
  • 1
    Where did you look? They are in the `Projects/STM32F769I-Discovery/Examples` directory for your board. – followed Monica to Codidact Jul 31 '17 at 05:46
0

Try this very complicated code instead:

TIM2 -> PSC = 21600 - 1;
TIM2 -> EGR = TIM_EGR_UG;
TIM2 -> CR1 |= TIM_CR1_CEN;

while(1) {
    if(TIM2 -> CNT >= 10000) 
    {
        TIM2 -> CNT = 0;
        ToggleLed();
    }
}

Replace ToggleLed with your actual code (which toggles the LED of course)

0___________
  • 60,014
  • 4
  • 34
  • 74
  • Thank you, will do, wanted to write the HAL version first, will definetly get to this too :) – xtrinch Jul 28 '17 at 11:55
  • 3
    Forget HAL for GPIO, SPI, U(S)ART, TIMERS and another similar devices. Even ethernet or usb has to be used very carefully as it was/is full of bugs. I use it only for those two peripherals. – 0___________ Jul 28 '17 at 13:24
  • @PeterJ: Not sure about USB, but Ethernet is easier without that HAL/STlib bloatware, too. As an additional benefit, it is much faster (relevant if you use interrupt/event based programming). – too honest for this site Jul 29 '17 at 21:10