Using the systick timer.html 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. <html><head>
  2. <meta http-equiv="content-type" content="text/html; charset=windows-1252"><title>Using the systick timer</title></head>
  3. <body>
  4. Working example available <a href="http://eleceng.dit.ie/frank/arm/BareMetalSTM32F0Discovery/systick.tar.gz">here</a><br>
  5. <h2>The systick timer</h2><br>
  6. The cortex m0 contains a 24 bit counter that can be used to generate
  7. periodic interrupts. Typically this is used as a timebase for an
  8. operating system (for example to allocate time slices) although this
  9. example
  10. will use it as a simple periodic interrupt source. The clock tree in
  11. the F0 discovery is a little tricky to get the hang of so care needs to
  12. be taken. It is also possible to select a clock source that is too
  13. fast for your cpu. This may brick your system or it may cause the
  14. system to reject your clock settings and revert to a slow default clock
  15. setting. BE CAREFUL WITH THE CLOCK! <br>
  16. <h2>The code</h2>
  17. While writing this code I found the following to be of use:<br>
  18. PM0215: STM32F0xxx Programming Manual <br>
  19. RM0091: Reference Manual for STM32F05xxx advanced ARM-based 32-bit MCUs <br>
  20. Cortex-M0 Technical Reference from ARM <br>
  21. Cortex-M0 Generic User Guide from ARM <br>
  22. The code has a number of initialization functions. The first of these
  23. is initClock. This function sets the processor clock to 48MHz. The AHB
  24. and APB busses are also set to run at this speed. The ADC is set
  25. to run at 12MHz (can't go faster than 14MHz). FLASH memory access speeds
  26. need to be modified at this speed also (wait state inserted). For
  27. testing purposes I routed the HSI (RC based 8MHz oscillator) out
  28. onto PA8 for a while to verify that the HSI clock speed was indeed 8MHz
  29. (it was). This code is still present but commented out. Finally, a
  30. global variable called SysTickCounter is set to zero. This
  31. is used later in the code.
  32. <pre>void initClock()
  33. {
  34. // This is potentially a dangerous function as it could
  35. // result in a system with an invalid clock signal - result: a stuck system
  36. // Set the PLL up
  37. // First ensure PLL is disabled
  38. RCC_CR &amp;= ~BIT24;
  39. while( (RCC_CR &amp; BIT25)); // wait for PLL ready to be cleared
  40. // set PLL multiplier to 12 (yielding 48MHz)
  41. // Warning here: if system clock is greater than 24MHz then wait-state(s) need to be
  42. // inserted into Flash memory interface
  43. FLASH_ACR |= BIT0;
  44. FLASH_ACR &amp;=~(BIT2 | BIT1);
  45. // Turn on FLASH prefetch buffer
  46. FLASH_ACR |= BIT4;
  47. RCC_CFGR &amp;= ~(BIT21 | BIT20 | BIT19 | BIT18);
  48. RCC_CFGR |= (BIT21 | BIT19 );
  49. // Need to limit ADC clock to below 14MHz so will change ADC prescaler to 4
  50. RCC_CFGR |= BIT14;
  51. // Do the following to push HSI clock out on PA8 (MCO)
  52. // for measurement purposes. Should be 8MHz or thereabouts (verified with oscilloscope)
  53. /*
  54. RCC_CFGR |= ( BIT26 | BIT24 );
  55. RCC_AHBENR |= BIT17;
  56. GPIOA_MODER |= BIT17;
  57. */
  58. // and turn the PLL back on again
  59. RCC_CR |= BIT24;
  60. // set PLL as system clock source
  61. RCC_CFGR |= BIT1;
  62. // zero the systick counter
  63. SysTickCounter = 0;
  64. }
  65. </pre><br>
  66. The above code sets up the internal clocks in the device. The next step
  67. is to enable the systick timer. The function initSysTick does this.
  68. The STM32F0 processor has a calibration register that
  69. contains the value 6000. If this value is used as for systick counter
  70. reload then an interrupt will be generated every millisecond.
  71. <pre>void initSysTick()
  72. {
  73. STK_CSR |= ( BIT1 | BIT1 | BIT0); // enable systick, source = cpu clock, enable interrupt
  74. // SysTick clock source = 48MHz/8. Divide this 6MHz by 6000 (from calib register) to create a 1ms timebase
  75. STK_RVR = (STK_CALIB &amp; 0xffffff);
  76. STK_CVR = 1; // don't want long wait for counter to count down from initial high unknown value
  77. enable_interrupts();
  78. }
  79. </pre><br>
  80. The systick interrupt service routine simply increments a counter
  81. (SysTickCounter) at each interrupt.
  82. When the count reaches 1000, it toggles the blue led on the discovery
  83. board. The result is that the blue led changes state every second.
  84. The interrupt vector for this service routine is setup in the file <strong>init.c</strong> an excerpt of which is shown here:<br>
  85. <pre>const void * Vectors[] __attribute__((section(".vectors"))) ={
  86. (void *)0x20002000, /* Top of stack */
  87. init, /* Reset Handler */
  88. Default_Handler, /* NMI */
  89. Default_Handler, /* Hard Fault */
  90. 0, /* Reserved */
  91. 0, /* Reserved */
  92. 0, /* Reserved */
  93. 0, /* Reserved */
  94. 0, /* Reserved */
  95. 0, /* Reserved */
  96. 0, /* Reserved */
  97. Default_Handler, /* SVC */
  98. 0, /* Reserved */
  99. 0, /* Reserved */
  100. Default_Handler, /* PendSV */
  101. SysTick, /* SysTick */
  102. /* External interrupt handlers follow */
  103. :
  104. :
  105. </pre>
  106. <br>
  107. The interrupt handler is shown here <br>
  108. <pre>void SysTick()
  109. {
  110. // This should occur at a rate of 1kHz.
  111. SysTickCounter++;
  112. if (SysTickCounter &gt;1000)
  113. {// toggle the blue led so we can verify timing
  114. SysTickCounter = 0;
  115. GPIOC_ODR ^= 0x00000100;
  116. }
  117. }
  118. </pre>
  119. <h2>Further reading</h2><br>
  120. For more examples go <a href="http://eleceng.dit.ie/frank/arm/BareMetalSTM32F0Discovery/index.html">back to STM32F0Discovery home</a><br>
  121. Other microcontroller examples are in <a href="http://eleceng.dit.ie/frank/arm/index.html">Bare-metal ARM home </a><br>
  122. </body></html>