mirror of
https://github.com/rfivet/stm32bringup.git
synced 2024-12-20 15:58:44 -05:00
266 lines
7.4 KiB
HTML
266 lines
7.4 KiB
HTML
|
<!DOCTYPE html>
|
|||
|
<html lang="en">
|
|||
|
<head>
|
|||
|
<meta charset="UTF-8">
|
|||
|
<title>1.8 Three-stage Rocket</title>
|
|||
|
<link type="text/css" rel="stylesheet" href="style.css">
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<h1>1.8 Three-stage Rocket</h1>
|
|||
|
|
|||
|
As I merge the cstartup with the ledtick code, I split the
|
|||
|
functionalities between three files according to the three phases: boot,
|
|||
|
initialization and main execution.
|
|||
|
<ul>
|
|||
|
<li> <b>startup.c</b> is mainly concerned with the early set up of the board:
|
|||
|
initializing the memory, calling system initialization and then
|
|||
|
executing the main C function if initialization is successful.
|
|||
|
</ul><ul>
|
|||
|
<li> <b>init.c</b> holds the middleware implementation and will abstract the
|
|||
|
peripherals into higher level interfaces. Its entry point is the
|
|||
|
<code>init()</code> function.
|
|||
|
</ul><ul>
|
|||
|
<li> the <code>main()</code> C function will be the focus of the last file and
|
|||
|
it should be written in a subset of standard C. As an example I will use
|
|||
|
<b>success.c</b>.
|
|||
|
</ul>
|
|||
|
<pre>
|
|||
|
/* success.c -- success does nothing, successfully */
|
|||
|
#include <stdlib.h>
|
|||
|
|
|||
|
int main( void) {
|
|||
|
return EXIT_SUCCESS ;
|
|||
|
}
|
|||
|
</pre>
|
|||
|
|
|||
|
<h2>startup.c</h2>
|
|||
|
|
|||
|
Beside the interrupt vector and the <code>Reset_Handler()</code> that calls
|
|||
|
<code>init()</code> and <code>main()</code>, I have created stubs for all the
|
|||
|
System Exceptions listed in the Programming Manual. For those, if there is no
|
|||
|
implementation avalable in <b>init.c</b>, the linker will use the
|
|||
|
<code>Default_Handler()</code> provided with the <code>__attribute__()</code>
|
|||
|
Gnu C extension.
|
|||
|
|
|||
|
<pre>
|
|||
|
/* Memory locations defined by linker script */
|
|||
|
extern long __StackTop ; /* &__StackTop points after end of stack */
|
|||
|
void Reset_Handler( void) ; /* Entry point for execution */
|
|||
|
extern const long __etext[] ; /* start of initialized data copy in flash */
|
|||
|
extern long __data_start__[] ;
|
|||
|
extern long __bss_start__[] ;
|
|||
|
extern long __bss_end__ ; /* &__bss_end__ points after end of bss */
|
|||
|
|
|||
|
/* Stubs for System Exception Handler */
|
|||
|
void Default_Handler( void) ;
|
|||
|
#define dflt_hndlr( fun) void fun##_Handler( void) \
|
|||
|
__attribute__((weak,alias("Default_Handler")))
|
|||
|
dflt_hndlr( NMI) ;
|
|||
|
dflt_hndlr( HardFault) ;
|
|||
|
dflt_hndlr( SVCall) ;
|
|||
|
dflt_hndlr( PendSV) ;
|
|||
|
dflt_hndlr( SysTick) ;
|
|||
|
|
|||
|
/* 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,
|
|||
|
NMI_Handler,
|
|||
|
HardFault_Handler,
|
|||
|
0, 0, 0, 0, 0, 0, 0,
|
|||
|
SVCall_Handler,
|
|||
|
0, 0,
|
|||
|
PendSV_Handler,
|
|||
|
SysTick_Handler
|
|||
|
} ;
|
|||
|
|
|||
|
extern int init( void) ;
|
|||
|
extern int main( void) ;
|
|||
|
|
|||
|
void Reset_Handler( void) {
|
|||
|
const long *f ; /* from, source constant data from FLASH */
|
|||
|
long *t ; /* to, destination in RAM */
|
|||
|
|
|||
|
/* Assume:
|
|||
|
** __bss_start__ == __data_end__
|
|||
|
** All sections are 4 bytes aligned
|
|||
|
*/
|
|||
|
f = __etext ;
|
|||
|
for( t = __data_start__ ; t < __bss_start__ ; t += 1)
|
|||
|
*t = *f++ ;
|
|||
|
|
|||
|
while( t < &__bss_end__)
|
|||
|
*t++ = 0 ;
|
|||
|
|
|||
|
if( init() == 0)
|
|||
|
main() ;
|
|||
|
|
|||
|
for( ;;)
|
|||
|
__asm( "WFI") ; /* Wait for interrupt */
|
|||
|
}
|
|||
|
|
|||
|
void Default_Handler( void) {
|
|||
|
for( ;;) ;
|
|||
|
}
|
|||
|
</pre>
|
|||
|
|
|||
|
Except for the future addition of stubs to handle the device specific
|
|||
|
interrupts, this file will not grow much anymore.
|
|||
|
|
|||
|
<h2>init.c</h2>
|
|||
|
|
|||
|
This is the embryo of an hardware abstraction layer where most of the
|
|||
|
device specific code will be added. The current code is the peripherals
|
|||
|
part of <b>ledtick.c</b>.
|
|||
|
|
|||
|
<pre>
|
|||
|
#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]
|
|||
|
|
|||
|
int init( 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 */
|
|||
|
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
void SysTick_Handler( void) {
|
|||
|
GPIOB_ODR ^= 1 << 1 ; /* Toggle PB1 (User LED) */
|
|||
|
}
|
|||
|
</pre>
|
|||
|
|
|||
|
<h2>Makefile</h2>
|
|||
|
|
|||
|
As I now build from multiple source files, I have modified the
|
|||
|
<b>Makefile</b> to list the sources that combine together. All steps I have
|
|||
|
done so far can be found in the commented <code>SRCS</code> lines. Single file
|
|||
|
steps can be build explicitly (<code>make ledon.hex</code>) or implicitly
|
|||
|
(<code>make</code>) after removing the comment on the corresponding
|
|||
|
<code>SRCS</code> line. Multiple file steps can only be build implicitly when
|
|||
|
their <code>SRCS</code> line is uncommented.
|
|||
|
|
|||
|
<pre>
|
|||
|
### Build environment selection
|
|||
|
|
|||
|
ifeq (linux, $(findstring linux, $(MAKE_HOST)))
|
|||
|
GCCDIR = $(HOME)/Packages/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi
|
|||
|
else
|
|||
|
GCCDIR = "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-13.3.rel1-mingw-w64-i686-arm-none-eabi"
|
|||
|
endif
|
|||
|
|
|||
|
BINPFX = @$(GCCDIR)/bin/arm-none-eabi-
|
|||
|
CC = $(BINPFX)gcc
|
|||
|
LD = $(BINPFX)ld
|
|||
|
OBJCOPY = $(BINPFX)objcopy
|
|||
|
OBJDUMP = $(BINPFX)objdump
|
|||
|
SIZE = $(BINPFX)size
|
|||
|
|
|||
|
### STM32F030F4P6 based board
|
|||
|
|
|||
|
PROJECT = f030f4
|
|||
|
#SRCS = boot.c
|
|||
|
#SRCS = ledon.c
|
|||
|
#SRCS = blink.c
|
|||
|
#SRCS = ledtick.c
|
|||
|
#SRCS = cstartup.c
|
|||
|
SRCS = startup.c init.c success.c
|
|||
|
OBJS = $(SRCS:.c=.o)
|
|||
|
CPU = -mthumb -mcpu=cortex-m0
|
|||
|
CFLAGS = $(CPU) -g -Wall -Wextra -Os
|
|||
|
LD_SCRIPT = $(PROJECT).ld
|
|||
|
|
|||
|
### Build rules
|
|||
|
|
|||
|
.PHONY: clean all
|
|||
|
|
|||
|
all: $(PROJECT).hex $(PROJECT).bin
|
|||
|
|
|||
|
clean:
|
|||
|
@echo CLEAN
|
|||
|
@rm -f *.o *.elf *.map *.lst *.bin *.hex
|
|||
|
|
|||
|
$(PROJECT).elf: $(OBJS)
|
|||
|
@echo $@
|
|||
|
$(LD) -T$(LD_SCRIPT) -Map=$(PROJECT).map -cref -o $@ $(OBJS)
|
|||
|
$(SIZE) $@
|
|||
|
$(OBJDUMP) -hS $@ > $(PROJECT).lst
|
|||
|
|
|||
|
%.elf: %.o
|
|||
|
@echo $@
|
|||
|
$(LD) -T$(LD_SCRIPT) -Map=$*.map -cref -o $@ $<
|
|||
|
$(SIZE) $@
|
|||
|
$(OBJDUMP) -hS $@ > $*.lst
|
|||
|
|
|||
|
%.bin: %.elf
|
|||
|
@echo $@
|
|||
|
$(OBJCOPY) -O binary $< $@
|
|||
|
|
|||
|
%.hex: %.elf
|
|||
|
@echo $@
|
|||
|
$(OBJCOPY) -O ihex $< $@
|
|||
|
</pre>
|
|||
|
|
|||
|
A successful build will generate the files <b>f030f4.hex</b>, <b>f030f4.bin</b>,
|
|||
|
<b>f030f4.map</b>, <b>f030f4.lst</b>.
|
|||
|
|
|||
|
<h2>Build and Test</h2>
|
|||
|
|
|||
|
Even if <b>stdlib.h</b> is included in <b>success.c</b>, there is no C
|
|||
|
libraries needed to complete the build as only the constant
|
|||
|
<code>EXIT_SUCCESS</code> from that header is used. Furthermore, default
|
|||
|
location of the header files is derived by the compiler from the location of
|
|||
|
gcc.
|
|||
|
|
|||
|
<pre>
|
|||
|
$ make
|
|||
|
f030f4.elf
|
|||
|
text data bss dec hex filename
|
|||
|
216 0 0 216 d8 f030f4.elf
|
|||
|
f030f4.hex
|
|||
|
f030f4.bin
|
|||
|
</pre>
|
|||
|
|
|||
|
Building shows an increase in code, still no data.
|
|||
|
<p>
|
|||
|
Once <b>f030f4.hex</b> is loaded into the board, the behavior is the same
|
|||
|
as <b>ledtick.hex</b>. The new file structure and data initialization
|
|||
|
didn’t introduce any <del>bugs</del> changes, just code overhead.
|
|||
|
|
|||
|
<h2>Checkpoint</h2>
|
|||
|
|
|||
|
This step was mainly to achieve a better structure for future evolution.
|
|||
|
<p>
|
|||
|
<a href="19_publish.html">Next</a>, I will make the code available in a
|
|||
|
public git repository.
|
|||
|
|
|||
|
<hr>© 2020-2024 Renaud Fivet
|
|||
|
</body>
|
|||
|
</html>
|