Working example available here

The systick timer


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!

The code

While writing this code I found the following to be of use:
PM0215: STM32F0xxx Programming Manual
RM0091: Reference Manual for STM32F05xxx advanced ARM-based 32-bit MCUs
Cortex-M0 Technical Reference from ARM
Cortex-M0 Generic User Guide from ARM
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.
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;
}

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.
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();
}

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 init.c an excerpt of which is shown here:
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 */
	:
	:

The interrupt handler is shown here
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;
        }
}

Further reading


For more examples go back to STM32F0Discovery home
Other microcontroller examples are in Bare-metal ARM home