123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- <html><head>
- <meta http-equiv="content-type" content="text/html; charset=windows-1252"><title>Using the systick timer</title></head>
- <body>
- Working example available <a href="http://eleceng.dit.ie/frank/arm/BareMetalSTM32F0Discovery/systick.tar.gz">here</a><br>
- <h2>The systick timer</h2><br>
- The cortex m0 contains a 24 bit counter that can be used to generate
- periodic interrupts. Typically this is used as a timebase for an
- operating system (for example to allocate time slices) although this
- example
- will use it as a simple periodic interrupt source. The clock tree in
- the F0 discovery is a little tricky to get the hang of so care needs to
- be taken. It is also possible to select a clock source that is too
- fast for your cpu. This may brick your system or it may cause the
- system to reject your clock settings and revert to a slow default clock
- setting. BE CAREFUL WITH THE CLOCK! <br>
- <h2>The code</h2>
- While writing this code I found the following to be of use:<br>
- PM0215: STM32F0xxx Programming Manual <br>
- RM0091: Reference Manual for STM32F05xxx advanced ARM-based 32-bit MCUs <br>
- Cortex-M0 Technical Reference from ARM <br>
- Cortex-M0 Generic User Guide from ARM <br>
- The code has a number of initialization functions. The first of these
- is initClock. This function sets the processor clock to 48MHz. The AHB
- and APB busses are also set to run at this speed. The ADC is set
- to run at 12MHz (can't go faster than 14MHz). FLASH memory access speeds
- need to be modified at this speed also (wait state inserted). For
- testing purposes I routed the HSI (RC based 8MHz oscillator) out
- onto PA8 for a while to verify that the HSI clock speed was indeed 8MHz
- (it was). This code is still present but commented out. Finally, a
- global variable called SysTickCounter is set to zero. This
- is used later in the code.
- <pre>void initClock()
- {
- // This is potentially a dangerous function as it could
- // result in a system with an invalid clock signal - result: a stuck system
- // Set the PLL up
- // First ensure PLL is disabled
- RCC_CR &= ~BIT24;
- while( (RCC_CR & BIT25)); // wait for PLL ready to be cleared
- // set PLL multiplier to 12 (yielding 48MHz)
- // Warning here: if system clock is greater than 24MHz then wait-state(s) need to be
- // inserted into Flash memory interface
- FLASH_ACR |= BIT0;
- FLASH_ACR &=~(BIT2 | BIT1);
- // Turn on FLASH prefetch buffer
- FLASH_ACR |= BIT4;
- RCC_CFGR &= ~(BIT21 | BIT20 | BIT19 | BIT18);
- RCC_CFGR |= (BIT21 | BIT19 );
- // Need to limit ADC clock to below 14MHz so will change ADC prescaler to 4
- RCC_CFGR |= BIT14;
- // Do the following to push HSI clock out on PA8 (MCO)
- // for measurement purposes. Should be 8MHz or thereabouts (verified with oscilloscope)
- /*
- RCC_CFGR |= ( BIT26 | BIT24 );
- RCC_AHBENR |= BIT17;
- GPIOA_MODER |= BIT17;
- */
- // and turn the PLL back on again
- RCC_CR |= BIT24;
- // set PLL as system clock source
- RCC_CFGR |= BIT1;
- // zero the systick counter
- SysTickCounter = 0;
- }
- </pre><br>
- The above code sets up the internal clocks in the device. The next step
- is to enable the systick timer. The function initSysTick does this.
- The STM32F0 processor has a calibration register that
- contains the value 6000. If this value is used as for systick counter
- reload then an interrupt will be generated every millisecond.
- <pre>void initSysTick()
- {
-
- STK_CSR |= ( BIT1 | BIT1 | BIT0); // enable systick, source = cpu clock, enable interrupt
- // SysTick clock source = 48MHz/8. Divide this 6MHz by 6000 (from calib register) to create a 1ms timebase
- STK_RVR = (STK_CALIB & 0xffffff);
- STK_CVR = 1; // don't want long wait for counter to count down from initial high unknown value
- enable_interrupts();
- }
- </pre><br>
- The systick interrupt service routine simply increments a counter
- (SysTickCounter) at each interrupt.
- When the count reaches 1000, it toggles the blue led on the discovery
- board. The result is that the blue led changes state every second.
- The interrupt vector for this service routine is setup in the file <strong>init.c</strong> an excerpt of which is shown here:<br>
- <pre>const void * Vectors[] __attribute__((section(".vectors"))) ={
- (void *)0x20002000, /* Top of stack */
- init, /* Reset Handler */
- Default_Handler, /* NMI */
- Default_Handler, /* Hard Fault */
- 0, /* Reserved */
- 0, /* Reserved */
- 0, /* Reserved */
- 0, /* Reserved */
- 0, /* Reserved */
- 0, /* Reserved */
- 0, /* Reserved */
- Default_Handler, /* SVC */
- 0, /* Reserved */
- 0, /* Reserved */
- Default_Handler, /* PendSV */
- SysTick, /* SysTick */
- /* External interrupt handlers follow */
- :
- :
- </pre>
- <br>
- The interrupt handler is shown here <br>
- <pre>void SysTick()
- {
- // This should occur at a rate of 1kHz.
- SysTickCounter++;
- if (SysTickCounter >1000)
- {// toggle the blue led so we can verify timing
- SysTickCounter = 0;
- GPIOC_ODR ^= 0x00000100;
- }
- }
- </pre>
- <h2>Further reading</h2><br>
- For more examples go <a href="http://eleceng.dit.ie/frank/arm/BareMetalSTM32F0Discovery/index.html">back to STM32F0Discovery home</a><br>
- Other microcontroller examples are in <a href="http://eleceng.dit.ie/frank/arm/index.html">Bare-metal ARM home </a><br>
- </body></html>
|