1
0
mirror of https://github.com/rfivet/stm32bringup.git synced 2024-12-20 15:58:44 -05:00
stm32bringup/docs/16_ledtick.html

170 lines
5.5 KiB
HTML
Raw Normal View History

<!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="Its blue! It blinks! Its 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> Its 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>
Its 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, its 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 didnt 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>