RPi GPIO Using Linux, Registers and C

Go to part 1(sysfs method)

Unlike sysfs method, direct register access requires some knowledge about registers in SOC. But that’s okay. We will discuss it here along with example C programs. Register knowledge is also good for writing kernel drivers for Linux. But that’s a topic for another post.

In direct method we will choose the registers and ask the OS to write to those registers what we tell it to write. Also we don’t need any extra libraries to be installed for this program to work. Earlier in sysfs method we just told the OS what kind of general behavior we want. The OS automatically initialized the required registers with required values. I assume you have some experience writing programs for microcontrollers.

You can also write program for the beaglebone in a similar way. Some modifications like changing the registers are required. But the overall process and Linux system calls remains the same.

modelBCM2835 Peripheral Base
Address
GPIO base address
raspberry pi 10x200000000x20000000+0x200000
raspberry pi 2/30x3F0000000x3F000000+0x200000

Below are the registers we care about. Note that these are registers from the BCM2835 data sheet. Actual SOC on raspberry pi 2 is BCM2836(we don’t have access to that data sheet). So in your case the addresses are most probably wrong. Since I’m using raspberry pi 2 we need to use 0x3F000000 as peripheral base address instead of 0x7E200000.

AddressRegisterOffsetPurpose
0x7E200000GPFSEL00x00Input/Output selection
0x7E200004GPFSEL10x04Input/Output selection
0x7E20001CGPSET00x1CSet pin high
0x7E200028GPCLR00x28Clear pin
0x7E200034GPLEV00x34Get pin input level

We are going to make use of pointers here. So make sure you know how pointers work in C. Its use is quite simple in our program. Hardware setup is same as previous post

Direct Register Access – GPIO output

Here is the program for blinking an LED:

Explanation

The key thing we are doing in the above program is writing to the /dev/mem file. This file gives access to the physical memory(RAM) of the raspberry pi. Since peripherals are memory mapped we get access to all the peripherals of the SOC. The starting of the file indicates the address 0x3F000000 which is the base address of the BCM2835 peripherals. Note that the exact datasheet for the SOC on your raspberry pi may not be available. But we can still use the offset addresses from BCM2835 datasheet. you cannot directly use the addresses from datasheet in our program because the OS will treat them as virtual addresses. Meaning these addresses will not be pointing to the actual peripheral.

/dev/mem is a character device file. They store ASCII code like values. Byte addresses in /dev/mem are treated as physical memory addresses. Anything you write to this file will be written to corresponding parts of RAM or memory mapped I/O devices(most probably memory mapped I/O devices). So if you make a mistake writing to this file something bad may happen to your system. But a reboot should fix the problem.

To make the reading and writing of ‘mem’ easier we mapped the address range 0x3F200000 – 0x3F2000B0 to the virtual address space of our program and stored the initial address of the block of memory to a pointer variable named ‘gpio_map’. Using this pointer(GPIO base address) we can access all registers in this address range. Here I mapped til 0x3F2000B0 because we will only be using this range for our GPIO example. If you want you can map more addresses.

One thing you maybe confused of is how did we get to adding 1, 7 and 10 to ‘gpio’

That’s because we have used an integer pointer to store the starting address of GPIO peripheral. On the raspberry pi integers will have a size of 4 bytes(each register also is of 4 bytes). So incrementing our pointer variable by 1 actually internally increments pointer by 4 bytes or 32 bits. So to access register GPSET0 which is 28 bytes away from gpio peripheral base address we add 7 (7*4=28). If we were using character pointer(character allocation size = 1byte) we would be adding 28 instead of 7.

Direct Register Access – GPIO input

Explanation

Here all we are doing is making GPIO4 as an input by clearing corresponding bits in GPFSEL0 and then displaying the value of 4th bit in GPLEV0 register. So if you have connected GPIO4 to ground then 0 will be displayed on screen. Else if you have connected that pin to 3.3V then a positive number will be displayed. Keeping a pin in floating state is not good and may cause random change on the input level due to electromagnetic fluctuations.

Compilation and running

1. Create the program file using vim editor or nano editor with .c extension:
vim gpio_program.c
2. Compile the program and create executable file using gcc:
gcc -o gpio_exe gpio_program.c
3. Run the program using sudo( to access /dev/mem we need superuser privillages):
sudo ./gpio_exe
4. Stop running the program press Ctrl+C.

Go to part-1(sysfs method)

Leave a Reply

Your email address will not be published. Required fields are marked *