Skip to content

Instantly share code, notes, and snippets.

@s4eidhp
Last active November 12, 2024 06:20
Show Gist options
  • Select an option

  • Save s4eidhp/e7f77c30ea865a1725e14467ae3f042f to your computer and use it in GitHub Desktop.

Select an option

Save s4eidhp/e7f77c30ea865a1725e14467ae3f042f to your computer and use it in GitHub Desktop.

Requirments

  • Install latest version of ARM toolchain
cd ~
wget https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi.tar.xz
tar xf arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi.tar.xz
  • Install openocd, modify its config file path and test it
sudo apt update
sudo apt install openocd

cd /usr/local/share
sudo ln -s /usr/share/openocd .

openocd -f board/stm32f4discovery.cfg

General build process flow

01

Detailed process

02

Linker script

  • Written in linker command language
  • The purpose of the linker script is basically to tell the linker:
    • Where to start executing code when the processor boots
    • Which memory regions exist on the microcontroller
    • Where to place the different code sections in memory
  • final script
ENTRY(reset_handler)

MEMORY
{
  FLASH (rx): ORIGIN = 0x08000000, LENGTH = 1024K
  SRAM (rwx): ORIGIN = 0x20000000, LENGTH = 112K
}

SECTIONS
{
  .isr_vector :
  {
    KEEP(*(.isr_vector))
  } >FLASH

  .text :
  {
    . = ALIGN(4);
		
    *(.text)
    *(.rodata)
		
    . = ALIGN(4);
    _etext = .;
  } >FLASH

  .data :
  {
    . = ALIGN(4);
    _sdata = .;
		
    *(.data)

    . = ALIGN(4);
    _edata = .;
  } >SRAM AT> FLASH

  .bss :
  {
    . = ALIGN(4);
    _sbss = .;
		
    *(.bss)
		
    . = ALIGN(4);
    _ebss = .;
  } >SRAM
}

Startup code

  • In startup.c we must do the following:
    • Initialize the main stack pointer and interrupt vector table in the .isr_vector section
    • Copy the .data section from flash to SRAM
    • Zero-fill the .bss section
  • final code
#include <stdint.h>

#define ISR_VECTOR_SIZE_WORDS 97

#define SRAM_START (0x20000000U)
#define SRAM_SIZE (112U * 1024U)
#define SRAM_END (SRAM_START + SRAM_SIZE)
#define STACK_POINTER_INIT_ADDRESS (SRAM_END)

extern uint32_t _etext, _sdata, _edata, _sbss, _ebss;
void main(void);

void reset_handler(void);
void default_handler(void);
void nmi_handler(void) __attribute__((weak, alias("default_handler")));
void hard_fault_handler(void) __attribute__((weak, alias("default_handler")));
void mem_manage_handler(void) __attribute__((weak, alias("default_handler")));
void bus_fault_handler(void) __attribute__((weak, alias("default_handler")));
void usage_fault_handler(void) __attribute__((weak, alias("default_handler")));
void svcall_handler(void) __attribute__((weak, alias("default_handler")));
void debug_monitor_handler(void) __attribute__((weak, alias("default_handler")));
void pendsv_handler(void) __attribute__((weak, alias("default_handler")));
void systick_handler(void) __attribute__((weak, alias("default_handler")));
// continue adding device interrupt handlers

uint32_t isr_vector[ISR_VECTOR_SIZE_WORDS] __attribute__((section(".isr_vector"))) = {
  STACK_POINTER_INIT_ADDRESS,
  (uint32_t)&reset_handler,
  (uint32_t)&nmi_handler,
  (uint32_t)&hard_fault_handler,
  (uint32_t)&mem_manage_handler,
  (uint32_t)&bus_fault_handler,
  (uint32_t)&usage_fault_handler,
  0,
  0,
  0,
  0,
  (uint32_t)&svcall_handler,
  (uint32_t)&debug_monitor_handler,
  0,
  (uint32_t)&pendsv_handler,
  (uint32_t)&systick_handler,
  // continue adding device interrupt handlers
};

void default_handler(void)
{
  while(1);
}

void reset_handler(void)
{
  // Copy .data from FLASH to SRAM
  uint32_t data_size = (uint32_t)&_edata - (uint32_t)&_sdata;
  uint8_t *flash_data = (uint8_t*) &_etext;
  uint8_t *sram_data = (uint8_t*) &_sdata;
  
  for (uint32_t i = 0; i < data_size; i++)
  {
    sram_data[i] = flash_data[i];
  }

  // Zero-fill .bss section in SRAM
  uint32_t bss_size = (uint32_t)&_ebss - (uint32_t)&_sbss;
  uint8_t *bss = (uint8_t*) &_sbss;

  for (uint32_t i = 0; i < bss_size; i++)
  {
    bss[i] = 0;
  }
  
  main();
}

Main application

  • To create a simple blink app we should do following steps:
    • Enable the peripheral clock for GPIO port A
    • Set PA5 as a push-pull output
    • Toggle the pin at a fixed interval in the super loop
  • final main
#include <stdint.h>

#define PERIPHERAL_BASE (0x40000000U)
#define AHB1_BASE (PERIPHERAL_BASE + 0x20000U)
#define GPIOD_BASE (AHB1_BASE + 0xC00U)
#define RCC_BASE (AHB1_BASE + 0x3800U)

#define RCC_AHB1ENR_OFFSET (0x30U)
#define RCC_AHB1ENR ((volatile uint32_t*) (RCC_BASE + RCC_AHB1ENR_OFFSET))
#define RCC_AHB1ENR_GPIODEN (0x03U)

#define GPIO_MODER_OFFSET (0x00U)
#define GPIOD_MODER ((volatile uint32_t*) (GPIOD_BASE + GPIO_MODER_OFFSET))
#define GPIO_MODER_MODER13 (26U)
#define GPIO_ODR_OFFSET (0x14U)
#define GPIOD_ODR ((volatile uint32_t*) (GPIOD_BASE + GPIO_ODR_OFFSET))

#define LED_PIN 13

void main(void)
{
  *RCC_AHB1ENR |= (1 << RCC_AHB1ENR_GPIODEN);

  // do two dummy reads after enabling the peripheral clock, as per the errata
  volatile uint32_t dummy;
  dummy = *(RCC_AHB1ENR);
  dummy = *(RCC_AHB1ENR);

  *GPIOD_MODER |= (1 << GPIO_MODER_MODER13);
  
  while(1)
  {
    *GPIOD_ODR ^= (1 << LED_PIN);
    for (uint32_t i = 0; i < 1000000; i++);
  }

}

Building & Execute

cd ~
arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi/bin/arm-none-eabi-gcc main.c startup.c -T linker.ld -o blink.elf -mcpu=cortex-m4 -mthumb -nostdlib
  • To check which memory sections an .elf or .o file contains
arm-none-eabi-objdump -h <filename>
# OR
arm-none-eabi-readelf -S <filename>
  • Program and verify
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c "program blink.elf verify reset exit"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment