cancel
Showing results for 
Search instead for 
Did you mean: 

Timer 2 on STM32F401C Discovery Board

psdeering
Associate II
Posted on September 10, 2015 at 16:57

Hi all,

I've just started playing with the STM32F4 on the STM32F401C Discovery board, and had some strange behaviour (slowed down time) when attempting to generate a 1ms TIM2 interrupt.

From the reference manual for the microcontroller, I have enabled HSE via SW in RCC_CR to give me a (supposedly) 8Mhz reference clock (external 8Mhz crystal) to drive the AHB bus.  Both AHB and APB1 prescalers are set at 0, so no clock division on the bus clocks.

From this, I assume that the clock driving the prescaler for TIM2 should be 8Mhz.  I have set the prescale for TIM2 at 7 (CK_CNT = CK_PSC / 8), which, presumably, should give me a 1Mhz clock driving the counter (CK_CNT = 1Mhz).  I have set ARR at 999, which I assumed would supply a 1ms interrupt.  This clearly isn't the case.

Am I misreading the the documentation?  Have I missed something?

Thanks,

Paul
9 REPLIES 9
Posted on September 10, 2015 at 17:04

> This clearly isn't the case.

So what is the case, and how do you know that?

JW

Posted on September 10, 2015 at 17:47

So what speed does it fire? That should tell you something about the nature of the problem. Is it consistent, random?

Can you output the internal clock via PA8 (MCO) and confirm what that is?

If you're using the external source, are you using the BYPASS option?

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
psdeering
Associate II
Posted on September 10, 2015 at 20:19

Hi Clive,

Thanks for the reply.  With TIM2_PSC set to 0, and TIM2_ARR set to 1, and driven by the HSE (8Mhz), I'm getting an interrupt fire period of 8ms (interrupts every 8ms).  I'm counting to 1000 in the timer interrupt, and when it hits 1000, I toggle the LED - the LED is toggling on for 8sec, and off for 8sec.  Setting the prescaler to a higher value obviously makes this period significantly longer, so the maximum speed I'm getting is an 8ms interrupt period which represents a 125Hz frequency.

As to the HSEBYP, this is clear in the configuration, so no bypass.

Timer 2 configuration:

// Enable Timer 2 peripheral clock

RCC_APB1ENR |= APB1ENR_TIM2;

// Configure Timer 2

TIM2_CR1 = 0;

TIM2_CR2 = 0;

TIM2_DIER = 0x1;

TIM2_PSC = 0;

TIM2_ARR = 1;

TIM2_CR1 |= 1;

HSE (switching from HSI internal RC reference initialised in startup):

// Enable HSE

RCC_CR |= 0x10000;

// Wait for HSE to become stable

while ((RCC_CR & 0x20000) == 0) {

}

// Set HSE as the system clock source

RCC_CFGR &= (~3);

RCC_CFGR |= (1);

Regards,

Paul

Posted on September 10, 2015 at 20:48

PSC=0, ARR = 1?? That would be calling at 4 MHz, which definitely isn't going to work.

PSC=0, ARR = 999; would call at  8 KHz, A count of 8000 would get you 1 second on, 1 second off.

PSC=0, ARR = 8000-1 should get you to 1 KHz (1ms) interrupt periods.

A non-oscillator (ie you don't have a crystal on the pins) clock source should use bypass mode.

I'm not interested in playing register level minutia, perhaps Jan will play.

If the timers aren't running as expected you need to look upstream, as they are being clocked with something other than the frequency you expect.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Posted on September 10, 2015 at 22:47

> PSC=0, ARR = 1?? That would be calling at 4 MHz, which definitely isn't going to work.

That's it. In other words, you are choking in interrupts firing way more rapidly than you can process them.

> I'm not interested in playing register level minutia, perhaps Jan will play

🙂

Yes my methods are probably way out of fashion these days, but even then I prefer using constants and symbols defined in the CMSIS-required stm32fxxxx.h and relying on the debugger to decipher the bits and bitfields; hand-decoding only as a last-resort in the rare cases where these two are not applicable.

For basic playing such as this I simply leave the clock tree alone - the reset values with HSI are good enough for assessment of much of the basic peripherals, and avoids various pitfalls with the crystal oscillators, external clock sources (and the bypass), and PLL.

JW

psdeering
Associate II
Posted on September 11, 2015 at 03:32

Hi Clive, Jan, all,

Thanks for the replies.

Unfortunately, I have to bit bash the hardware, as using the standard libraries isn't going to teach me all that much about the STM32F4 hardware - I have to use the microcontroller in projects in the upcoming months, so I need to get up-to-speed in understanding the hardware at the register level for those projects.

Are there any sources of code snippets which bit-bash the various peripherals directly (the discovery board which I am using has the STM32F401VCT6 microntroller, which is not the microcontroller I will be actually using, but represents a good first step), instead of using the standard libraries directly?  I'm particularly interested in the various timer modes (such as PWM), SPI, I2C, USART, and ADC.

Regards,

Paul

Posted on September 11, 2015 at 09:10

> Unfortunately,

What's unfortunate in that?

What I suggest is that NEVER use numeric values for register content, use the symbols defined in stm32f4xxx.h. That's the only header (plus the ARM's CMSIS headers) you need, and that's the header you definitively need.

What's really unfortunate is, that ST did not define the stm32xxxx.h headers properly - values for bitfields are missing. The've put them into the ''libraries''' headers, together with redefinitions of the single-bit fields, which is IMO simply stupid. I have defined some of the bitfields myself, my augmented (although incompletely - I am not paid by ST to do their job) version of stm32f4xx.h is

http://www.efton.sk/STM32/stm32f4xx.zip

.

An example, setting up the clock (note that being a C-hater, I have defined some of the operators to my liking):

  #define CLOCK_MHZ   160

 

  RCC->CR |= ((uint32_t)RCC_CR_HSEON);  // enable HSE

  while((RCC->CR AND RCC_CR_HSERDY) == 0);  // wait until up

  // before firing up PLL, one word of caution: the VOS bit in PWR_CR register must be 1 for fSYS>144MHz

  // that's the reset value so there's no need to change it here (VOS=0 brings some 10% reduction in power consumption between 30MHz-144MHz)

  // if we'd need to change it, the power module's clock would have to be fired up, too - see the STM-supplied ''systemInit'' code

  RCC->CFGR |=    RCC_CFGR_HPRE_DIV1    // AHB prescaler set to 1 -> AHB clock = 160MHz

               OR RCC_CFGR_PPRE1_DIV4   // APB1 prescaler set to 4 -> APB1 clock = 40MHz (required to be < 42MHz)

               OR RCC_CFGR_PPRE2_DIV2;  // APB2 prescaler set to 2 -> APB2 clock = 80MHz (required to be < 84MHz)

  RCC->PLLCFGR =    ( 12  * RCC_PLLCFGR_PLLM_0)     // M = XTAL divider - the PLL needs 1MHz to 2MHz at its input

                 OR (( 160  * 2) * RCC_PLLCFGR_PLLN_0)    // N = PLL multiplier (feedback divider) - the VCO's output needs to fall between 64MHz and 432MHz

                 OR (0 * RCC_PLLCFGR_PLLP_0)      // P = divider for SYSCLK - 0 = /2, 1 = /4, 2 = /6, 3 = /8

                 OR (7 * RCC_PLLCFGR_PLLQ_0)      // Q = divider for USB (and SDIO and RNG), should result in 48MHz but that's unattainable with this setup - we won't use it anyway - for SDIO and RNG it should be below 48MHz

                 OR (RCC_PLLCFGR_PLLSRC_HSE);     // PLL source is HSE

  RCC->CR |= RCC_CR_PLLON;  // fire up PLL

  while((RCC->CR AND RCC_CR_PLLRDY) == 0);  // and wait until up

  FLASH->ACR =  0

               OR FLASH_ACR_ICEN          // meantime, configure flash for maximum performance - enable both caches

               OR FLASH_ACR_DCEN

               OR FLASH_ACR_PRFTEN        // errata says this is ''unavailable'' for A-revision devices - should we selectively disable this through reading DBGMCU_IDCODE?

               OR FLASH_ACR_LATENCY_5WS;  // for VCC>2.7V and FSYS>144MHz, 5 waitstate is appropriate

  RCC->CFGR = (RCC->CFGR AND (~(RCC_CFGR_SW))) OR RCC_CFGR_SW_PLL;  // switch clock source to PLL

  while ((RCC->CFGR AND RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);  // wait until switches

  // and that's it.

> Are there any sources of code snippets which bit-bash the various peripherals directly

Not for the 'F4 which I am aware of. However, the peripherals are largely compatible across families. Have a look at the 'F0 UM appendix (the 'F0 SPI is quite different though in that it has a FIFO). There is also a package for F0 called Snippets, but I personally found that less illustrative, although it may contain material worth studying.

JW

psdeering
Associate II
Posted on September 11, 2015 at 15:39

Just solved this problem - I had TIM2_ARR cast to a uint16_t, instead of a uint32_t (32bit register), so when I was loading the ARR value as a 16 bit value, it was doubly loading the lower 16 bits into the upper 16 bits of the register - so the ARR value was insanely high, and obviously why there was a massive delay when loading a value other than 1.

The device register display (TIM2) in Keil is definitely my new friend!  Moral of the story is, look at the device registers in the debugger, and make sure they're actually loaded correctly!

So I now have my 1 second on / off flashing LED.  😉

Posted on September 11, 2015 at 16:31

> Just solved this problem - I had TIM2_ARR cast to a uint16_t, instead of a uint32_t (32bit register),

This again would be avoided using the types defined in stm32f4xx.h.

> The device register display (TIM2) in Keil is definitely my new friend!  Moral of the story is, look at the device registers in the debugger, and make sure they're actually loaded correctly!

And even more, most timer-related issues can be debugged without writing a single line of code, simply writing into the registers in the debugger.  Beware though if you go for other peripherals, there are registers which clear-upon-read and there are flag-cleared-upon-register read (see USART for prime example).

JW