Preface
At the beginning I’d like to mention that I’m not a guru in that matter, I’ve never been hired as an embedded system developer, I’m rather a hobbyist.
The goal of this article is to summarize the experience that I’ve gained so far. So, if you see some outrageous mistakes feel free to leave a comment.
Some time ago, I decided to start my journey through embedded systems. I bought an appropriate device (BeagleBone Black), read articles, googled information related to that particular subject.
What information has showed up the most often? 404…
I was really surprised by the number of "404 Not found" that showed up :/
Some crucial data were stored on that sites; sadly only on them.
Sad, disappointed, tired I kept searching and I found mailing list very helpful!
If you have a serious problem or you stuck, it is a good place to ask for help.
The general idea of this article is to point some useful materials/information for beginners in order to spare them some time 🙂 So, below I present some mailing lists which will be useful for some:
At this point I owned a prototype board, I had some general information about the Angstrom system (which is official BeagleBone Black distro), so I started writing my first embedded application ever.
After hello world I’ve realised that there is no (or I couldn’t find) any decent C++ GPIO library. That was a challenge for me (at least at the beginning). Here you have detailed description of my work: BeagleBone Black C++ GPIO library for beginners
After the library was done I decided to create a small project, here you can find the results: One-wire DS1820 thermometer with BeagleBone Black & libmicrohttpd
While I’ve been working on the project I realized how important is properly set up environment. Copying files manually, compiling on target (shame on me) or setting up correct library paths on different targets was really annoying. I set my sights on cleaning the mess I’ve done.
In this article I’m going to present you some tools, configurations and practical problems solutions which I had to struggle with (literally).
Qemu target
At the beginning of my journey with embedded development I used to deploy on real machine (BBB). The minus of that solution is that the device must be turned on and available through internet (in a case that I’m not at home). Qemu is an alternative!
Before I start to explain how does the qemu work, I’d like to say smth more about bealgebone or beagleboard in general.
How does the boot process look like on BBB?
General information
There are three files that you need to boot system on BBB:
- MLO
- u-boot
- zImage/uImage
MLO stands for MMC Loader, it is simply a x-loader which task is to initialize low level stuff (i.e set up the pin muxing, initialize the clock memory and run u-boot).
U-boot stands for universal bootloader, its task is to initialize boot for specific platform and the most important: boot the kernel file.
zImage is compiled linux kernel file which sets up peripherals and mount the filesystem (in brief)
uImage is also the linux kernel but converted into a special format understood by u-boot
IMPORTANT: It is not required to create uImage. zImage is all you need, u-boot from some version accept zImage (via bootz command). For some newbies like me it wasn’t so obvious 😛
In summary: MLO -> u-boot -> linux kernel -> initd
Some useful links:
- Bootloader in details: http://omappedia.org/wiki/Bootloader_Project
- MLO and RBL in details: http://coherentmusings.wordpress.com/2012/09/05/what-is-mlo-file/comment-page-1/
- U-boot on qemu explained: http://balau82.wordpress.com/2010/04/12/booting-linux-with-u-boot-on-qemu-arm/
U-boot
At this point we know that u-boot sets up some bootargs and boots the kernel.
What are those bootargs and where should I set them? Let’s take a look on real example:
# Our goal is to boot the linux system using qemu-linaro on beagleXm machine, so perform:
qemu-system-arm -M beaglexm -drive if=sd,cache=writeback,file=/dev/sdb -clock unix -serial stdio
# What we see is
U-Boot SPL 2013.07 (Mar 10 2014 - 16:10:52)
OMAP SD/MMC: 0
reading u-boot.img
reading u-boot.img
U-Boot 2013.07 (Mar 10 2014 - 16:10:52)
OMAP36XX/37XX-GP ES1.1, CPU-OPP2, L3-165MHz, Max CPU Clock 1 Ghz
OMAP3 Beagle board + LPDDR/NAND
I2C: ready
DRAM: 512 MiB
NAND: 256 MiB
MMC: OMAP SD/MMC: 0
*** Warning - bad CRC, using default environment
In: serial
Out: serial
Err: serial
Beagle xM Rev A
No EEPROM on expansion board
Die ID #51454d5551454d555400000051454d55
musb-hdrc: ConfigData=0x06 (UTMI-8, dyn FIFOs, SoftConn)
musb-hdrc: MHDRC RTL version 1.400
musb-hdrc: setup fifo_mode 4
musb-hdrc: 28/31 max ep, 16384/16384 memory
USB Peripheral mode controller at 480ab000 using PIO, IRQ 0
Net: usb_ether
Hit any key to stop autoboot: 0
mmc0 is current device
gpio: pin 173 (gpio 173) value is 0
gpio: pin 4 (gpio 4) value is 0
SD/MMC found on device 0
reading uEnv.txt
** Unable to read file uEnv.txt **
** File not found /boot/uImage **
Booting from nand ...
NAND read: device 0 offset 0x280000, size 0x400000
4194304 bytes read: OK
Wrong Image Format for bootm command
ERROR: can't get kernel image!
OMAP3 beagleboard.org #
As you can easily see the x-loader works (mmc0 is up), u-boot is read into memory and requests for uEnv.txt. What is that?
uEnv.txt is a file containing all information needed to run the kernel. Let’s take a look into that file:
OMAP3 beagleboard.org # printenv
baudrate=115200
beaglerev=xMA
bootargs=console=ttyO2,115200n8 mpurate=auto buddy=none camera=none vram=12M omapfb.mode=dvi:640x480MR-16@60 omapdss.def_disp=dvi root=ubi0:rootfs ubi.mtd=4 rootfstype=ubifs
bootcmd=mmc dev ${mmcdev}; if mmc rescan; then if run userbutton; then setenv bootenv uEnv.txt;else setenv bootenv user.txt;fi;echo SD/MMC found on device ${mmcdev};if run loadbootenv; then echo Loaded environment from ${bootenv};run importbootenv;fi;if test -n $uenvcmd; then echo Running uenvcmd ...;run uenvcmd;fi;if run loaduimage; then run mmcboot;fi;fi;run nandboot;
bootdelay=3
bootenv=uEnv.txt
bootfile=uImage.beagle
buddy=none
camera=none
console=ttyO2,115200n8
defaultdisplay=dvi
dieid#=51454d5551454d555400000051454d55
dvimode=640x480MR-16@60
ethact=usb_ether
importbootenv=echo Importing environment from mmc ...; env import -t $loadaddr $filesize
loadaddr=0x80200000
loadbootenv=fatload mmc ${mmcdev} ${loadaddr} ${bootenv}
loadramdisk=fatload mmc ${mmcdev} ${rdaddr} ramdisk.gz
loaduimage=ext2load mmc ${mmcdev}:2 ${loadaddr} /boot/uImage
loaduimagefat=fatload mmc ${mmcdev} ${loadaddr} uImage
mmcargs=setenv bootargs console=${console} ${optargs} mpurate=${mpurate} buddy=${buddy} camera=${camera} vram=${vram} omapfb.mode=dvi:${dvimode} omapdss.def_disp=${defaultdisplay} root=${mmcroot} rootfstype=${mmcrootfstype}
mmcboot=echo Booting from mmc ...; run mmcargs; bootm ${loadaddr}
mmcdev=0
mmcroot=/dev/mmcblk0p2 rw
mmcrootfstype=ext3 rootwait
mpurate=auto
nandargs=setenv bootargs console=${console} ${optargs} mpurate=${mpurate} buddy=${buddy} camera=${camera} vram=${vram} omapfb.mode=dvi:${dvimode} omapdss.def_disp=${defaultdisplay} root=${nandroot} rootfstype=${nandrootfstype}
nandboot=echo Booting from nand ...; run nandargs; nand read ${loadaddr} 280000 400000; bootm ${loadaddr}
nandroot=ubi0:rootfs ubi.mtd=4
nandrootfstype=ubifs
ramargs=setenv bootargs console=${console} ${optargs} mpurate=${mpurate} buddy=${buddy} vram=${vram} omapfb.mode=dvi:${dvimode} omapdss.def_disp=${defaultdisplay} root=${ramroot} rootfstype=${ramrootfstype}
ramboot=echo Booting from ramdisk ...; run ramargs; bootm ${loadaddr}
ramroot=/dev/ram0 rw ramdisk_size=65536 initrd=0x81000000,64M
ramrootfstype=ext2
rdaddr=0x81000000
stderr=serial
stdin=serial
stdout=serial
usbtty=cdc_acm
userbutton=if gpio input 173; then run userbutton_xm; else run userbutton_nonxm; fi;
userbutton_nonxm=gpio input 7;
userbutton_xm=gpio input 4;
vram=12M
Environment size: 2535/131068 bytes
The line below force u-boot to search the uImage on second mmc partition (ext3), /boot/uImage
loaduimage=ext2load mmc ${mmcdev}:2 ${loadaddr} /boot/uImage
The line below presents bootargs which are passed to kernel (kernel must be compiled in that way to accept the bootargs):
bootargs=console=ttyO2,115200n8 mpurate=auto buddy=none camera=none vram=12M omapfb.mode=dvi:640x480MR-16@60 omapdss.def_disp=dvi root=ubi0:rootfs ubi.mtd=4 rootfstype=ubifs
What if I want redirect the output to tty0?
Do it by setenv in u-boot console or via uEnv.txt or hardcode the command in kernel image
setenv bootargs 'console=tty0 console=ttyO2,115200n8 root=/dev/mmcblk0p2 rw rdinit=/sbin/init rootfstype=ext3 rootwait'
What if I don’t have uImage?
The first option is to create one, the second boot zImage instead.
# set bootcmd
setenv bootcmd 'mmc init; fatload mmc 0:1 0x80300000 zImage.bin; bootz 0x80300000'
# create uImage
mkimage -A arm -O linux -T kernel -C none -a 0x80008000 -e 0x80008000 -n "Linux kernel" -d ./zImage uImage
PLEASE READ IT IF YOU RESPECT YOUR TIME:
Recently I’ve spend about three days trying to boot system generated by yocto on linaro-qemu beagle xm machine (as in examples). Kernel boots up but there is no LOGIN PROMPT.
I’ve tried almost everything, without a success. I’m pretty sure that the problem is caused by some changes in kernel (tested on v. 3.10): serial console changed from ttyS0 to ttyO2 and qemu have problem with that :/ In older versions such as 2.6 its working via ttyS0.
Bug: https://bugs.launchpad.net/qemu-linaro/+bug/714600
Prepare disk or sdcard
Your sdcard must be omap3 compatibile, so format it that way:
wget http://cgit.openembedded.org/cgit.cgi/openembedded/plain/contrib/angstrom/omap3-mkcard.sh
chmod +x omap3-mkcard.sh
sudo ./omap3-mkcard.sh /dev/sdX
sync
# Print information about sdcard
fdisk -l /dev/sdX
Device Boot Start End Blocks ID System
/dev/sdb1 * 63 144584 72261 c W95 FAT32 (LBA)
/dev/sdb2 144585 2008124 931770 83 Linux
# If yo want to make an image of sdcard
dd if=/dev/sdX of=./myimage.img
# Mount it
sudo mount -t auto -o loop,offset=144585 myimage.img /mnt/
Of course you can format sdcard manually (use fdisk -c=dos -u=cylinders /dev/sdX):
undefined
Build the system
At this point we know how to deal with bootloader, qemu and sdcard. There is only one thing that we must do: Build the system or use precompiled one.
NOTE: Building system (compiling packages) takes a lot of time, be patient and prepare some free space on your disk.
There are some embedded distributions that might be useful for you (If you don’t want to build your own):
- Ångström linux – official BBB distro
- Arch Linux ARM – port of arch linux
- Embedded Debian – port of debian linux
Angstrom linux seems to be quite popular, I’ve never seen so many 404 on any site 🙂
To help you out, below I’ve listed some useful urls:
- Narcissus – customize and build your angstrom online
- Demo images – beagleboard demo images with sdcard images (very useful)
If you are a pro and you want to build your own customized system, you can do it by:
Buildroot is very well described here: http://elinux.org/images/2/2a/Using-buildroot-real-project.pdf
Yocto is very large and extensive project it allows you to build customized linux distro.
I prefer to use buildroot, because its much faster and I can easily control the packages. Yocto is quite heavy. Anyway I’m using both for testing (I’m still a beginner).
To start with yocto follow: https://www.yoctoproject.org/docs/current/yocto-project-qs/yocto-project-qs.html
To start with buildroot follow: http://buildroot.uclibc.org/downloads/manual/manual.html#getting-buildroot
Let us assume that you managed to build your own yocto image (with sparate rootfs), so how to write the rootfs on sdcard?
Install the boot loaders:
# cp MLO-beagleboard /media/boot/MLO
# cp u-boot-beagleboard.bin /media/boot/u-boot.bin
Install the root filesystem:
# tar x -C /media/root -f core-image-$IMAGE_TYPE-image.tar.bz2
# tar x -C /media/root -f modules-$KERNEL_VERSION-image.tgz
Install the kernel uImage:
# cp uImage-beagleboard.bin /media/boot/uImage
What about IDE support and crosscompilation?
Both yocto and buildroot generates toolchain for you with possibility to use them in Eclipse
Buildroot: https://github.com/mbats/eclipse-buildroot-toolchain-plugin
Yocto: https://www.yoctoproject.org/tools-resources/projects/eclipse-ide-plug
Libraries and includes?
It happens that you make some changes on your dev system i.e. install extra libraries (without rebuilding the rootfs in yocto dir) and then eclipse cannot find the libraries.
My solution is to mount the rootfs image and prefix compiler to that location
(–sysroot="/mnt/mysysfs"). After each re-mount eclipse will reindex the directory.
Script:
#/usr/bin/bash
ROOTFS="/media/qemu-angstrom-rootfs/"
if [ ! -d "$ROOTFS" ]; then
sudo mkdir $ROOTFS
fi
COUNT_ELEMENTS=$(ls -l $ROOTFS | wc -l)
if [ $COUNT_ELEMENTS -le 1 ]; then
sudo mount -t ext3 -o loop ./Angstrom-systemd-image-eglibc-ipk-v2013.06-qemuarm.devel.ext3 /media/qemu-angstrom-rootfs
fi
sudo qemu-system-arm -nographic -kernel zImage -net nic,vlan=0 -net tap,vlan=0,ifname=tap1,script=/home/kaczanowsky/sh/angstrom-ifup,downscript=no -M versatilepb -hda Angstrom-systemd-image-eglibc-ipk-v2013.06-qemuarm.devel.ext3 -no-reboot -m 256 --append "root=/dev/sda rw console=ttyAMA0,115200 console=ttyS0 ip=192.168.8.2::192.168.8.1:255.255.255.0 mem=256M highres=off"
Some useful commands
# Configure yocto kernel
bitbake linux-yocto -c menuconfig
# Compile yocto kernel
bitbake virtual/kernel
# Import machine configuration in buildroot
make qemu_arm_vexpress_defconfig
# Compile kernel only in buildroot
make -j4 linux
QT5 and OpenGL
My goal was to install qt5 on my distro with qpe (opengl) as a default renderer. I’m still working on that but I’m having troubles with qemu which probably still is bugged. So, expect this section to be updated soon.
Till then some useful links:
- Qt with beagleboard xm: http://qt-project.org/wiki/TIBeagleBoard
- Qt5 on beaglebone black: http://gpupowered.org/node/20
- OpenGL acceleration: https://wiki.linaro.org/Resources/HowTo/Qemu-beagleboard
UPDATE: I have failed :/ (But I’ll keep trying). I can’t redirect login prompt to tty0. I’ve also tried with ttyS2 but still without success. In kernel configuration I saw that the framebuffer is enabled with standard linux logo, so I guess that there is smth wrong with framebuffer (or it should be configured somehow) because no logo is on screen.
If you know solution for any of above problems, please leave a comment.