Ajay on Non-MMU Embedded Linux
Getting started with buildroot for STM32F469

Buildroot

To me using buildroot to make a embedded linux image is the easiest way to not have to deal with GNU toolchain, packages, kernel, uboot compilation separately.

First clone the buildroot code and install necessary tools:

$ sudo apt update
$ sudo apt install -y 
    build-essential git wget cpio unzip rsync file bc 
    python3 python3-distutils 
    gawk bison flex libncurses5-dev libssl-dev
$ git clone https://git.busybox.net/buildroot

Buildroot ships with many configurations that can help the buid process. In buildroot/configs there is a file named stm32f746_disco_sd_defconfig Use that and configure buildroot to use it.

$ cd buildroot
$ make stm32f746_disco_sd_defconfig

You must see a message that the configuration has been written. Now that the config is written to go ahead and build the code. This can take sometime and so have patience. It builds the toolchain first, libraries, GNU userspace tools, kernel and u-boot etc..

Upon completion, the images are stored in directory: output/images/

The file of interest is sdcard.img, u-boot.bin

This file: u-boot.bin needs to be copied to the internal flash of the MCU. To do so install: STM32 Cube Programmer. I am not detailing that step since that is available on Windows. It requires creating an account at ST Microelectronics and downloading the tool from their website. I use a virtual machine and copy the image from the VM to the windows machine. This is a one time step.

Alt STM32 Cube Programmer Front screen

Fig 1: STM32 Cube Programmer

Download the u-boot.bin file to location 0x08000000

Alt STM32 Program u-boot.bin

Fig 2: STM32 Program u-boot

After that we need to copy the sdcard.img file to a SD Card. Use a card of around 2GB or more. First install a tool called as bmap-tools.

sudo apt update && sudo apt install bmap-tools

After the installation, Insert the memory card and check its drive number.

sudo fdisk -l

You will see an output something like below. What we are interested in is the /dev/sdc 1.77 GiB drive it says it is -SD: SD card.

sudo fdisk -l
Disk /dev/nvme0n1: 1.82 TiB, 2000398934016 bytes, 3907029168 sectors
Disk model: Samsung SSD 990 EVO 2TB                 
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 0E2F804C-6B66-447C-BB88-944E23EF0E9E

Device              Start        End    Sectors  Size Type
/dev/nvme0n1p1       2048    2000895    1998848  976M EFI System
/dev/nvme0n1p2    2000896 3873835007 3871834112  1.8T Linux filesystem
/dev/nvme0n1p3 3873835008 3907028991   33193984 15.8G Linux swap


Disk /dev/sdc: 1.77 GiB, 1903165440 bytes, 3717120 sectors
Disk model: USB3.0 CRW   -SD
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00000000

Device     Boot Start     End Sectors  Size Id Type
/dev/sdc1         153 3717119 3716967  1.8G  6 FAT16

We now write the sdcard.img to the /dev/sdc location

sudo bmaptool copy --nobmap output/images/sdcard.img /dev/sdc

Upon completion, the SD card is ready. Insert it on the board and reboot the board. Plug a USB cable to the serial port and connect a terminal program like: putty

The baudrate is 115200, 8, N, 1. Figure out the serial port number from the device manager. If the host is Linux, the device will be /dev/ttyUSBX or /dev/ttyACMX

$ sudo picocom -b 115200 /dev/ttyACM0 
picocom v3.1

port is        : /dev/ttyACM0
flowcontrol    : none
baudrate is    : 115200
parity is      : none
databits are   : 8
stopbits are   : 1
escape is      : C-a
local echo is  : no
noinit is      : no
noreset is     : no
hangup is      : no
nolock is      : no
send_cmd is    : sz -vv
receive_cmd is : rz -vv -E
imap is        : 
omap is        : 
emap is        : crcrlf,delbs,
logfile is     : none
initstring     : none
exit_after is  : not set
exit is        : no

Type [C-a] [C-h] to see available commands
Terminal ready


U-Boot 2025.01 (Mar 21 2025 - 00:43:24 +0000)

Model: STMicroelectronics STM32F469i-DISCO board
DRAM:  16 MiB
stm32fx_rcc_clock rcc@40023800: set_rate not implemented for clock index 4
stm32fx_rcc_clock rcc@40023800: set_rate not implemented for clock index 4
Core:  38 devices, 22 uclasses, devicetree: separate
Flash: 2 MiB
MMC:   mmc@40012c00: 0
Loading Environment from nowhere... OK
stm32fx_rcc_clock rcc@40023800: set_rate not implemented for clock index 4
cyclic function video_init took too long: 46881us vs 5000us max
In:    serial
Out:   serial
Err:   serial
Hit any key to stop autoboot:  0 
switch to partitions #0, OK
mmc0 is current device
Scanning mmc 0:1...
Found /extlinux/extlinux.conf
Retrieving file: /extlinux/extlinux.conf
1:      stm32f469-disco-buildroot
Retrieving file: /zImage
append: console=ttySTM0,115200 root=/dev/mmcblk0p2 rw rootfstype=ext2 rootwait earlyprintk consoleblank=0 ignore_loglevel
Retrieving file: /stm32f469-disco.dtb
Kernel image @ 0x008000 [ 0x000000 - 0x1a8f10 ]
## Flattened Device Tree blob at 00408000
   Booting using the fdt blob at 0x408000
Working FDT set to 408000
   Loading Device Tree to 00bf8000, end 00bfff44 ... OK
Working FDT set to bf8000

Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 5.14.12 (asharma@dev) (arm-buildroot-uclinux-uclibcgnueabi-gcc.br_real (Buildroot 2024.05.2) 13.3.0, GNU ld (GNU Binutils) 2.41) #5 PREEMPT Sat Jun 21 21:56:32 PDT 2025
[    0.000000] CPU: ARMv7-M [410fc241] revision 1 (ARMv7M), cr=00000000
[    0.000000] CPU: unknown data cache, unknown instruction cache
[    0.000000] OF: fdt: Machine model: STMicroelectronics STM32F469i-DISCO board
[    0.000000] printk: debug: ignoring loglevel setting.
[    0.000000] Zone ranges:
[    0.000000]   Normal   [mem 0x0000000000000000-0x0000000000ffffff]
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000000000000-0x0000000000ffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000000000000-0x0000000000ffffff]
[    0.000000] pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768
[    0.000000] pcpu-alloc: [0] 0 
[    0.000000] Built 1 zonelists, mobility grouping off.  Total pages: 4064
[    0.000000] Kernel command line: console=ttySTM0,115200 root=/dev/mmcblk0p2 rw rootfstype=ext2 rootwait earlyprintk consoleblank=0 ignore_loglevel
[    0.000000] Unknown command line parameters: earlyprintk consoleblank=0
[    0.000000] Dentry cache hash table entries: 2048 (order: 1, 8192 bytes, linear)
[    0.000000] Inode-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] Memory: 12972K/16384K available (1800K kernel code, 347K rwdata, 684K rodata, 84K init, 108K bss, 3412K reserved, 0K cma-reserved)
[    0.000000] SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] rcu: Preemptible hierarchical RCU implementation.
[    0.000000] rcu:     RCU event tracing is enabled.
[    0.000000]  Trampoline variant of Tasks RCU enabled.
[    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 10 jiffies.
[    0.000000] NR_IRQS: 16, nr_irqs: 16, preallocated irqs: 16
[    0.000000] /soc/interrupt-controller@40013c00: bank0
[    0.000000] random: get_random_bytes called from start_kernel+0x247/0x428 with crng_init=0
[    0.000000] clocksource: arm_system_timer: mask: 0xffffff max_cycles: 0xffffff, max_idle_ns: 331816030 ns
[    0.000000] ARM System timer initialized as clocksource
[    0.000022] sched_clock: 32 bits at 90MHz, resolution 11ns, wraps every 23860929530ns
[    0.000620] timer@40000c00: STM32 sched_clock registered
[    0.001146] Switching to timer-based delay loop, resolution 11ns
[    0.001563] timer@40000c00: STM32 delay timer registered
[    0.002052] clocksource: timer@40000c00: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 21236227187 ns
[    0.002813] /soc/timer@40000c00: STM32 clockevent driver initialized (32 bits)
[    0.010069] Calibrating delay loop (skipped), value calculated using timer frequency.. 180.00 BogoMIPS (lpj=900000)
[    0.010821] pid_max: default: 4096 minimum: 301
[    0.014853] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.015565] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.041484] rcu: Hierarchical SRCU implementation.
[    0.046483] devtmpfs: initialized
[    0.242480] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
[    0.243162] pinctrl core: initialized pinctrl subsystem
[    0.519243] platform 40016c00.dsi: Fixing up cyclic dependency with 40016800.display-controller
[    0.549326] stm32f469-pinctrl soc:pin-controller@40020000: No package detected, use default one
[    0.566354] stm32f469-pinctrl soc:pin-controller@40020000: GPIOA bank added
[    0.573587] stm32f469-pinctrl soc:pin-controller@40020000: GPIOB bank added
[    0.580611] stm32f469-pinctrl soc:pin-controller@40020000: GPIOC bank added
[    0.587655] stm32f469-pinctrl soc:pin-controller@40020000: GPIOD bank added
[    0.594938] stm32f469-pinctrl soc:pin-controller@40020000: GPIOE bank added
[    0.602277] stm32f469-pinctrl soc:pin-controller@40020000: GPIOF bank added
[    0.609200] stm32f469-pinctrl soc:pin-controller@40020000: GPIOG bank added
[    0.616515] stm32f469-pinctrl soc:pin-controller@40020000: GPIOH bank added
[    0.623688] stm32f469-pinctrl soc:pin-controller@40020000: GPIOI bank added
[    0.630662] stm32f469-pinctrl soc:pin-controller@40020000: GPIOJ bank added
[    0.637607] stm32f469-pinctrl soc:pin-controller@40020000: GPIOK bank added
[    0.638365] stm32f469-pinctrl soc:pin-controller@40020000: Pinctrl STM32 initialized
[    0.844050] stm32-dma 40026000.dma-controller: STM32 DMA driver registered
[    0.870655] stm32-dma 40026400.dma-controller: STM32 DMA driver registered
[    0.916518] clocksource: Switched to clocksource timer@40000c00
[    0.963723] workingset: timestamp_bits=30 max_order=12 bucket_order=0
[    1.109692] io scheduler mq-deadline registered
[    1.110043] io scheduler kyber registered
[    1.125222] STM32 USART driver initialized
[    1.134634] 40004800.serial: ttySTM0 at MMIO 0x40004800 (irq = 32, base_baud = 2812500) is a stm32-usart
[    1.597385] printk: console [ttySTM0] enabled
[    1.614762] stm32-display-dsi 40016c00.dsi: supply phy-dsi not found, using dummy regulator
[    1.979266] stm32_rtc 40002800.rtc: registered as rtc0
[    1.984760] stm32_rtc 40002800.rtc: setting system clock to 2000-01-01T00:00:00 UTC (946684800)
[    1.995614] stm32_rtc 40002800.rtc: Date/Time must be initialized
[    2.005952] i2c /dev entries driver
[    2.028960] pcf857x 0-0027: probed
[    2.033581] stm32f4-i2c 40005400.i2c: STM32F4 I2C driver registered
[    2.066062] mmci-pl18x 40012c00.mmc: Got CD GPIO
[    2.076735] mmci-pl18x 40012c00.mmc: mmc0: PL180 manf 80 rev8 at 0x40012c00 irq 35,0 (pio)
[    2.150169] mmc0: host does not support reading read-only switch, assuming write-enable
[    2.167262] mmc0: new SDHC card at address 0001
[    2.191548] mmcblk0: mmc0:0001 SD16G 14.6 GiB 
[    2.232496]  mmcblk0: p1 p2
[    2.242929] Hello world init.
[    2.276675] random: fast init done
[    2.286090] [drm] Initialized stm 1.0.0 20170330 for 40016800.display-controller on minor 0
[    2.327454] stm32-display 40016800.display-controller: [drm] fb0: stm frame buffer device
[    2.772762] input: gpio-keys as /devices/platform/gpio-keys/input/input0
[    2.800439] EXT4-fs (mmcblk0p2): mounting ext2 file system using the ext4 subsystem
[    2.824879] EXT4-fs (mmcblk0p2): warning: mounting unchecked fs, running e2fsck is recommended
[    2.978238] EXT4-fs (mmcblk0p2): mounted filesystem without journal. Opts: (null). Quota mode: disabled.
[    2.988780] VFS: Mounted root (ext2 filesystem) on device 179:2.
[    2.998052] devtmpfs: mounted
[    3.008290] Freeing unused kernel image (initmem) memory: 84K
[    3.014195] This architecture does not have kernel memory protection.
[    3.021144] Run /sbin/init as init process
[    3.025343]   with arguments:
[    3.028586]     /sbin/init
[    3.031365]     earlyprintk
[    3.034232]   with environment:
[    3.037620]     HOME=/
[    3.040045]     TERM=linux
[    3.042825]     consoleblank=0
[    3.303782] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null). Quota mode: disabled.
Seeding 2048 bits without crediting
Saving 2048 bits of non-creditable seed for next boot

Welcome to Buildroot
(none) login: root
Jan  1 00:00:05 login[45]: root login on 'console'
~ # 

If you see a prompt to login, you have successfully installed Linux on the board. We continue from this step.