Low-Level GPIO Programming of Raspberry Pi 2 in C

The most interesting thing about the Raspberry Pi is programming it’s pins to do something practically useful. As a student of electrical engineering, I’m going to write my thesis in the coming two final semesters about battery cell balancing. To accomplish this, I’ll have to use a microcontroller as the brains of the circuit. The Arduino is a perfect candidate for the task, since it has analog inputs, whereas the Raspberry Pi is only digital. However, the Raspi is capable of more powerful things and it can also take analog input when connected with an Arduino, for example. So I bought my Raspberry Pi 2 back when it was the newest. I bought a case, a 32 GB micro SD card, a wifi dongle, a Sunfounder starter’s kit and installed Raspbian on it. As my code editor I use Geany, which is an awesome little piece of software. The reason for using it is, that I don’t want to write gcc x.c -o x and the rest each time I want to run a program. With Geany, I can build and run code very easily. In the beginning, I used the WiringPi library, which comes with all the functions to play around with GPIOs. And exactly this was the problem. I wanted to understand how those functions worked, how they set a pin high and low, as input and output and so on. I wanted to directly access the pins and write my own functions to control them to make an LED blink, to start with. Using someone else’s code without even understanding it just wasn’t enough for me. I wanted to do real, hard-core microcontroller programming.

It wasn’t easy to find out how to do that, because mostly everyone in the internet was doing high-level programming. Finally I found a blog post from Pieter-Jan here. As it turns out, directly programming the pins is actually easy: you need to access different registers and set specific bits. The meat of the whole thing is finding the registers and set the bits with bit shifting and bitwise operations. In principle it’s very easy, but I couldn’t have done it without Pieter’s blog post and this source. Pieter-Jan’s post is particularly useful, because he actually explains how his code works. Since I mostly copied his code, I won’t explain much here. Ho does a great job doing that on his own website!

The code in both of these sources was written for the first Raspberry Pi, but it works on the Pi 2 as well with only a small modification. BCM2708_PERI_BASE has to be changed to 0x3F000000 and fcntl.h needs to be included in the header file. After (mostly) understanding Pieter’s code, I copied it into a header file and amended it with some of my own functions. Since the Pi 2 has a BCM2836 chip, I named the struct bcm2836_peripheral instead of bcm2835, which is in the first Pi. It is important to note, that this code uses the Broadcomm GPIO numbering, which is different from the WiringPi numbering! If you want to know which pins are what, just type gpio readall in the terminal and you should see this (WiringPi has to be installed):

 +-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 |     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     |
 |   2 |   8 |   SDA.1 |   IN | 1 |  3 || 4  |   |      | 5V      |     |     |
 |   3 |   9 |   SCL.1 |   IN | 1 |  5 || 6  |   |      | 0v      |     |     |
 |   4 |   7 | GPIO. 7 |  OUT | 1 |  7 || 8  | 1 | ALT0 | TxD     | 15  | 14  |
 |     |     |      0v |      |   |  9 || 10 | 1 | ALT0 | RxD     | 16  | 15  |
 |  17 |   0 | GPIO. 0 |   IN | 0 | 11 || 12 | 0 | IN   | GPIO. 1 | 1   | 18  |
 |  27 |   2 | GPIO. 2 |   IN | 0 | 13 || 14 |   |      | 0v      |     |     |
 |  22 |   3 | GPIO. 3 |   IN | 0 | 15 || 16 | 0 | IN   | GPIO. 4 | 4   | 23  |
 |     |     |    3.3v |      |   | 17 || 18 | 0 | IN   | GPIO. 5 | 5   | 24  |
 |  10 |  12 |    MOSI |   IN | 0 | 19 || 20 |   |      | 0v      |     |     |
 |   9 |  13 |    MISO |   IN | 0 | 21 || 22 | 0 | IN   | GPIO. 6 | 6   | 25  |
 |  11 |  14 |    SCLK |   IN | 0 | 23 || 24 | 1 | IN   | CE0     | 10  | 8   |
 |     |     |      0v |      |   | 25 || 26 | 1 | OUT  | CE1     | 11  | 7   |
 |   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | IN   | SCL.0   | 31  | 1   |
 |   5 |  21 | GPIO.21 |  OUT | 0 | 29 || 30 |   |      | 0v      |     |     |
 |   6 |  22 | GPIO.22 |  OUT | 1 | 31 || 32 | 0 | IN   | GPIO.26 | 26  | 12  |
 |  13 |  23 | GPIO.23 |   IN | 0 | 33 || 34 |   |      | 0v      |     |     |
 |  19 |  24 | GPIO.24 |   IN | 0 | 35 || 36 | 0 | IN   | GPIO.27 | 27  | 16  |
 |  26 |  25 | GPIO.25 |   IN | 0 | 37 || 38 | 0 | IN   | GPIO.28 | 28  | 20  |
 |     |     |      0v |      |   | 39 || 40 | 0 | IN   | GPIO.29 | 29  | 21  |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+

Before diving into the code, let’s see the circuit. The LED’s positive (longer) pole is connected in series with a 220 Ohm resistor to the Pi’s 3,3 V power pin. The LED’s negative pole is connected to the Pi’s GPIO pin we want to program.

Blinking LED circuit

Now let’s see the header file! For explanations, visit Pieter’s website!

// source: www.pieter-jan.com/node/15

#include <stdio.h>

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> // this library has to be included too, otherwise map_peripheral doesn't work. source: elinux.org/RPi_GPIO_Code_Samples

#include <unistd.h>

#define BCM2708_PERI_BASE	0x3F000000
#define GPIO_BASE			(BCM2708_PERI_BASE + 0x200000)

#define BLOCK_SIZE 			(4*1024)
#define INPUT				0 // defining the GPIO function to use in my own function, the value has no significance
#define OUTPUT				1 // defining the GPIO function to use in my own function, the value has no significance
#define LOW					0 // defining the GPIO state to use in my own function, the value has no significance
#define HIGH				1 // defining the GPIO state to use in my own function, the value has no significance

struct bcm2836_peripheral
{
	unsigned long addr_p;
	int mem_fd;
	void *map;
	volatile unsigned int *addr;
};

struct bcm2836_peripheral gpio = {GPIO_BASE};

extern struct bcm2836_peripheral gpio;

int map_peripheral(struct bcm2836_peripheral *p)
{
   // Open /dev/mem
   if ((p->mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { // without fcntl.h you get an error here
      printf("Failed to open /dev/mem, try checking permissions.\n");
      return -1;
   }
 
   p->map = mmap(
      NULL,
      BLOCK_SIZE,
      PROT_READ|PROT_WRITE,
      MAP_SHARED,
      p->mem_fd,      // File descriptor to physical memory virtual file '/dev/mem'
      p->addr_p       // Address in physical map that we want this memory block to expose
   );
 
   if (p->map == MAP_FAILED) {
        perror("mmap");
        return -1;
   }
 
   p->addr = (volatile unsigned int *)p->map;
 
   return 0;
}
 
void unmap_peripheral(struct bcm2836_peripheral *p) {
 
    munmap(p->map, BLOCK_SIZE);
    close(p->mem_fd);
}

// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x)
#define INP_GPIO(g)   *(gpio.addr + ((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g)   *(gpio.addr + ((g)/10)) |=  (1<<(((g)%10)*3))
#define SET_GPIO_ALT(g,a) *(gpio.addr + (((g)/10))) |= (((a)<=3?(a) + 4:(a)==4?3:2)<<(((g)%10)*3))
 
#define GPIO_SET  *(gpio.addr + 7)  // sets   bits which are 1 ignores bits which are 0
#define GPIO_CLR  *(gpio.addr + 10) // clears bits which are 1 ignores bits which are 0
 
#define GPIO_READ(g)  *(gpio.addr + 13) &= (1<<(g))

// my own functions using Pieter-Jan's code

void gpio_inout(int io, int pin)
{
	if (io == INPUT)
		INP_GPIO(pin);
	
	if (io == OUTPUT)
	{
		INP_GPIO(pin); // if you want to set a pin as output, always set it as input first, for reasons see Pieter's explanation
		OUT_GPIO(pin);
	}
}

void gpio_set(int state, int pin)
{
	if (state == LOW)
		GPIO_SET = 0 << pin;
	if (state == HIGH)
		GPIO_SET = 1 << pin;
}

void gpio_clear(int pin)
{
		GPIO_CLR = 1 << pin;
}

void gpio_read(int pin)
{
		GPIO_READ(pin);
}

Now let’s see the c file, where I put my custom functions in use. It’s important, that you have the header file and the c file in the same directory.

#include "rpi2.h" // you have to have the header file and the c file in the same directory for this to work

int main(int argc, char **argv)
{	
	int pin = 27; // this code uses the BCM GPIO numbering
	
	if(map_peripheral(&gpio) == -1) 
	{
		printf("Failed to map the physical GPIO registers into the virtual memory space.\n");
		return -1;
	}
	
	gpio_inout(OUTPUT, pin); // set pin as output
	
	while(1)
	{
		gpio_set(HIGH, pin); // set pin high
		sleep(1);
 
		gpio_clear(pin); // clear pin
		sleep(1);
	}
	
	return 0; 
}

If you get your circuit right, this code will make a LED blink without installing any special libraries. The cool thing is, that once you know how to set a pin’s function and state, i. e. you know which bits you have to set, you can write your own code in plain vanilla C to do pretty much anything.

Don’t forget to check these sources out:
http://www.pieter-jan.com/node/15
http://elinux.org/RPi_GPIO_Code_Samples

Advertisements

I enjoy microcontroller programming (AVR), programming in C/C#, playing with electronics, the Raspberry Pi and Arduino. My other passion is for IT - virtualization, PowerShell, servers and so on.

Tagged with: ,
Posted in Programming, Raspberry Pi

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Archives

Enter your email address to follow this blog and receive notifications of new posts by email.

Join 167 other followers

%d bloggers like this: