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>
|