Building an ARM cross compiler from source

There are many ways to get an arm cross compiler installed on your platform of choice, but sometimes using a pre-built binary toolchain is not an option. I had such a case, where I had to use the latest version of GCC because that was what the rest of the project was using. At the time, there were no pre-built toolchains available, which contained the latest version of GCC.

There are tools to make the job easier. One such a tool is crosstool-NG, but to be honest, I did not have much success with it. Anyway, I foolishly decided to do it the hard way because I also wanted to understand the process. What follows, is the recipe I followed to get everything built. I will try to point out some gotchas or tips along the way. Be warned, that this is a tedious process with a million different things that can go wrong. It literally took me weeks to get all the issues ironed out, researching issues as I went along. Anyway, enough waffling, lets get on with it!

Preparation
First, we need to obtain the source code packages for the different components that will eventually make up the toolchain. These are as follows:

Package Version
Binutils 2.24
Glibc 2.19
GMP 5.1.3
MPFR 3.1.2
Linux kernel 2.6.31.14
Boost 1.55.0

Technically, Boost is not part of the toolchain, but I am including it since we are building a C/C++ toolchain.

Download the source packages from their respective websites, or source control repositories. I don’t have links to these sites, Google is your friend.

I am using Ubuntu 12.10 as the host platform. Make sure that the build essentials package is installed:
sudo apt-get install build-essential

Also, if the compile fails somewhere along the line, make sure the libgmp-dev, libmpfr-dev and libmpc-dev are installed for your host platform.

The default shell on Ubuntu is Dash, which is incompatible with some of the shell scripts used in the building process. We need to make sure that /bin/sh points to bash, before we can begin:

sudo dpkg-reconfigure dash

A screen will popup, select ‘No’.

Environment
This process is loosely based on the description found in the book Building Embedded Linux Systems, but was adapted with the required changes for the latest versions being built. These step by step instructions are meant to be run from a single command line console, in the sequence described here. The whole process takes about an hour and a half, so take your time and concentrate on each step, since it is advisable to start over until the whole process goes through flawlessly.

Here are some steps to prepare the environment:

  1. Create directory from where we will work.
  2. Create two sub directories, one called ‘sources’ and the other one ‘build’.
  3. Extract the source packages that was downloaded, into the ‘sources’ directory. Each package will be in it’s own directory.
  4. Open a command console in the build directory and copy and paste the instructions into the console, as we go along.
  5. We will need write access to the destination path.

Building
The process makes use of environment variables, to keep track of stuff. Each section contains shell code to create a clean directory in the build directory, from where the actual build will take place. This makes it easy to start over if we need to, while keeping the source directory in it’s original state. Modify the following environment variable to reflect your environment and copy them to the console:

SRCDIR=/home/username/ARM/sources
BUILDMACH=i686-pc-linux-gnu
TARGETMACH=arm-unknown-linux-gnueabi
BUILDDIR=/home/username/ARM/build
INSTALLDIR=/opt/arm/$TARGETMACH
SYSROOTDIR=$INSTALLDIR/sysroot

Binutils
We start by building binutils. This contains the tools like the linker, which we will need to link the object files generated by GCC.

Do the configuring:
[ -d $BUILDDIR/binutils ] && rm -rf $BUILDDIR/binutils
mkdir $BUILDDIR/binutils
cd $BUILDDIR/binutils

$BINUTILS_SRC/configure \
--disable-werror \
--build=$BUILDMACH \
--target=$TARGETMACH \
--prefix=$INSTALLDIR \
--with-sysroot=$SYSROOTDIR \
--disable-nls

The run the following commands individually:
make
make install

Kernel headers
The kernel headers will contain the header files and descriptions of things like integer size, of the target system. These commands can all be pasted in one go:
KERNEL_SRC=$SRCDIR/linux-2.6.31.14
TARGETARCH=arm

cd $KERNEL_SRC
make mrproper
make ARCH=$TARGETARCH integrator_defconfig

[ -d $SYSROOTDIR/usr ] && rm -rf $SYSROOTDIR/usr
mkdir $SYSROOTDIR/usr

make ARCH=$TARGETARCH headers_check
make ARCH=$TARGETARCH INSTALL_HDR_PATH=$SYSROOTDIR/usr headers_install

Bootstrap GCC
Now that the headers and binutils are in place, we are going to build the most basic of GCC compilers, called the bootstrap GCC. This compiler does not contain a C library yet, but it is perfectly suitable for writing bare metal code. This compiler will be used to build the C library later on.

Do the configuring:

[ -d $BUILDDIR/bootstrap-gcc ] && rm -rf $BUILDDIR/bootstrap-gcc
mkdir $BUILDDIR/bootstrap-gcc
cd $BUILDDIR/bootstrap-gcc

$GCC_SRC/configure \
--build=$BUILDMACH \
--host=$BUILDMACH \
--target=$TARGETMACH \
--prefix=$INSTALLDIR \
--without-headers \
--enable-bootstrap \
--enable-languages=c \
--disable-threads \
--enable-__cxa_atexit \
--disable-libmudflap \
--with-gnu-ld \
--with-gnu-as \
--disable-libssp \
--disable-libgomp \
--disable-nls \
--disable-shared

The following commands must be run one at a time:
make all-gcc install-gcc

make all-target-libgcc install-target-libgcc

ln -s $INSTALLDIR/lib/gcc/$TARGETMACH/4.8.2/libgcc.a $INSTALLDIR/lib/gcc/$TARGETMACH/4.8.2/libgcc_sh.a

GLibC headers
Building the GLibC library is a two step process. The reason for the two steps, is to get around the chicken and egg situation, that the GLibC library needs headers built by the pre processor, but the headers do not exist yet. To get around this, the developers introduced a process whereby only the headers are built. The output of this process will then be used by the actual compiling of the library.

Do the configuring:
LIBC_SRC=$SRCDIR/glibc-2.19

[ -d $BUILDDIR/libc ] && rm -rf $BUILDDIR/libc
mkdir -p $BUILDDIR/libc
cd $BUILDDIR/libc

echo "libc_cv_forced_unwind=yes" > config.cache
echo "libc_cv_c_cleanup=yes" >> config.cache
export PATH=$INSTALLDIR/bin:$PATH

export CROSS=$TARGETMACH
export CC=${CROSS}-gcc
export LD=${CROSS}-ld
export AS=${CROSS}-as

$LIBC_SRC/configure \
--build=$BUILDMACH \
--host=$TARGETMACH \
--prefix=$SYSROOTDIR/usr \
--with-headers=$SYSROOTDIR/usr/include \
--config-cache \
--enable-add-ons \
ports=yes \
--enable-kernel=2.6.31

Installing the headers:
make -k install-headers cross_compiling=yes install_root=$SYSROOTDIR

We need a symbolic link:
ln -s $INSTALLDIR/lib/gcc/$TARGETMACH/4.8.2/libgcc.a $INSTALLDIR/lib/gcc/$TARGETMACH/4.8.2/libgcc_eh.a

BuildingGLibC
Now that the headers are installed we build the actual C library.

Do the configuring:
[ -d $BUILDDIR/libc ] && rm -rf $BUILDDIR/libc
mkdir -p $BUILDDIR/libc
cd $BUILDDIR/libc

echo "libc_cv_forced_unwind=yes" > config.cache
echo "libc_cv_c_cleanup=yes" >> config.cache

export CROSS=$TARGETMACH
export CC=${CROSS}-gcc
export LD=${CROSS}-ld
export AS=${CROSS}-as

$LIBC_SRC/configure \
--build=$BUILDMACH \
--host=$TARGETMACH \
--prefix=/usr \
--with-headers=$SYSROOTDIR/usr/include \
--config-cache \
--enable-add-ons \
--enable-kernel=2.6.31 \
ports=yes \
CFLAGS=-O

Run the following commands one at a time:
make -k install-headers cross_compiling=yes install_root=$SYSROOTDIR

make

make install_root=${SYSROOTDIR} install

Building the next GCC
Now that we have a working C library, we are ready to build a version of GCC that makes use of it.

Do the configuring:
[ -d $BUILDDIR/final-gcc ] && rm -rf $BUILDDIR/final-gcc
mkdir -p $BUILDDIR/final-gcc
cd $BUILDDIR/final-gcc

echo "libc_cv_forced_unwind=yes" > config.cache
echo "libc_cv_c_cleanup=yes" >> config.cache
export CC=gcc
export LD=ld
export AS=as

$GCC_SRC/configure \
--build=$BUILDMACH \
--target=$TARGETMACH \
--prefix=$INSTALLDIR \
--with-sysroot=${SYSROOTDIR} \
--enable-languages=c,c++ \
--with-gnu-as \
--with-gnu-ld \
--disable-multilib \
--disable-libmudflap \
--with-float=soft \
--disable-sjlj-exceptions \
--disable-nls \
--enable-threads=posix \
--enable-long-longx \
--enable-__cxa_atexit

Run the following commands one at a time:
make all-gcc
make install-gcc

Now that we have a version of GCC that contains a C library, we are ready to build the support libraries that will be needed by the final version of GCC.

GMP
GMP is a library with all sorts of arithmetic functions, as well as support for big numbers. This library will be needed by the final GCC.

Do the configuring:
LIBGMP_SRC=$SRCDIR/gmp-5.1.3

[ -d $BUILDDIR/gmp ] && rm -rf $BUILDDIR/gmp
mkdir -p $BUILDDIR/gmp
cd $BUILDDIR/gmp

export CROSS=$TARGETMACH
export CC=${CROSS}-gcc
export LD=${CROSS}-ld
export AS=${CROSS}-as
export CFLAGS=-static

$LIBGMP_SRC/configure \
--build=$BUILDMACH \
--host=$TARGETMACH \
--prefix=$INSTALLDIR \
--disable-shared

Run the following two commands one at a time:
make
make install

MPFR
The MPFR library contains code that implements precision floating point calculations. This library will be needed by the final version of GCC.

Do the configuring:
LIBMPFR_SRC=$SRCDIR/mpfr-3.1.2

[ -d $BUILDDIR/mpfr ] && rm -rf $BUILDDIR/mpfr
mkdir -p $BUILDDIR/mpfr
cd $BUILDDIR/mpfr

export CROSS=$TARGETMACH
export CC=${CROSS}-gcc
export LD=${CROSS}-ld
export AS=${CROSS}-as
export CFLAGS=-static

$LIBMPFR_SRC/configure \
--build=$BUILDMACH \
--host=$TARGETMACH \
--prefix=$INSTALLDIR \
--with-gmp=$INSTALLDIR

Run the following two commands one at a time:
make
make install

Building the final GCC
At last we have everything we need to build the final version of the GCC toolchain.

Do the configuring:
[ -d $BUILDDIR/final-gcc-2 ] && rm -rf $BUILDDIR/final-gcc-2
mkdir -p $BUILDDIR/final-gcc-2
cd $BUILDDIR/final-gcc-2

export CC=gcc
export LD=ld
export AS=as
export CFLAGS=

echo "libc_cv_forced_unwind=yes" > config.cache
echo "libc_cv_c_cleanup=yes" >> config.cache

$GCC_SRC/configure \
--prefix=$INSTALLDIR \
--target=$TARGETMACH \
--build=$BUILDMACH \
--with-sysroot=${SYSROOTDIR} \
--bindir=$INSTALLDIR/bin \
--disable-nls \
--enable-shared \
--disable-multilib \
--disable-libmudflap \
--disable-libgomp \
--enable-languages=c,c++ \
--enable-c99 \
--enable-long-long \
--with-float=soft \
--with-mode=arm \
--with-arch=armv5te \
--with-abi=aapcs-linux \
--with-mpfr=$INSTALLDIR \
--with-gmp=$INSTALLDIR

Run the following two commands one at a time:
make
make install

If everything went according plan, you should be able to run the following command to test your shiny new toolchain installation:
arm-unknown-linux-gnueabi-gcc -v

You should see something like this:
Using built-in specs.
COLLECT_GCC=arm-unknown-linux-gnueabi-gcc
COLLECT_LTO_WRAPPER=/usr/lib/arm-unknown-linux-gnueabi/libexec/gcc/arm-unknown-linux-gnueabi/4.8.2/lto-wrapper
Target: arm-unknown-linux-gnueabi
Configured with: /home/username/ARM/sources/gcc-4.8.2/configure --prefix=/usr/lib/arm-unknown-linux-gnueabi --target=arm-unknown-linux-gnueabi --build=i686-pc-linux-gnu --with-sysroot=/usr/lib/sysroot/arm-unknown-linux-gnueabi --bindir=/usr/lib/arm-unknown-linux-gnueabi/bin --disable-nls --enable-shared --disable-multilib --disable-libmudflap --disable-libgomp --enable-languages=c,c++ --enable-c99 --enable-long-long --with-float=soft --with-mode=arm --with-arch=armv5te --with-abi=aapcs-linux --with-mpfr=/usr/lib/arm-unknown-linux-gnueabi --with-gmp=/usr/lib/arm-unknown-linux-gnueabi
Thread model: posix
gcc version 4.8.2 (GCC)

Building Boost libraries
As mentioned before, the boost libraries are technically not part of the toolchain. I am adding them, because some of my projects rely on them.

The Boost libraries make use of an annoying proprietary build system, that almost require one to study it completely just for the sake of compiling the source code. I am not going to try and explain the how’s or why’s, because I am not that familiar with it. Allot of Googling was required to figure this one out.

One thing I must mention, it took me a couple of hours to figure out that the build just does not work, when not done from within the source directory. It is generally good practice to build normal open source projects outside of the source, but this one is an exception.

Firstly, edit the file tools/build/v2/user-config.jam and add the following line under the ‘GCC configuration’ section:
using gcc : arm : arm-unknown-linux-gnueabi-g++ ;

Run the bootstrap shell script that sets everything up:
./bootstrap.sh

Now we can perform the actual build:
./b2 install toolset=gcc-arm --prefix=$INSTALLDIR/usr

We’re almost done. Before we can use the libraries, we need to do some more housekeeping to get around boost peculiarities. A convention seems to exist to add ‘-mt’ to the end of library names to indicate a multi thread safe version of a library. As far as I can ascertain the multithreaded and normal libraries are exactly the same, so we can merely add symbolic links to the original library. Below are the libraries that I have come across, that needs ‘-mt’ versions. You might very well encounter more, just add the links and you should be OK. Just don’t blame me if this is not the case.

ln -s libboost_filesystem.a libboost_filesystem-mt.a
ln -s $INSTALLDIR/usr/lib/libboost_filesystem.so $INSTALLDIR/usr/lib/libboost_filesystem-mt.so
ln -s $INSTALLDIR/usr/lib/libboost_regex.a $INSTALLDIR/usr/lib/libboost_regex-mt.a
ln -s $INSTALLDIR/usr/lib/libboost_regex.so $INSTALLDIR/usr/lib/libboost_regex-mt.so
ln -s $INSTALLDIR/usr/lib/libboost_system.a $INSTALLDIR/usr/lib/libboost_system-mt.a
ln -s $INSTALLDIR/usr/lib/libboost_system.so $INSTALLDIR/usr/lib/libboost_system-mt.so

That’s it, I hope you got this far without any major issues. If not, trace your previous steps CAREFULLY for any typos. It is advisable to start from scratch when an error occurred, I know it is tedious but there are no shortcuts in this complex process.

Binary blobs

This is a very short article on a neat feature of the GNU compile tools, that I don’t think many people are aware of. It is often required to include large sets of binary data with an executable. One option is to just include it as a separate data file. But there are cases where you might not have access to a storage medium. Another option is to convert it to a byte array and declare it as a constant array variable, but a large data set will quickly make your source files cumbersome to work with.

A neat feature of the GNU tools, is the ability to link a separate binary file right into the executable. This is ideal for our kernel project, where I included a font file as well a logo file, right into the kernel image file. The way this is done, is to use the objcopy tool, to convert the file into an object file as follows:

objcopy -I binary -O elf-littlearm32 -B arm ./bin/binfile.dat binfile.o

The resulting object file is then linked along with the other object files. The -I tells objcopy that our input file is in binary format. The -O indicates that our output format is elf-littlearm32. The -B indicates that our architecture is arm and the remaining two parameters are the input and out filenames.

Having a binary file linked into the executable is only half of the magic, though. We are not using it yet. The way we reference the data from our code, is a bit awkward at first. The linker resolves the memory occupied by our binary data to standard variables that we can reference from our code. To be exact, three variables are available, a start and end memory address pointer and a length variable. Using the command in previous example, results in the following variables:

unsigned char _binary_bin_binfile_dat_start
unsigned char _binary_bin_binfile_dat_end
unsigned int _binary_bin_binfile_dat_size

A little closer inspection should make it obvious how the linker comes up with variable names, based on the parameters we supplied. To see the variables that the linker provided from our previous example, run the following command:

objdump -x binfile.o

The variables _binary_bin_binfile_dat_start and _binary_bin_binfile_dat_end are pointers and should be used as follows:

unsigned char *ptr_start=(unsigned char*)&_binary_bin_binfile_dat_start;
unsigned char *ptr_end=(unsigned char*)&_binary_bin_binfile_dat_end;

And there you have it! A neat way to handle messy data sets without making your head explode.

Raspberry Pi Frame buffer

In my previous article I introduced you to VarkOS, my experimental OS for learning about the ARM architecture. In this article I will go over the steps involved in getting to the point where we can display text and graphics on the screen. For source code that implements what we are going to do here, head over to my Github repository for a copy of the complete VarkOS project, or view the framebuffer source code here.

The frame buffer is essentially a block of memory, which gets mapped to the screen coordinates, based on some parameters we configure. But before we can get into the nitty gritty of how this work, we need to understand how our platform, the Raspberry Pi, works.

The Raspberry Pi CPU is actually a high performance graphical processor with an ARM CPU strapped to its back. The CPU, ARM1176JZF-S, basically boots by reading the firmware files from the SD card, loads our kernel image into memory and starts executing it. Communication between the ARM CPU and the graphical processor, is by means of a mailbox mechanism. The mailbox is nothing other than a set hardware registers that are accessible by both processors. From our point of view, we write our message to memory and then pass the memory address of our message to the GPU, via the mailbox.

Mailbox

The mailbox’s hardware register addresses are found at the following addresses:

Register Address
Base 0x2000B880
Poll 0x2000B890
Sender 0x2000B894
Status 0x2000B898
Configuration 0x2000B89C
Write 0x2000B8A0

We are only going to concern ourselves with some of these registers. Writing to the mailbox is done as follows:

  1. Read the status register (0x2000B898).
  2. Check if bit 31 (0x80000000) is set.
  3. If not, go back to step 1 else proceed to next step.
  4. The data to be written, has to be left shifted by 4 bits. This converts our address to 28 bits.
  5. The lower 4 bits will contain the mailbox channel number, combine this with 28bit address by performing a logical OR with the 28 bit address from step 4.
  6. Write the data to the Write register (0x2000B8A0).

We read from the mailbox as follows:

  1. Read the status (0x2000B898) register.
  2. Check if bit 30 (0x40000000) is set.
  3. If not, go back to step 1.
  4. Read the base register (0x2000B880).
  5. The lower 4 bits will contain the mailbox channel number of the mailbox that responded. Check that this matches the mailbox we are interested in. If not, then go back to step 1.
  6. The upper 28 bit should contain the same address that we passed in originally.

It is important to note, that the addresses passed to and from the mailbox are left shifted by 4 bits. This is because the first 4 bits contains the mailbox channel number. There are 10 mailbox channels:

  1. Power management
  2. Framebuffer
  3. Virtual UART
  4. VCHIQ
  5. LEDs
  6. Buttons
  7. Touch screen
  8. NA
  9. Mailbox-property-interface – ARM to GPU
  10. Mailbox-property-interface – GPU to ARM

Although channel 1 is marked as being the Framebuffer channel, we will be using channel 8.

Configuration
Configuring the framebuffer this way, is achieved by querying and setting various tags to the appropriate values. Below is a list of the tags that are relevant to the framebuffer.

Register Address
Allocate buffer 0x00040001
Release buffer 0x00048001
Blank screen 0x00040002
Get physical (display) width/height 0x00040003
Test physical (display) width/height 0x00044003
Set physical (display) width/height 0x00048003
Get virtual (buffer) width/height 0x00040004
Test virtual (buffer) width/height 0x00044004
Set virtual (buffer) width/height 0x00048004
Get depth 0x00040005
Test depth 0x00044005
Set depth 0x00048005
Get pixel order 0x00040006
Test pixel order 0x00044006
Set pixel order 0x00048006
Get alpha mode 0x00040007
Test alpha mode 0x00044007
Set alpha mode 0x00048007
Get pitch 0x00040008
Get virtual offset 0x00040009
Test virtual offset 0x00044009
Set virtual offset 0x00048009
Get overscan 0x0004000a
Test overscan 0x0004400a
Set overscan 0x0004800a
Get palette 0x0004000b
Test palette 0x0004400b
Set palette 0x0004800b

The way these tags are used, is to format a buffer in a way which will be explained soon. The address of this buffer is then sent to the graphics processor via the mailbox mechanism explained above. What is important to understand, is that we are not sending a message and getting response, but rather our buffer is being modified directly to include the response data filled in by the graphics processor. The mailbox analogy could be confusing, by making it seem that we are sending and receiving messages, where in fact we are providing a buffer and some of our original request values get modified with the appropriate response values, by the graphics processor.

Remember that the buffer we are going to use to configure the frame buffer, has to be aligned to a 16 byte boundary, since we will be passing it through the mailbox. It should be declared as an array of unsigned, 32 bit integers. The layout of tags in this buffer is as follows:

Offset Description
0 Total Buffer size (number of bytes)
1 Request/Response indicator
0x00000000 – Request
0x80000000 – Success Response
0x80000001 – Error Response
2 Tag ID
3 Tag value length (number of bytes)
4 Tag Request/Response indicator
0x00000000 – Request
0x80000000 – Success Response
0x80000001 – Error Response
5 Value data
Value data
n 0 – End tag

The steps for configuring basic frame buffer functionality, is as follows:

  1. Query the physical width and height of the frame buffer by using tag 0x00040003. The response should match the supported size of the attached monitor on the HDMI port.
  2. Use the information in step 1 to request a matching frame buffer by setting all the relevant tags IN ONE GO. It is important that concatenated tags are used in a single request, since sending the requests one by one does not provide the correct context for the graphics processor to know what is required, in which case it will just ignore all the parameters you provide. The tags we will set are as follows:
    1. 0x00048003 – Set physical width and height. This was retrieved in step 1.
    2. 0x00048004 – Set virtual width and height. For now, just set it to same values as the physical settings.
    3. 0x00048005 – Set depth. This is how many bits should be used per pixel, to denote colour. I have only tried 16 and 24 so far.
    4. 0x00040001 – Allocate the actual buffer in memory, based on the provide tag values. Provides required alignment in bytes, in our case 16.
  3. Parse the response by checking for errors.
  4. Get the pitch by using tag 0x00040008, which tells us how many bytes are in one row of our framebuffer.

Conclusion
If everything went according to plan, you should now have a frame buffer that you can write to. One thing to note, is the layout of the tag values that allocate the frame buffer, tag 0x00040001. What is interesting here, is that we are actually expecting more values in the response, than what we have populated in the request. As such, we must be sure to leave space for the response value to be filled in. This was not immediately apparent to me and took me longer than I would have liked, to figure out what was going here. We are only populating the required alignment, but in the response we will receive the alignment value as well as the actual frame buffer memory address pointer, that we are after.

References
The information contained in this article is not entirely all my own, but was collected though tireless research from a number os sources and by examining source code of similar projects.

  1. The Bare Metal section in the Raspberry Pi forum is a great source of information.
  2. Tags are documented in the Raspberry Pi firmware wiki
  3. BCM2835 (SoC) Datasheet
  4. ARM1176JZF-S Technical Reference

Introducing VarkOS

I have never really been satisfied to just know that something works, I want to know HOW it works. What makes it tick? How does it all fit together? Getting an in depth understanding of how the magic is performed, is more than just a passing fascination. I have been writing ‘embedded’ software professionally for many years and was always intrigued by how the operating system functions, deep inside the circuit board.

To this end, I finally decided to dedicate some time on the subject and started digging. I don’t have allot of experience with writing assembler, I find it way too tedious. But, writing an OS requires one to get very intimate with the platform, so I decided to brush up on some basic assembler. The Cambridge University’s Baking Pi series of tutorials is an excellent place to start. I worked through these and felt confident that I have a fairly good idea of what is going on and so it came about that VarkOS was born. ‘Vark’ is the Afrikaans word for pig. Why would I name my project after a pig? Well, I expect it to be dirty and in some places it might even smell funny. So there you go.

The Raspberry Pi turned out to be the ideal platform for this adventure, since I already have a couple lying around. There is also allot of information available on the Raspberry Pi platform, although for my purposes, allot of digging was required.

The trickiest part of starting a project like this, is getting the environment sorted out. I am using the Mac version of the Yagarto tool chain. Normally it would be customary to do a ‘Hello world’ type of project to confirm that everything is working well. Well, in a bare metal environment it is not that simple. To this end I cheated just a little bit. I ‘borrowed’ a small build project from somewhere that contained a working uart example. This allowed me to do the “Hello world” thing via the serial port, in order to confirm that my environment is up and running. From here I started by implementing some logging functions to allow me to send formatted messages out the serial port. Being able to format binary values into a human readable format is crucial for debugging purposes.

With the logging sorted, I though it might be a good time to use my new found knowledge from the Baking Pi tutorials, to implement a module that can manage the GPIO pins of the Raspberry Pi. With the customary blinking LED functionality sorted, it was time to move on to some more serious functionality.

First priority is being able to display stuff on the screen. It will also go a long way in making debugging easier if I have access to the screen to display data. As such, the frame buffer beast has to be tamed. This proved to be allot harder than expected, since documentation is sparse, to say the least. I am writing this in the hope that it might assist other people in digging through the dirt in order to get a better understanding of how these things work.

In my next article, I will write in more detail about my understanding of the way the frame buffer operates and how it was implemented. In the meantime, the code for VarkOS is available in my Github repository if you would like to give it a spin. At the moment is does very little, but it can display text and a logo, which is a good place to start.

UV Light Box

Probably the most indispensable tool for making printed circuit boards, is the UV light box. There are a couple of different methods for transferring of a PCB image. The most common homemade method, seems to be the toner transfer method, judging by the number of sites describing it. However, I use the photo lithography method and I have had great results with it so far, so no need to change.

I am not going describe the process in detail, since there is allot information on it elsewhere on the net. In a nutshell it consists of using a photo resist spray, this is a spray that is sensitive ultra violet light, to coat a copper clad fibreglass board. A transparency, containing the printed circuit image, is then placed on top and the board is then exposed to ultra violet light for a couple of minutes. The UV light degrades the coating in areas where it was exposed to the UV light, making it easily removable in a developer liquid like caustic soda. It leaves behind the coating in a pattern matching the circuit image, and exposes the bare copper in places where there are no tracks. An acid solution is then used to eat away the exposed copper, leaving the neat circuit pattern behind, protected from the acid by the photo resist coating.

The ultra violet light source is key to this process and since a suitable light source is not available as an off the shelf product, I decided to make a light box for this purpose. Now, at this point I should mention that this is my second attempt at building a UV light box. The first one was a monstrosity if a box over a meter long, half a meter wide and half a meter high! Inside was a dual fluorescent light fitting, fitted with two 8 Watt UV tubes. This light box worked well and I used it for a couple of years. But it took up way to much space and was cumbersome to setup and use. Through trial and error, I found that it required an exposure time of 8 minutes, for descent results. The biggest draw back of this box, was that it only allowed me to make single side circuit boards. This was fine then, but as my projects increased in complexity, so did the usefulness of this solution decrease.

I needed something that could illuminate a board from both the top and bottom simultaniously, in order to avoid complex re-allignment of the top and bottom layers in between exposures. Secondly, it need to fit on my desk, since working in the garage is a pain. So I settled on a box built from MDF from the local hardware store and UV leds. The end product lloks as follows:

It contains two sections of 54 LED’s each, one for the top and the other for the bottom. It runs off 12V DC and draws about 1.2A. I found that an exposure time of three minutes gives good results. Keep in mind that UV light is extremely bad for your eyes and you should always take the necessary precautions operating equipment like this. In order to avoid unnecessary exposure to harmful UV rays, I added a micro switch in the lid that will switch the light source off when the lid is lifted.

It is worthwhile mentioning that the dimensions of the box and the spacing of the UV LED’s are key to it’s success. LED’s have a conical light pattern that varies depending on the type and model of LED. Consult the data sheet of your model, before doing anything. The trick is to get as much overlap as possible, between all the neighbouring LED’s to avoid getting ‘shadows’ that will ruin the exposure. Some basic trigonometry will be helpful in determining the spacing between LED’s.

The LED’s I used (WW05A3SPE4-B) has a viewing angle of 30 degrees. My box’s inside dimensions worked out to 300mm (W) x 200mm (L) x 110mm (H).

The working area is a normal piece of windows glass, 4mm thick. It is important to get glass that is free of any UV coating that will shield the UV rays. Obviously it has to be spotlessly clean, since any imperfections will show up in the exposure. I use normal methalated spritis to give a good wipe down, every now and then.

The circuit for driving the LED’s is fairly straight forward. The LED’s are grouped in three’s and connected in a current regulator configuration. The schematic for one of the two sections can be found here.

Soldering so many components can really be a pain in the neck.

The circuit was constrcuted using vero board. The soldering took about two nights, lots of components. I used two seperate peices of vero board, that I joined using solder joints in a couple of strategic places.

Using solder joints to join two pieces of vero board together

Once the circuit construction was completed it was time to test everything before final assembly. At this point it is worthwhile remembering that UV rays are harmfull to your eyes, take precaution and use common sense.

To hold the vero board circuit in place, inside the box, I used 10mm stainless steel stand-off’s that was glued to the wood. Make sure that the surface is free from dust and grease and only ginger tighten the screws. I found it easier to first drill the holes and screw the stand-off’s to the vero board, before glueing and positioning it in the box. This way there is no hassling with alignment. Very important, do not use allot of force when screwing the stand-off’s to the vero board. The glue will not form a bond strong enough to withstand the torque forces if the screws have to be undone.

Be sure to put some weight ontop of the vero board while the glue sets. The vero board might have a slight bend to it, caused by the heat from soldering the components. Putting a weight on it will assist in getting the glue to dry with the stand-off’s firmly pressed against the wooden surface.

The glass panel was recessed into the bottom half of the box, by cutting it a few millimeter bigger than the actual opening. I used a Dremmel with routing attachment, to mill away a small section of the edge of the box. This ensures that the glass fits in snuggly and flush with the opening. This provides safer working platform with no risk of cutting ot injuring yourself with the sharp edge on the glass.

I hope that you found this article helpfull and even draw some inspiration to build your own UV Light Box. It is a fun project and a very worthwhile investment that makes DIY printed circuit manfacturing just a bit easier and convenient. I might add an automatic timer circuit in future, but it is quite straight forward to to just time it manually.

Some of my projects

I decided that my first real post will be a brief overview of some of the projects I keep myself busy. I will try to write a more detailed article on each in the future. This article is meant to at least put something up here as a start and also to get myself more familiar with the process of publishing this way.

I have never been much of a writer and English is not my first language, so please bear with me. Hopefully I wont bore you to death in the process.

Firstly, I enjoy tinkering with embedded software. I have always been fascinated by the interaction of software with the physical world. The fascination probably started in the good old days of dial-up modems and the characteristic chirping noise they made to establish a connection. This was hardware and software in action, working together and using sound to get a job done. My young mind was blown and I just had to satisfy my curiosity into this technological wonder world.

In order to get software to interact with the outside world, you need a circuit off course. I make my own printed circuit boards as you will notice. I am getting better at it. I even made my first double sided pc board recently.

This is a USB serial converter / infrared receiver and transmitter. It is the first board that I made using surface mount components. I am quite pleased with the result. It produces a board that requires fewer holes to be drilled, I think. I still need to drill holes for the vias, for which I use a 0.6mm drill bit. The software is still a work in progress though.

I recently pushed my manufacturing and soldering skills to a new limit, with this breakout board that contains an iPod connector.

The pitch of the iPod connector is 0.3mm! This breakout will come in handy in trying to interface iPod accessories. Notice the marks where I cut the traces connecting the ground pins.

Some of my more primitive projects involved white LED’s meant for illumination. We enjoy camping allot and these projects were meant to experiment with new ways LED’s can be applied in the outdoors.

This one is a LED light with a dimmer switch as well as an adjustable day/night switch to activate the light automatically after dark. It works surpisingly well, I just need to redo the board layout into a more suitable format for packaging.

It uses a dual 555 timer packaging, one timer implements pulse width modulation for dimming the LED’s and the other timer is configured as a one shot, to activate the pulse width modulation using a light dependant resistor as input. The LED’s are grouped in groups of 3, each group driven by a constant current regulator circuit to avoid thermal runaway.

The second LED project is simple LED strip with a 555 timer providing a push button on/off circuit.

Here is a lead acid battery charger I built, to charge a 6V LA battery I used for a line follower robot I built long ago. The charger worked well, but was a beast when it came to generating heat. It got hotter than hell and I had to position it in front of a normal household fan running at full speed to keep it from overheating.

That’s it for now. Im hoping in my next article, I will be able to show you some of the equipment I had to make, in order to refine my pcb manufacturing process. More on that later though…

It’s here!

I finally got round to creating a space where I can share my thoughts and hobbies with the world.

I am an electronics hobbyist and the title of this site refers to the idea that electronics are powered by smoke, because without the smoke, there is no magic.

If you came here hoping to find magic tabaco to smoke, sorry to dissapoint.