STM32/ARM Cortex-M3 HOWTO: Development under Ubuntu (Debian)

The mini example

But how much do we actually need?

If we lower the standard to a bare minimum, and only focus on this:

  1. It shall build
  2. Shall be flashable
  3. Runs in some sense, visible with gdb is good enough

Then it turn out that we can cut away a lot of code, and this is actually all we need.

Filename: main.c

#define STACK_TOP 0x20000800

void nmi_handler(void);
void hardfault_handler(void);
int main(void);

// Define the vector table
unsigned int * myvectors[4] 
__attribute__ ((section("vectors")))= {
    (unsigned int *)	STACK_TOP,         // stack pointer
    (unsigned int *) 	main,              // code entry point
    (unsigned int *)	nmi_handler,       // NMI handler (not really)
    (unsigned int *)	hardfault_handler  // hard fault handler (let's hope not)
};


int main(void)
{
    int i=0;

    while(1)
    {
        i++;
    }
}

void nmi_handler(void)
{
    return ;
}

void hardfault_handler(void)
{
    return ;
}

The linker file is the same as before.

Filename: stm32.ld

MEMORY
{
  ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
  rom (rx)  : ORIGIN = 0x00000000, LENGTH = 128K
}
SECTIONS
{
    .  = 0x0;         /* From 0x00000000 */
    .text : 
    {
        *(vectors)    /* Vector table */
        *(.text)      /* Program code */
        *(.rodata)    /* Read only data */
    } >rom

    .  = 0x20000000;  /* From 0x20000000 */      
    .data : 
    {
        *(.data)      /* Data memory */
    } >ram AT > rom

    .bss :
    {
        *(.bss)       /* Zero-filled run time allocate data memory */
    } >ram AT > rom
}  

And since we don't compile more than one file the Makefile is smaller as well.

Filename: Makefile

CC      = arm-none-eabi-gcc
LD      = arm-none-eabi-ld -v
CP      = arm-none-eabi-objcopy
OD      = arm-none-eabi-objdump
  
CFLAGS  =  -I./ -c -fno-common -O0 -g -mcpu=cortex-m3 -mthumb 
LFLAGS  = -Tstm32.ld -nostartfiles
CPFLAGS = -Obinary
ODFLAGS = -S

all: test

clean:
	-rm main.lst main.o main.elf main.lst main.bin

test: main.elf
	@ echo "...copying"
	$(CP) $(CPFLAGS) main.elf main.bin
	$(OD) $(ODFLAGS) main.elf > main.lst

main.elf: main.o stm32.ld 
	@ echo "..linking"
	$(LD) $(LFLAGS) -o main.elf main.o    

main.o: main.c
	@ echo ".compiling"
	$(CC) $(CFLAGS) main.c

The only thing in this example that is a little magic is the array called myvectors. If we look a little bit closer we find that it has a directive called, __attribute__ ((section("vectors"))). And this "vectors" can be found in the stm32.ld linker file.

.  = 0x0;         /* From 0x00000000 */
.text : 
{
    *(vectors)    /* Vector table */
    *(.text)      /* Program code */
    *(.rodata)    /* Read only data */
} >rom

So we actually tell the linker to put this array in flash, starting at address 0x0. And there is also where we find the the Nested vectored interrupt controller, NVIC. This actually just gives some startup values for PC and SP, so to get the core running we don't need to provide much info.

But beware, everything but the core is turned off since all clocks are turned off...

But we can connect to it with gdb, and we can see the integer increase when you step. So it is not much, but it is small :)

This page is licensed with Creative Commons ShareAlike 3.0