Step01 – Bare Metal Programming in C Pt1

Fork me on GitHub

Part 1 – Getting Started

Although the Raspberry-Pi comes with a good Linux distribution, the Pi is about software
development, and sometimes we want a real-time system without an operating system. I
decided it’d be great to do a tutorial outside of Linux to get to the resources of this great
piece of hardware in a similar vein to the
[Cambridge University Tutorials](http://www.cl.cam.ac.uk/freshers/raspberrypi/tutorials/)
which are excellently written. However, they don’t create an OS as purported and they start
from assembler rather than C. I will simply mimic their tutorial here, but using C instead
of assembler. The C compiler simply converts C syntax to assembler and then assembles this
into executable code for us anyway.

I highly recommend going through the Cambridge University Raspberry Pi tutorials as they are
excellent. If you want to learn a bit of assembler too, then definitely head off to there!
These pages provide a similar experience, but with the additional of writing code in C and
understanding the process behind that.

Cross -Compiling for the Raspberry Pi (BCM2835/6)

The GCC ARM Embedded project on Launchpad gives us
a GCC toolchain to use for ARM compilation on either Windows or Linux. I suggest you install
it (on Windows I would install it somewhere without spaces in the file path) so that you
have it in your path. On Linux install the relevant package which is usually something like
gcc-arm-none-eabi and also the debugger package gdb-arm-none-eabi. You should be able to
type arm-none-eabi-gcc on the command line and get a response like the following:

>arm-none-eabi-gcc
arm-none-eabi-gcc: fatal error: no input files
compilation terminated.

Compiler Version

I tried the current 4.9 release and it doesn’t work with some of the examples here, it
generates incorrect code, so stick to the 4.7 series until that’s sorted out.
This is the compiler I’m
using throughout these tutorials. Having the same helps because you’ll be able to step
through the disassembly listings as the tutorial progresses.

The eLinux page gives us the optimal GCC settings
for compiling code for the original Raspberry-Pi:

-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s

It is noted that -Ofast may cause problems with some compilations, so it is probably
better that we stick with the more traditional -O2 optimisation setting. The other flags merely
tell GCC what type of floating point unit we have, tell it to produce hard-floating point code
(GCC can create software floating point support instead), and tells GCC what ARM processor
architecture we have so that it can produce optimal and compatible assembly/machine code.

For the Raspberry-Pi 2 we know that the architecture is different. The ARM1176 from the original
pi has been replaced by a quad core Cortex A7 processor. Therefore, in order to compile
effectively for the Raspberry-Pi 2 we use a different set of compiler options:

-O2 -mfpu=neon-vfpv4 -mfloat-abi=hard -march=armv7-a -mtune=cortex-a7

You can see from the
ARM specification of the Cortex A7
that it contains a VFPV4 floating point processor and a NEON engine. The settings are gleaned
from the GCC ARM options page.

Getting to know the Compiler and Linker

In order to use a C compiler, we need to understand what the compiler does and what the linker
does in order to generate executable code. The compiler converts C statements into assembler
and performs optimisation of the assembly instructions. This is in-fact all the C compiler
does!

The C compiler then implicitly calls the assembler to assemble that file (usually a temporary)
into an object file. This will have relocatable machine code in it along with symbol
information for the linker to use. These days the C compiler pipes the assembly to the
assembler so there is no intermediate file as creating files is a lot slower than passing data
from one program to another through a pipe.

The linker’s job is to link everything into an executable file. The linker requires a linker
script. The linker script tells the linker how to organise the various object files. The linker
will resolve symbols to addresses when it has arranged all the objects according to the rules
in the linker script.

What we’re getting close to here is that a C program isn’t just the code we type. There are
some fundamental things that must happen for C code to run. For example, some variables need to
be initialised to certain values, and some variables need to be initialised to 0. This is all
taken care of by an object file which is usually implicitly linked in by the linker because
the linker script will include a reference to it. The object file is called crt0.o
(C Run-Time zero)

This code uses symbols that the linker can resolve to clear the start of the area where
initialised variables starts and ends in order to zero this memory section. It generally sets up
a stack pointer, and it always includes a call to _main. Here’s an important note: symbols present
in C code get prepended with an underscore in the generation of the assembler version of the code.
So where the start of a C program is the main symbol, in assembler we need to refer to it as it’s
assembler version which is _main.

Github

All of the source in the tutorials is available from the
Github repo.
So go clone or fork now so you have all the code to compile and modify as you work through the
tutorials.

Let’s have a look at compiling one of the simplest programs that we can. Lets compile and link
the following program:

part-1/armc-00

int main(void)
{
    while(1)
    {

    }

    return 0;
}

There are build “scripts” for each of the different types of Raspberry-pi under the part-1/armc-00
directory of the tutorials source code. There are three types, the original which is targeted with
built.bat/sh, the B+ which has the extended IO connector and fixing holes but it still a V1 RPi
which is targeting using build-rpi-bplus.bat/sh and finally the V2 board which features a quad
core processor and also has the extended IO connector and fixing holes which is targeted with
build-rpi-2.bat/sh.

The V1 boards are fitted with the Broadcom BCM2835 (ARM1176) and the V2 board uses the BCM2836
(ARM Cortex A7). Because of the processor difference, we use different build commands to build for
V1 or V2 boards, hence the different scripts for building:

arm-none-eabi-gcc -O2 -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s arm-test.c
arm-none-eabi-gcc -O2 -mfpu=vfp -mfloat-abi=hard -march=armv7-a -mtune=cortex-a7 arm-test.c

GCC does successfully compile the source code (there are no C errors in it), but the linker fails
with the following message:

.../arm-none-eabi/lib/fpu\libc.a(lib_a-exit.o): In function `exit':
exit.c:(.text.exit+0x2c): undefined reference to `_exit'
collect2.exe: error: ld returned 1 exit status

So with our one-line command above we’re invoking the C compiler, the assembler and the linker.
The C compiler does most of the menial tasks for us to make life easier for us, but because we’re
embedded engineers (aren’t we?) we MUST be aware of how the compiler, assembler and linker work
at a very low level as we generally work with custom systems which we must describe intimately
to the toolchain.

So there’s a missing _exit symbol. This symbol is reference by the C library we’re using. It is
in-fact a system call. It’s designed to be implemented by the OS. It would be called when a
program terminates. In our case, we are our own OS at we’re the only thing running, and in fact we
will never exit so we do not need to really worry about it. System calls can be blank, they just
merely need to be provided in order for the linker to resolve the symbol.

So the C library has a requirement of system calls. Sometimes these are already implemented as
blank functions, or implemented for fixed functionality. For a list of system calls see the
newlib documentation on system calls.
Newlib is an open source, and lightweight C library.

The C library is what provides all of the C functionality found in standard C header files such
as stdio.h, stlib.h, string.h, etc.

At this point I want to note that the standard Hello World example won’t work here without an OS,
and it is exactly unimplemented system calls that prevent it from being our first example. The
lowest part of printf(…) includes a write function “write” – this function is used by all of
the functions in the C library that need to write to a file. In the case of printf, it needs to
write to the file stdout. Generally when an OS is running stdout produces output visible on a
screen which can then be piped to another file system file by the OS. Without an OS, stdout
generally prints to a UART to so that you can see program output on a remote screen such as a PC
running a terminal program. We will discuss write implementations later on in the tutorial
series, let’s move on…

The easiest way to fix the link problem is to provide a minimal exit function to satisfy the
linker. As it is never going to be used, all we need to do is shut the linker up and let it
resolve _exit. So now we can compile the next version of the code:

part-1/armc-01

int main(void)
{
    while(1)
    {

    }

    return 0;
}

void exit(int code)
{
    while(1)
        ;
}

It’s important to have an infinite loop in the exit function. In the C library, which is not
intended to be used with an operating system (hence arm-NONE-eabi-*), _exit is marked as being
noreturn. We must make sure it doesn’t return otherwise we will get a warning about it. The
prototype for _exit always includes an exit code int too.

Now using the same build command above we get a clean build! Yay! But there is really a problem,
in order to provide a system underneath the C library we will have to provide linker scripts and
our own C Startup code. In order to skip that initially and to simply get up and running we’ll
just use GCC’s option not to include any of the C startup routines, which excludes the need for
exit too, -nostartfiles

Getting to Know the Processor

As in the Cambridge tutorials we will copy their initial example of illuminating an LED in order
to know that our code is running correctly.

Raspberry-Pi Boot Process

First, let’s have a look at how a Raspberry-Pi processor boots. The BCM2385 from Broadcom includes
two processors that we should know about, one is a Videocore(tm) GPU which is why the Raspberry-Pi
makes such a good media-centre and the other is the ARM core which runs the operating system. Both
of these processors share the peripheral bus and also have to share some interrupt resources.
Although in this case, share means that some interrupt sources are not available to the ARM
processor because they are already taken by the GPU.

The GPU starts running at reset or power on and includes code to read the first FAT partition of
the SD Card on the MMC bus. It searches for and loads a file called bootcode.bin into memory and
starts execution of that code. The bootcode.bin bootloader in turn searches the SD card for a file
called start.elf and a config.txt file to set various kernel settings before searching the SD card
again for a kernel.img file which it then loads into memory at a specific address (0x8000) and
starts the ARM processor executing at that memory location. The GPU is now up and running and the
ARM will start to come up using the code contained in kernel.img. The start.elf file contains the
code that runs on the GPU to provide most of the requirements of OpenGL, etc.

Therefore in order to boot your own code, you need to firstly compile your code to an executable
and name it kernel.img, and put it onto a FAT formatted SD Card, which has the GPU bootloader
(bootcode.bin, and start.elf) on it as well. The latest Raspberry-Pi firmware is available on
GitHub. The bootloader is located under the boot
sub-directory
. The rest of the firmware
provided is closed-binary video drivers. They are compiled for use under Linux so that accelerated
graphics drivers are available. As we’re not using Linux these files are of no use to us, only the
bootloader firmware is.

All this means that the processor is already up and running when it starts to run our code. Clock
sources and PLL settings are already decided and programmed in the bootloader which alleviates
that problem from us. We get to just start messing with the devices registers from an already
running core. This is something I’m not that used too, normally the first thing in my code would
be setting up correct clock and PLL settings to initialise the processor, but the GPU has setup
the basic clocking scheme for us.

The first thing we will need to set up is the GPIO controller. There are no drivers we can rely on
as there is no OS running, all the bootloader has done is boot the processor into a working state,
ready to start loading the OS.

You’ll need to get the
Raspberry-Pi BCM2835 peripherals datahsheet
which gives us the information we require to control the IO peripherals of the BCM2835. I’ll guide
us through using the GPIO peripheral – there are as always some gotcha’s:

We’ll be using the GPIO peripheral, and it would therefore be natural to jump straight to that
documentation and start writing code, but we need to first read some of the ‘basic’ information
about the processor. The important bit to note is the virtual address information. On page 5 of
the BCM2835 peripherals page we see an IO map for the processor. Again, as embedded engineers we
must have the IO map to know how to address peripherals on the processor and in some cases how to
arrange our linker scripts when there are multiple address spaces.

ARM Virtual addresses

The VC CPU Bus addresses relate to the Broadcom Video Core CPU. Although the Video Core CPU is what
bootloads from the SD Card, execution is handed over to the ARM core by the time our kernel.img code
is called. So we’re not interested in the VC CPU Bus addresses.

The ARM Physical addresses is the processors raw IO map when the ARM Memory Management Unit (MMU)
is not being used. If the MMU is being used, the virtual address space is what what we’d be
interested in.

Before an OS kernel is running, the MMU is also not running as it has not been initialised and the
core is running in kernel mode. Addresses on the bus are therefore accessed via their ARM Physical
Address. We can see from the IO map that the VC CPU Address 0x7E000000 is mapped to ARM Physical
Address 0x20000000 for the original Raspberry Pi. This is important!

Although not documented anywhere, the Raspberry-Pi 2 has the ARM IO base set to 0x3F000000 instead
of the original 0x20000000 of the original Raspberry-Pi. Unfortunately for us software engineers
the Raspberry-Pi foundation don’t appear to be good a securing the documentation we need, in fact,
their attitude suggests they
think we’re magicians and don’t actually need any. What a shame! Please if you’re a member of the
forum, campaign for more documentation. As engineers, especially in industry we wouldn’t accept
this from a manufacturer, we’d go elsewhere! In fact, we did at my work and use the TI Cortex
A8 from the Beaglebone Black, a very good and well documented SoC!

Anyway, the base address can be gleaned from searching for uboot patches. The Raspberry Pi 2 uses
a BCM2836 so we can search for that and u-boot and we come along a
patch for supporting the Raspberry-Pi 2.

Further on in the manual we come across the GPIO peripheral section of the manual (Chapter 6,
page 89).

Visual Output and Running Code

Finally, let’s get on and see some of our code running on the Raspberry-Pi. We’ll continue with
using the first example of the Cambridge tutorials by lighting the OK LED on the Raspberry-Pi
board. This’ll be our equivalent of the ubiquitous Hello World example. Normally an embedded
Hello World is a blinking LED, so that we know the processor is continuously running. We’ll move
on to that in a bit.

The GPIO peripheral has a base address in the BCM2835 manual at 0x7E200000. We know from getting
to know our processor that this translates to an ARM Physical Address of 0x20200000 (0x3F200000
for RPI2). This is the first register in the GPIO peripheral register set, the ‘GPIO Function
Select 0’ register.

In order to use an IO pin, we need to configure the GPIO peripheral. From the
Raspberry-Pi schematic diagrams
the OK LED is wired to the GPIO16 line (Sheet 2, B5) . The LED is wired active LOW – this is
fairly standard practice. It means to turn the LED on we need to output a 0 (the pin is connected
to 0V by the processor) and to turn it off we output a 1 (the pin is connected to VDD by the
processor).

Unfortunately, again, lack of documentation is rife and we don’t have schematics for the
Raspberry-Pi 2 or plus models! This is important because the GPIO lines were re-jigged and as
Florin has noted in the comments section, the Raspberry Pi Plus configuration has the LED on
GPIO47, so I’ve added the changes in brackets below for the RPI B+ models (Which includes the
RPI 2).

Back to the processor manual and we see that the first thing we need to do is set the GPIO
pin to an output. This is done by setting the function of GPIO16 (GPIO47 RPI+) to an output.

Bits 18 to 20 in the ‘GPIO Function Select 1’ register control the GPIO16 pin.

Bits 21 to 23 in the ‘GPIO Function Select 4’ register control the GPIO47 pin. (RPI B+)

In C, we will generate a pointer to the register and use the pointer to write a value into the
register. We will mark the register as volatile so that the compiler explicitly does what I tell
it to. If we do not mark the register as volatile, the compiler is free to see that we do not
access this register again and so to all intents and purposes the data we write will not be used
by the program and the optimiser is free to throw away the write because it has no effect.

The effect however is definitely required, but is only externally visible (the mode of the GPIO
pin changes). We inform the compiler through the volatile keyword to not take anything for
granted on this variable and to simply do as I say with it:

#ifdef RPI2
    #define GPIO_BASE 0x3F200000UL
#else
    #define GPIO_BASE 0x20200000UL
#endif

volatile unsigned int* gpio_fs1 = (unsigned int*)(GPIO_BASE+0x04);
volatile unsigned int* gpio_fs4 = (unsigned int*)(GPIO_BASE+0x10);

In order to set GPIO16 as an output then we need to write a value of 1 in the relevant bits of
the function select register. Here we can rely on the fact that this register is set to 0 after
a reset and so all we need to do is set:

#if defined( RPIPLUS ) || defined ( RPI2 )
    *gpio_fs4 |= (1<<21);
#else
    *gpio_fs1 |= (1<<18);
#endif

This code looks a bit messy, but we will tidy up and optimise later on. For now we just want to
get to the point where we can light an LED and understand why it is lit!

The ARM GPIO peripherals have an interesting way of doing IO. It’s actually a bit different to
most other processor IO implementations. There is a SET register and a CLEAR register. Writing
1 to any bits in the SET register will SET the corresponding GPIO pins to 1 (logic high), and
writing 1 to any bits in the CLEAR register will CLEAR the corresponding GPIO pins to 0
(logic low). There are reasons for this implementation over a register where each bit is a pin
and the bit value directly relates to the pins output level, but it’s beyond the scope of this
tutorial.

So in order to light the LED we need to output a 0. We need to write a 1 to bit 16 in the CLEAR
register:

*gpio_clear |= (1<<16);

Putting what we’ve learnt into the minimal example above gives us a program that compiles and
links into an executable which should provide us with a Raspberry-Pi that lights the OK LED
when it is powered. Here’s the complete code we’ll compile:

part-1/armc-02

/* The base address of the GPIO peripheral (ARM Physical Address) */
#ifdef RPI2
    #define GPIO_BASE       0x3F200000UL
#else
    #define GPIO_BASE       0x20200000UL
#endif

#if defined( RPIBPLUS ) || defined( RPI2 )
    #define LED_GPFSEL      GPIO_GPFSEL4
    #define LED_GPFBIT      21
    #define LED_GPSET       GPIO_GPSET1
    #define LED_GPCLR       GPIO_GPCLR1
    #define LED_GPIO_BIT    15
#else
    #define LED_GPFSEL      GPIO_GPFSEL1
    #define LED_GPFBIT      18
    #define LED_GPSET       GPIO_GPSET0
    #define LED_GPCLR       GPIO_GPCLR0
    #define LED_GPIO_BIT    16
#endif

#define GPIO_GPFSEL0    0
#define GPIO_GPFSEL1    1
#define GPIO_GPFSEL2    2
#define GPIO_GPFSEL3    3
#define GPIO_GPFSEL4    4
#define GPIO_GPFSEL5    5

#define GPIO_GPSET0     7
#define GPIO_GPSET1     8

#define GPIO_GPCLR0     10
#define GPIO_GPCLR1     11

#define GPIO_GPLEV0     13
#define GPIO_GPLEV1     14

#define GPIO_GPEDS0     16
#define GPIO_GPEDS1     17

#define GPIO_GPREN0     19
#define GPIO_GPREN1     20

#define GPIO_GPFEN0     22
#define GPIO_GPFEN1     23

#define GPIO_GPHEN0     25
#define GPIO_GPHEN1     26

#define GPIO_GPLEN0     28
#define GPIO_GPLEN1     29

#define GPIO_GPAREN0    31
#define GPIO_GPAREN1    32

#define GPIO_GPAFEN0    34
#define GPIO_GPAFEN1    35

#define GPIO_GPPUD      37
#define GPIO_GPPUDCLK0  38
#define GPIO_GPPUDCLK1  39

/** GPIO Register set */
volatile unsigned int* gpio;

/** Simple loop variable */
volatile unsigned int tim;

/** Main function - we'll never return from here */
int main(void)
{
    /* Assign the address of the GPIO peripheral (Using ARM Physical Address) */
    gpio = (unsigned int*)GPIO_BASE;

    /* Write 1 to the GPIO16 init nibble in the Function Select 1 GPIO
       peripheral register to enable GPIO16 as an output */
    gpio[LED_GPFSEL] |= (1 << LED_GPFBIT);

    /* Never exit as there is no OS to exit to! */
    while(1)
    {
        for(tim = 0; tim < 500000; tim++)
            ;

        /* Set the LED GPIO pin low ( Turn OK LED on for original Pi, and off
           for plus models )*/
        gpio[LED_GPCLR] = (1 << LED_GPIO_BIT);

        for(tim = 0; tim < 500000; tim++)
            ;

        /* Set the LED GPIO pin high ( Turn OK LED off for original Pi, and on
           for plus models )*/
        gpio[LED_GPSET] = (1 << LED_GPIO_BIT);
    }
}

We now compile with the slightly modified compilation line:

arm-none-eabi-gcc -O2 -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s -nostartfiles armc-2.c -o kernel.elf

The linker gives us a warning, which we’ll sort out later, but importantly the linker has resolved
the problem for us. This is the warning we’ll see and ignore:

.../arm-none-eabi/bin/ld.exe: warning: cannot find entry symbol _start; defaulting to 00008000

As we can see from the compilation, the standard output is ELF format which is essentially an
executable wrapped with information that an OS may need to know. We need a binary ARM executable
that only includes machine code. We can extract this using the objcopy utility:

arm-none-eabi-objcopy kernel.elf -O binary kernel.img

A quick note about the ELF format

ELF is a file format used by some
OS, including Linux which wraps the machine code with meta-data. The meta-data can be useful.
In Linux and in fact most OS these days, running an executable doesn’t mean the file gets loaded
into memory and then the processor starts running from the address at which the file was loaded.
There is usually an executable loader which uses formats like ELF to know more about the
executable, for example the Function call interface might be different between different
executables, this means that code can use different calling conventions which use different
registers for different meanings when calling functions within a program. This can determine
whether the executable loader will even allow the program to be loaded into memory or not. The
ELF format meta-data can also include a list of all of the shared objects (SO, or DLL under
Windows) that this executable also needs to have loaded. If any of the required libraries are
not available, again the executable loader will not allow the file to be loaded and run.

This is all intended (and does) to increase system stability and compatibility.

We however, do not have an OS and the bootloader does not have any loader other than a disk read,
directly copying the kernel.img file into memory at 0x8000 which is then where the ARM processor
starts execution of machine code. Therefore we need to strip off the ELF meta-data and simply
leave just the compiled machine code in the kernel.img file ready for execution.

Back to our example

This gives us the kernel.img binary file which should only contain ARM machine code. It should be
tens of bytes long. You’ll notice that kernel.elf on the otherhand is ~34Kb. Rename the kernel.img
on your SD Card to something like old.kernel.img and save your new kernel.img to the SD Card.
Booting from this SD Card should now leave the OK LED on permanently. The normal startup is for
the OK LED to be on, then extinguish. If it remains extinguished something went wrong with
building or linking your program. Otherwise if the LED remains lit, your program has executed
successfully.

A blinking LED is probably more appropriate to make sure that our code is definitely running.
Let’s quickly change the code to crudely blink an LED and then we’ll look at sorting out the C
library issues we had earlier as the C library is far too useful to not have access to it.

Compile the code in part-1/armc-03. The code listing is identical to part-1/armc-02 but the build
scripts use objcopy to convert the ELF formatted binary to a raw binary ready to deploy on the SD
Card.

…and see the OK LED Blink! πŸ˜€

As this is the first example where code should run and give you a visible output on your RPi,
I’ve included the kernel binaries for each Raspberry-Pi board so that you can load the pre-built
binary and see the LED flash before compiling your own kernel to make sure the build process is
working for you. After this tutorial, you’ll have to build your own binaries!

Although the code may appear to be written a little odd, please stick with it! There are reasons
why it’s written how it is. Now you can experiment a bit from a basic starting point, but beware
– automatic variables won’t work, and nor will initialised variables because we have no C Run
Time support yet.

That will be where we start with
Step 2 of Bare metal programming the Raspberry-Pi!

EDIT: 23/01/14 – Added some more information about the boot process because of a few questions
I had emailed to me. Hopefully now the bootloading process is a bit clearer. Also added “A
quick note about the ELF format” for clarification of why we use objcopy -O binary

Edit Added part-1 to the Git repo so the code is better organised and updated the tutorial so
it works with all Raspberry-Pi boards such as the B+ and V2

79 thoughts on “Step01 – Bare Metal Programming in C Pt1

    1. Brian_S Post author

      Sorry, I keep meaning to get around to using the C-RunTime which would be Pt2. Debugging would probably be Pt3 after the C-RunTime library. It would probably have to include using a JTAG connector too. As we’ve just finished moving house I should be able to get round to developing this some more. It’d be nice to create an OS for the Raspberry-Pi for small, specific tasks.

  1. Henry

    This is a great tutorial! Thanks a lot since It’s really helpful to me!
    I tried to add a timer like the Cambridge tutorials but in vain.
    Why function calls and automatic variables can’t be used?

    Looking forward to your Pt2!

  2. Saba

    Somehow it’s not working for me. uint32_t gives error of undefined type, so I changed that to “unsigned int” instead. The linker gives the warning “/usr/bin/../lib/gcc/arm-none-eabi/4.7.4/../../../../arm-none-eabi/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000008000
    ” but as you say I am still able to create the image. But still the LED does not blink. I can’t figure out why. Would copying the image to a blank SD card work?

    1. Brian_S Post author

      Hi Saba, Sorry – I fixed the uint32_t problem. The warning you show is the one I said can be ignored, I’ve included it in the page now so it’s more obvious that this warning is safe to ignore for now.

      I’m not sure why you cannot get the LED to blink. Please re-try using the example code and post your console output if it doesn’t work for you again.

    1. Brian_S Post author

      Thanks for the info Don! Apologies, we shouldn’t be using anything from the C library at the moment, so I’ve just changed the examples to use unsigned int instead.

  3. GJS

    Hi all.

    I’ve modified this example to alternate flash LEDs connected to pins 23 and 24 of a picobbler. I’ve also implemented a “clock.h & clock.c” to get the millisecond delay a la cambridge tuts.

    Not sure how to share them on here…guess I could paste the code if someones interested?

    TTFN,

    GJS

  4. Tom

    Hey, can you please give me your hex code of the working kernel.img.
    I’ve tried everything and i can’t get the LED to blink. I think the problem might reside in my compiler.

    Also, this for the 512 mb version of raspberry Pi?

    1. Brian_S Post author

      Hi Tom, Sorry to hear you’re having trouble. Did you use the code on Github? It’s the easiest way to get up and running, the compiler shouldn’t be too much problem if it’s arm-none-gnueabi-

      Email me your binary and I’ll have a look at it: brian@valvers.com

      I use a 256Mb Pi Model B, but it shouldn’t make any difference for these binaries.

  5. poofjunior

    Hi Brian_S,
    Thanks again for these posts. Just a quick note: it looks like the compiler flags:

    -mfpu=vfp -mfloat-abi=hard

    aren’t quite enough to invoke the floating point unit. In addition, certain registers need to be configured to enable the floating point unit. Otherwise, code that uses floats will hang inexplicably. The Assembly code needed (special thatnks to DWelch67) is:

    mrc p15, 0, r0, c1, c0, 2
    orr r0, r0, #0x300000 @ single precision
    orr r0, r0, #0xC00000 @ double precision
    mcr p15, 0, r0, c1, c0, 2
    mov r0, #0x40000000
    fmxr fpexc,r0

    I think the floating point unit is a coprocessor in the BCM2835, which is why the assembly code contains coprocessor-specific commands, but this is at the edge of what I know right now.

    There’s a bit more info here:
    http://www.raspberrypi.org/forum/viewtopic.php?f=72&t=55500

    1. Brian_S Post author

      Hi,

      That’s correct. There is actually plenty that still needs to go in the startup assembler, but I wanted to keep it really simple for the first few tutorials. Sorry it’s taking me so long to get the next one online. It’s nice to have the information here for anyone who tries to do floating point based on these first tutorials. Thanks.

  6. Vu Nguyen

    I just found that if we shift left 1 to 18 positions (1 << 18), then we will end up in setting bit 20. When you set GPIO16 to output, you set either of the bits 18, 19, or 20 to 1. Initially, you wanted to set bit 18 to to 1. But what really happens is that bit 20 is set to 1, not bit 18. Luckily, bit 20 is also the set of bits that controls GPIO16. And everything seems to work fine after that.

    1. Brian_S Post author

      Hi There,

      That’s incorrect, (1<<18) sets bit 18 to 1 given the bit range 0-31. It's best to try this out by writing a quick console program or something and printing the result out in hex or something to prove it to yourself. Use the %X format in printf to help you.

  7. jCook

    Thank you for writing the tutorial. I had a question regarding the peripherals data sheet. In your example you use the BCM 2835 data sheet. The raspberry pi that I have (mod B rev 2) seems to have a micron SOC 3VA78 D9QHN.

    The main question I have is do you know if the data sheet for broadcom is compatible with micron. I have tried to find a micron peripherals datasheet to no avail.

    I apologize for my ignorance I am not even sure if that is the correct question to ask, or the right way to ask it. I am new to all of this.

    1. Brian_S Post author

      Hi, The processor is still a BCM2835, but the device fitted to the board can vary – for example the device fitted to a raspberry-pi in front of me now is marked samsung. Generally this is the package manufacturer. This package includes the RAM and the processor. So just use the BCM2835 datasheet! πŸ˜€

      Best Regards, Brian.

  8. gshrikant

    Hi,

    Great work on the tutorial! Just wanted to point out that the processor onboard the RPi isn’t an ARM Cortex-A8 as you mention in the section on Boot Process. In fact, the RPi has the ARM11 core which is the ARMv6 ISA.

    Regards

    1. Brian_S Post author

      Hi,

      You’re absolutely right, I’m just going to correct it now. Sorry I’ve been doing parallel development with the Beaglebone Black too which uses a Cortex-A8. Sometimes things get confused between the two!

      Best Regards, Brian.

  9. Kris

    I couldn’t help to notice your avatar is a Manta. I’m looking into creating a virtual dash for mine (with a Rpi using a ardiuno and a 12.1 inch screen). I need boot times under 2 seconds from ignition, so a custom embedded setup would be the only way to go I think. So I need to get this to work so I can display graphical stuff, monitoring the ardiuno. Plus in the end I would also need turn-by-turn navigation… anyway love your tutorial so far. Greetings, Kris.

  10. theone

    Strange, title says bare metal programming but 1 paragraph in author does a complete 180 and contradicts the title. Why am I not surprised.

    1. Brian_S Post author

      Hi Theone,

      Thanks for your comment, please take the time to explain what you are referring to and what your definition of bare metal programming is when you get time.

      Best Regards, Brian.

  11. Florin

    Hi Brian,
    I followed your tutorial to have a first run with my RPi B+. It’s working fine the GPIO16 but the ACT LED (Green) is mapped on GPIO47; to have this LED working the gpio[GPIO_GPFSEL4] |= (1 << 21) and the gpio[GPIO_GPSET1/GPIO_GPCLR1] = (1 << 15) is needed. Also on the SD CARD was needed bootcode.bin and start.elf, both of them downloaded from the link indicated by you.

    Regards,
    Florin

  12. erwincoumans

    Thanks for the great tutorial, I love it. Ideally I would like to have a faster work-flow without operating system, similar to ARDUINO, where you upload a C or C++ ‘sketch’ with setup and loop, through USB (instead of copying to flash). One question: do you have some links/details about those reasons mentioned here: “There are reasons for this implementation over a register where each bit is a pin and the bit value directly relates to the pins output level, but it’s beyond the scope of this tutorial.”

  13. Paul

    Nice article, works well for a Raspberry Pi Model B+ as long as you also include start.elf on the SD card. Haven’t managed to get it going on a Raspberry Pi 2 though : the files unpacked from NOOBS 1.3.12 do not include kernel.img or start.elf. Any clues from the cognescenti ?

    1. Brian_S Post author

      Hi Kienan, I’ve just tested again and this works fine on a RPI2. Can you confirm you’re using the build.bat in the part-1/armc-003 folder?

      I’ve just included the latest start.elf and bootcode.bin RPi firmware in the repo. For the RPI2 you need a relatively late start.elf. I’d suggest using both from the repo and trying with the pre-built kernel in part-1/armc-003/bin-rpi-2 and then when that works, move to your own build.

      NOTE: The LED flashes quite a bit slower on an RPI2!

    2. Brian_S Post author

      Hi Kienan,

      Can you elaborate a bit as to how you’re compiling? Have you tried the pre-built binary? It all *just works* on my RPI2 here.

      Best Regards, Brian.

  14. shawn

    hey Brian, thank you very much for your efforts behind this tutorial and your consistent feedback. I am new to rpi programming and have basic level of C know how. I was trying to understand but was little stuck in understanding the code with big list of numbers you defined. I assume they are port numbers..right? or are they something else…So that way in the circuit diagram does #define GPIO_GPSET0 7 & #define GPIO_GPSET1 8 refers to port numbers 7 and 8 of processor where we can apply input to clear and set value?

    and lastly, how did you come to know that bits 18 to 20 in the ‘GPIO Function Select 1’ register control the GPIO16 pin because there are 4 alternate function registers and everytime bit 18 to 20 belongs to different FSELs. I am really confused in that and i am really sorry for disturbing you by asking this noob++ questions. It will be really helpful for me to understand them. Thank you again.

    1. Brian_S Post author

      Hi Shawn,

      The “list of large numbers” are register addresses and define the register address locations for the GPIO peripheral. These numbers are in the Broadcom BMC2835 ARM Peripherals manual. Similarly, the GPIO Function Select registers are defined in the same document.

      Best Regards, Brian.

  15. Louis

    Hi Brian, thank you for your great work on this tutorial !
    I’m quite new to Raspberry Pi programming and I apologize in advance if I misunderstood something in your tutorial.

    I tried to run your code on my Raspberry PI 2 and the LED remains off. I tried your pre-build binarie which is not working for me. I also tried to build the code for Rasperry PI (the old one) and it’s working great.

    I also noticed something strange :

    The following is working on the RPI2 is waiting a few seconds before turning on the LED (which is normal) :
    while(1)
    {
    for(tim = 0; tim < 5000000; tim++);
    gpio[LED_GPSET] = (1 << LED_GPIO_BIT);
    for(tim = 0; tim < 5000000; tim++);
    //gpio[LED_GPCLR] = (1 << LED_GPIO_BIT);
    }

    Whereas , the following code is not working at all on the RPI2 (I have uncommented the instruction to turn off the LED) :
    while(1)
    {
    for(tim = 0; tim < 5000000; tim++);
    gpio[LED_GPSET] = (1 << LED_GPIO_BIT);
    for(tim = 0; tim < 5000000; tim++);
    gpio[LED_GPCLR] = (1 << LED_GPIO_BIT);
    }

    NB : The rest of the code is just like yours

    NB2 : I've tested it with your start.elf and bootcode.bin AND the latest start.elf and bootcode.bin from RPI firmware.

    1. Brian_S Post author

      Hi Louis,

      Thank-you for reporting your problem. It’s too complicated to take up in the comments section here, so I’ve moved your comment over to an issue on Github

      Thank-you for the comment! Hopefully we can get this one nailed quickly.

      Best Regards,

      Brian.

  16. robert

    FIrst of all congrats for this job! thank you!

    Maybe you can help me:

    I am having problems with the LED.

    FIrst of all I started with the raspbian kernel.img inside and it works perfeclty.

    Then, I rename this kernel.img to kernel-old.img.

    Then I add the part-1/armc-03/bin-rpi-bplus/kernel.img (because I have a raspberry pi b+).

    Now, the only LED on is the red one, the ACT LED is always off.

    I don know why it is not working πŸ™

    Thank you.

    1. robert

      I am trying to do what florins said a few comments before but I can not achieve it.

      Could you please post the exactly code?

      Thank you

    2. Brian_S Post author

      Hi Robert, sorry you’re having grief. I’ve got all three types of RPi here and, of course, they work for me!!

      To help investigate the issues it would be great if you could log a new issue on Github

      Thanks for the info!

  17. Peter Carnegie

    Hi Brian

    Absolutely BRILLIANT !

    Thank you so much for bothering/taking-the-time to help the rest of us get to grips with Bare Metal RasPI.

    BTW, I’ve got your tutorial working in Netbeans.

    Once again, very many thanks

    Peter

  18. Adam

    Hi Brian,

    Thanks for these. I had tried the BakingPi tutorials but only got as far as the LED blinking. I’ve tried part 1 and haven’t been able to get it working. I also tried the binary from GitHub and that didn’t work either. In both cases the LED didn’t light up at all. Any suggestions or advice?

    Thanks a lot.
    Adam.

  19. KH Park

    Hi,
    Many thanks for this tutorial!

    I tried the ACT LED blinking but it doesn’t work when I put the binary output file (the result of the objcopy kernel.elf -O binary kernel.img command) directly on the FAT boot partition of the SD Card, as you suggest. However, it works when I make a uImage from it, and then load it via u-boot (fetches the uImage from a TFTP server)

    Why doesn’t it work when kernel.img is put directly on the SD Card ?
    Is there something I am missing?

    KH Park

  20. Nilesh M

    Hi ,
    Thank for this tutorial ! its great work

    I am working with PI2 But its not working can somebody put any video or all file which need to place in SD card to required Get it on PI2

  21. David

    Hi,
    I got this example to work on a PI2 without any difficultly, However am a bit confused as the LED flashes at ~1 second interval. Given the delay loop is 500000 iterations I would have expected much faster flashing which suggests the CPU is not running at 900Mhz. I realize the for statement will expand to many instructions and there may be several clock cycles per instruction but it still seem very slow.
    Is the CPU running slow and if so how do I speed it up, or am I mistaken. I have tried creating and editing a config.txt file to no avail.
    Thanks David

  22. Srinidhi

    Hi Brian,
    Thanks for this series! I have written small helper gpio functions (set/clear/fsel), these helper functions will be called by “main” function.
    Everything works fine unless compiler adds all the instructions (of helper functions) in main routine.
    But, if I code in such a way that, main should make a function call to helper functions (which are not inline) then it stops working.
    I fail to understand this behaviour. Can you help understand.
    PS: I am using same compiler version as you have mentioned in the above post.

  23. Dennis ng

    I can install the latest 4.9 but not 4.7. In fact, the 4.9 via the version release number cannot be installed via the command given. I just have to install after setup of ppa then install the gcc without the version no. The 4.7 has no cue how to install it. It seem you can just download it but given there is no configure etc just copy them seems no use.

    I work fine under mac using the suggested cambridge source. The assembler work ok and hence want to try this. Also the qemu later cannot find a way to install under mac.

    All these is under 64 but desktop Ubuntu just download from their web site to my VMware 6.0.6 under mac El Capitan.

  24. fei chen

    Hi Brian,
    Thanks for this series!
    sorry, my english is very pool, I read your article, and I write some code, but it doesn’t work. I use rpi2
    could you help me?

    this is the code:

    /* gpio.h */
    #ifndef GPIO_H
    #define GPIO_H

    #include

    #define GPIO_BASE 0x3f200000u

    /* gpio register index */
    enum gpio_reg_index {
    GPIO_GPFSEL0 = 0,
    GPIO_GPFSEL1 = 1,
    GPIO_GPFSEL2 = 2,
    GPIO_GPFSEL3 = 3,
    GPIO_GPFSEL4 = 4,
    GPIO_GPFSEL5 = 5,
    GPIO_GPSET0 = 7,
    GPIO_GPSET1 = 8,
    GPIO_GPCLR0 = 10,
    GPIO_GPCLR1 = 11,
    GPIO_GPLEV0 = 13,
    GPIO_GPLEV1 = 14,
    GPIO_GPEDS0 = 16,
    GPIO_GPEDS1 = 17,
    GPIO_GPREN0 = 19,
    GPIO_GPREN1 = 20,
    GPIO_GPFEN0 = 22,
    GPIO_GPFEN1 = 23,
    GPIO_GPHEN0 = 25,
    GPIO_GPHEN1 = 26,
    GPIO_GPLEN0 = 28,
    GPIO_GPLEN1 = 29,
    GPIO_GPAREN0 = 31,
    GPIO_GPAREN1 = 32,
    GPIO_GPAFEN0 = 34,
    GPIO_GPAFEN1 = 35,
    GPIO_GPPUD = 37,
    GPIO_GPPUDCLK0 = 38,
    GPIO_GPPUDCLK1 = 39,
    };

    enum gpio_fsel {
    GPIO_FSEL_INPUT = 0x0,
    GPIO_FSEL_OUTPUT = 0x1,
    GPIO_FSEL_ALT0 = 0x4,
    GPIO_FSEL_ALT1 = 0x5,
    GPIO_FSEL_ALT2 = 0x6,
    GPIO_FSEL_ALT3 = 0x7,
    GPIO_FSEL_ALT4 = 0x3,
    GPIO_FSEL_ALT5 = 0x2,
    };

    extern void gpio_pin_fsel(u32 pin, enum gpio_fsel value);

    extern void gpio_pin_high(u32 pin);
    extern void gpio_pin_low(u32 pin);

    enum gpio_level {
    GPIO_LEVEL_LOW = 0,
    GPIO_LEVEL_HIGH = 1,
    };

    extern enum gpio_level gpio_pin_level(u32 pin);

    #endif

    / * gpio.c */
    #include

    void gpio_pin_fsel(u32 pin, enum gpio_fsel value)
    {
    volatile u32 *base = (u32 *)GPIO_BASE;
    base[GPIO_GPFSEL0+pin/10] |= (value << pin%10*3);
    }

    void gpio_pin_high(u32 pin)
    {
    volatile u32 *base = (u32 *)GPIO_BASE;
    base[GPIO_GPSET0+pin/32] |= (1 << pin%32);
    }

    void gpio_pin_low(u32 pin)
    {
    volatile u32 *base = (u32 *)GPIO_BASE;
    base[GPIO_GPCLR0+pin/32] |= (1 <> pin%32) & 1;
    }

    /* main.c */
    int main()
    {

    gpio_pin_fsel(47);
    while (1) {
    delay();
    gpio_pin_high(47);
    delay();
    gpio_pin_low(47);
    }
    }

  25. fei chen

    Hi Brian,
    Sorry, gpio.c should be:

    #include

    void gpio_pin_fsel(u32 pin, enum gpio_fsel value)
    {
    volatile u32 *base = (u32 *)GPIO_BASE;
    base[GPIO_GPFSEL0+pin/10] |= (value << pin%10*3);
    }

    void gpio_pin_high(u32 pin)
    {
    volatile u32 *base = (u32 *)GPIO_BASE;
    base[GPIO_GPSET0+pin/32] |= (1 << pin%32);
    }

    void gpio_pin_low(u32 pin)
    {
    volatile u32 *base = (u32 *)GPIO_BASE;
    base[GPIO_GPCLR0+pin/32] |= (1 <> pin%32) & 1;
    }

  26. Varghese Mathew

    I was trying this on the Raspberry Pi 2.

    I wasn’t at first able to get this to work

    But then the precompiled binary and the bootcode.bin+start.elf that you had on the github worked.

    So I diffed your armc-03.c file with my file which is a ditto copy from what you have here on the blog.
    I see this
    70a99 (the difference in line numbers being I don’t have the license in the header comments)
    > int main(void) __attribute__((naked));

    I added that line on top of (and in addition to) the int main (void) line (exactly as you have in armc-03.c
    and also
    #define RPI2
    at the top and then it works.

    Then I verified it works with the newest start.elf+bootcode.bin as well.

    Also verified, when I take out the attribute line, it doesn’t work again. (And if I take out the int main(void) without the attribute, it doesn’t compile)

  27. Varghese Mathew

    PS: in spite of difficulties trying to figure this out – I did get it working.
    Thank you A TON for writing and maintaining this – this is something I’ve always wanted to try and learn.

    PS2: is the source for start.elf and bootcode.bin available? Or is that closed source?

  28. Peter_P

    Success! Thank you Brian for an excellent article!

    Going ‘bare metal’ here is a first for me with the RPi and your article has walked me through it very well indeed. It is just Step01 of course but it has simply my day! – and of the previous 2 or 3 days too!!

    Running under Ubuntu 15.10, using the latest version (4.9) of gcc-arm-none-eabi (installed via software centre) built in Code::Blocks IDE, I built your armc-03.c from GitHub and produce a kernel.img of 220 bytes that flashes the green (OK?) LED at a circa 1/2 sec cycle.

    With Code::Blocks I simply stripped out all it’s defacto-options using just the information supplied from GitHub’s build files (specifically for armc-03.c).
    Whoaa! Now for Step-02!

    Thanks again Brian!

    PS. I initially gave myself some basic ‘not booting’ problems from putting all files in a subdirectory /boot (thus ‘mimicking’ my Raspbian SDcard). I also orginally had my SDcard with F16 format (my active partition is just 1GB so accepts F16). My Raspbian SDcard has a boot partition (~1GB) using F16 format and boots fine… but to be on the safe side I ‘followed orders’ and used FAT32 for the above bare-metal example

  29. Matt

    How do the memory addresses work in your code? I see in the processor documentation that the GPFSEL4 is marked as/at 0x0010 (or 0x32000010) but you have it referenced at 0x3F300000 for selecting the gpio pin? Why? Also, for setting the pin, the address is marked at 0x3200001C but you have it referenced at 0x32000007. Also at 0x3200800F for setting it to zero. This doesn’t make sense to me, so I must be misinterpreting your code. I’m using your defines as address values. Is this not what they are? I haven’t used C in a long time and I’ve never done low level programming like this before.

  30. Albert Dayn

    For anyone doing this with a Pi Zero, use the code applicable to the B+, as both have similar GPIO setups. This includes the Act LED being tied to GPIO47. However, unlike the plus models it’s in an active low configuration, so the line

    gpio[LED_GPCLR] = (1 << LED_GPIO_BIT);

    turns the LED on, like the origional Pi.

  31. Bernd

    Hello Brian_S,
    thanks a lot for your idea and work.
    The sources Armc-02/03 need for compiling a compiler switch “-DRPIBPLUS=1” for arm1176jzf-s:

    arm-none-eabi-gcc -DRPIBPLUS=1 -O2 -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s -nostartfiles armc-2.c -o kernel.elf

    And “-DRPI2=1” for cortex-a7.
    Then it works fine.

    1. Brian_S Post author

      Hi Bernd,

      Thanks for your comment. I spotted an error in the armc-02/build-rpi-bplus.* scripts which didn’t define -RPIBPLUS and were using the wrong processor definition. armc-03 looks ok though.

      Thanks, Brian.

  32. Francky

    Hi Brian,

    Thank you for this really good and interresting tutorial.

    I met a problem while I compile the part-1/armc-02 code with this command line for my arm-test.c file that contains the code for lighting the led :
    “/root/Informatique/logiciels/gcc-arm-none-eabi-5_2-2015q4/bin/arm-none-eabi-gcc -o2 -mfpu=vfp -mfloat-abi=hard -march=armv7-a -mtune=cortex-a7 -nostartfiles /root/Informatique/rpi/tuto_embedded_arm/arm-test.c -o kernel.elf”

    And I get this error like it is said in the tuto :
    “/root/Informatique/logiciels/gcc-arm-none-eabi-5_2-2015q4/bin/../lib/gcc/arm-none-eabi/5.2.1/../../../../arm-none-eabi/bin/ld: warning: cannot find entry symbol _start; defaulting to 00008000”

    So I wrote the lines you advise :
    “/root/Informatique/logiciels/gcc-arm-none-eabi-5_2-2015q4/bin/arm-none-eabi-objcopy kernel.elf -O binary kernel.img”

    But I cannot see any compiled file in the directory !?!
    I think that when I compile the code, I must find the compiled code into another file, isn’t it ? but my directory still contains the only “arm-test.c” file.

    Can you help me please ?

    Thank you !

    1. Brian_S Post author

      The toolchain is downloaded or installed for your platform, no need to make. No IDE is required, just a text editor if you wish to develop code.

      There are no preferably boards either, apart from I’ve only just got a pi-zero and therefore I can’t confirm it works well yet…

  33. Dave

    Great and interesting article, cant wait to read the other parts!

    the line

    /* Assign the address of the GPIO peripheral (Using ARM Physical Address) */
    gpio = (unsigned int*)GPIO_BASE;

    is setting the address of gpio to the value pointed by 0x3F200000. 0x3F200000 is a ARM Physical address.

    Is the value at the ARM address of 0x3F200000 the respective videocore address? If this correct what writes the VC address in the ARM memory?

  34. Marko

    Great article, but unfortunately i can’t get it to work. I think i tried everything. I used your bootcode.bin and start.elf file as well as your compiled file for rpi b+ but is still not working. It has the same effect as if no sd card is inserted (both leds are on). I also have pi 2 model b and the code also didn’t work (ofcourse i took the binary for rpi2). I tried with the latest bootcode.bin and start.elf.

    Does the sd card needs any special partion or something like that? I formated it using fat32 filesystem.
    Have you any ideas what could go wrong and what should i do to get it working?

    Thanks for your reply. πŸ™‚

  35. Alexandre

    Dear Brian,

    I just can join others to congratulate you about this great tutorial. Lots of effective information with proper explanation and sources, to really make things and understanding them. Thank you very much for that.

    I meet a light problem in my case however. I took the code from the web page rather than from the files you provided and I noticed a line you did not comment:
    int main(void) __attribute__((naked));
    I’m not very clear with this and the reason why it is necessary. First I omitted it and found my code ran only once (the led just turned OFF then never turned back ON). Somehow the While seemed to be ignored. Then I added this attribute to the main and this fixed the behaviour perfectly.

    Even I understand the function had some additional entry and exit code without the naked attribute, I cannot explain the resulting behaviour :/
    As well as I cannot understand why the naked attribute is necessary.

    Any highlight on this point would be very helpful.

    Thanks again for everything, continuing to step 2 now πŸ™‚

    Alexandre

  36. SJ

    Hi,

    I am a bare metal newbie. I am trying to install the toolchain and keep getting the message

    “arm-none-eabi-gcc is not recognized as an internal or external command” when I try compiling the arm-02.c example. How do i fix the issue?

    Regards,
    SJ

  37. B_Sanford

    Hi,

    Thank you for doing these tutorials, I enjoy working through them.

    If possible I would like to ask a question.

    When accessing the registers this way (PART-1/ARMC-02) gpio[LED_GPFSEL] this way vs (GPIO_BASE+0x10)

    Does gpio[LED_GPFSEL] work because each increment of the base address is a byte wide? I am trying to be sure I understand the BCM2835 GPIO register spec view spec (section 6/2) and the implementation.

  38. Saijo

    rpi-zero uses this configuration in arm-03.c (gpio 47 and bcm2835 )

    #define GPIO_BASE 0x20200000UL

    #define LED_GPFSEL GPIO_GPFSEL4
    #define LED_GPFBIT 21
    #define LED_GPSET GPIO_GPSET1
    #define LED_GPCLR GPIO_GPCLR1
    #define LED_GPIO_BIT 15

Leave a Reply