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