mirror of
https://github.com/rfivet/stm32bringup.git
synced 2024-12-20 15:58:44 -05:00
170 lines
5.5 KiB
HTML
170 lines
5.5 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>1.6 The Tick</title>
|
||
<link type="text/css" rel="stylesheet" href="style.css">
|
||
</head>
|
||
<body>
|
||
<h1>1.6 The Tick</h1>
|
||
|
||
<img alt="It’s blue! It blinks! It’s the Tick!" src="img/16_tick.png">
|
||
<p>
|
||
In previous iteration, I made the user LED blink using an active delay
|
||
loop. I have two issues with this implementation:
|
||
<ul>
|
||
<li> It’s hard to control the delay timing accurately
|
||
</ul><ul>
|
||
<li> Active loops are not cool
|
||
</ul>
|
||
So I am going to call in some serious reinforcement, which means one of
|
||
the Arm Core Peripherals: the System Tick.
|
||
<p>
|
||
What the System Tick does is very similar to my active delay loop as
|
||
can be seen from the following pseudo-code.
|
||
|
||
<pre>
|
||
while( enabled) {
|
||
if( --current_value == 0) {
|
||
current_value = reload_value ;
|
||
countflag = true ;
|
||
if( interrupt_enabled)
|
||
SysTick_Handler() ;
|
||
}
|
||
}
|
||
</pre>
|
||
|
||
It’s an auto decremented counter that reloads and sets a flag when
|
||
reaching zero. It can trigger a system interrupt if requested to. Its
|
||
default clock is the processor clock and can be switched to external
|
||
clock. Details can be found in the Programming Manual as this is part of
|
||
Arm Core.
|
||
|
||
<h2>Code, build and test</h2>
|
||
|
||
I copy <b>blink.c</b> into <b>ledtick.c</b> to make the following
|
||
modifications:
|
||
<ul>
|
||
<li> Expand the interrupt vector to make room for all 15 System Exceptions
|
||
from <b>Reset_Handler</b> to <b>SysTick_Handler</b>.
|
||
</ul><ul>
|
||
<li> Introduce the <b>SysTick</b> core peripheral to the compiler, using
|
||
pre-processor macroes to give the location of SysTick registers. As this
|
||
is a core peripheral, it’s in a different address space than the
|
||
peripherals I have seen so far.
|
||
</ul><ul>
|
||
<li> Start the <b>Reset_Handler</b> by initializing and enabling the System
|
||
Tick. The reload value register is initialized with a constant based on
|
||
the internal clock value divided by 8. As I want one tick per second and
|
||
the default internal clock is 8 MHz, SysTick will have to count one
|
||
million steps, from 999999 to 0. So the reload value is 999999.
|
||
</ul><ul>
|
||
<li> Create the <b>SysTick_Handler</b> interrupt routine which toggles GPIO
|
||
B1.
|
||
</ul><ul>
|
||
<li> Replace the previous active delay loop by a <b>cool</b> idle loop.
|
||
Instead of doing nothing actively, I instruct the processor to wait for
|
||
interrupt, which means it will suspend until waked up by the SysTick.
|
||
This is one way to lower power consumption, besides, waiting for SysTick
|
||
interrupt is really the only thing left to do.
|
||
</ul>
|
||
<pre>
|
||
/* Memory locations defined by linker script */
|
||
extern long __StackTop ; /* &__StackTop points after end of stack */
|
||
void Reset_Handler( void) ; /* Entry point for execution */
|
||
|
||
void SysTick_Handler( void) ;
|
||
|
||
/* Interrupt vector table:
|
||
* 1 Stack Pointer reset value
|
||
* 15 System Exceptions
|
||
* NN Device specific Interrupts
|
||
*/
|
||
typedef void (*isr_p)( void) ;
|
||
isr_p const isr_vector[ 16] __attribute__((section(".isr_vector"))) = {
|
||
(isr_p) &__StackTop,
|
||
/* System Exceptions */
|
||
Reset_Handler,
|
||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||
SysTick_Handler
|
||
} ;
|
||
|
||
#define SYSTICK ((volatile long *) 0xE000E010)
|
||
#define SYSTICK_CSR SYSTICK[ 0]
|
||
#define SYSTICK_RVR SYSTICK[ 1]
|
||
#define SYSTICK_CVR SYSTICK[ 2]
|
||
|
||
#define RCC ((volatile long *) 0x40021000)
|
||
#define RCC_AHBENR RCC[ 5]
|
||
#define RCC_AHBENR_IOPBEN 0x00040000 /* 18: I/O port B clock enable */
|
||
|
||
#define GPIOB ((volatile long *) 0x48000400)
|
||
#define GPIOB_MODER GPIOB[ 0]
|
||
#define GPIOB_ODR GPIOB[ 5]
|
||
|
||
void Reset_Handler( void) {
|
||
/* By default SYSCLK == HSI [8MHZ] */
|
||
|
||
/* SYSTICK */
|
||
SYSTICK_RVR = 1000000 - 1 ; /* HBA / 8 */
|
||
SYSTICK_CVR = 0 ;
|
||
SYSTICK_CSR = 3 ; /* HBA / 8, Interrupt ON, Enable */
|
||
/* SysTick_Handler will execute every 1s from now on */
|
||
|
||
/* User LED ON */
|
||
RCC_AHBENR |= RCC_AHBENR_IOPBEN ; /* Enable IOPB periph */
|
||
GPIOB_MODER |= 1 << (1 * 2) ; /* PB1 Output [01], over default 00 */
|
||
/* OTYPER Push-Pull by default */
|
||
/* PB1 output default LOW at reset */
|
||
|
||
for( ;;)
|
||
__asm( "WFI") ; /* Wait for interrupt */
|
||
}
|
||
|
||
void SysTick_Handler( void) {
|
||
GPIOB_ODR ^= 1 << 1 ; /* Toggle PB1 (User LED) */
|
||
}
|
||
</pre>
|
||
|
||
I didn’t initialize the GPIO B before enabling the SysTick as I have a
|
||
whole second before the first interrupt will tick in.
|
||
<p>
|
||
Build is straightforward.
|
||
|
||
<pre>
|
||
$ make ledtick.hex
|
||
ledtick.elf
|
||
text data bss dec hex filename
|
||
148 0 0 148 94 ledtick.elf
|
||
ledtick.hex
|
||
rm ledtick.o ledtick.elf
|
||
</pre>
|
||
|
||
If I compare with blink.hex, 56 bytes of the 80 bytes code increase are
|
||
due to the expansion of the interrupt vector.
|
||
<p>
|
||
Once flashed in the board I can see that the LED changes state every second.
|
||
|
||
<h2>Checkpoint</h2>
|
||
|
||
I now have the foundation for timing and a first taste of shifting
|
||
execution between the main loop and an interrupt routine.
|
||
<p>
|
||
Code size has been growing steadily since the first bootstrap. On the
|
||
other hand, except for the stack, I have not used RAM memory so far.
|
||
|
||
<pre>
|
||
│ │ text │ data │ bss │
|
||
├────────┼──────┼───────┼───────┤
|
||
│<b>boot</b> │ 10 │ 0 │ 0 │
|
||
│<b>ledon</b> │ 40 │ 0 │ 0 │
|
||
│<b>blink</b> │ 68 │ 0 │ 0 │
|
||
│<b>ledtick</b> │ 148 │ 0 │ 0 │
|
||
</pre>
|
||
|
||
<a href="17_cstartup.html">Next</a>, I will focus on RAM initialization.
|
||
|
||
<hr>© 2020-2024 Renaud Fivet
|
||
</body>
|
||
</html>
|