Void’s Vault

Knowledge source for efficiency.

How to Use the GPIO on the BeagleBone Black

This info was scattered on multiple sites, so this post shows how to use the BeagleBone Black works in the fun example of playing SNES using a USB controller.

Warning about the example: The BBB seems barely not fast enough for the game to work, even with direct GPIO access. If it’s fast enough I had the following issues that was messing with the communication. Buttons responds, but when a button is pressed, the one or two buttons coming right next to the currently polled one appear pressed from time to time. Also, my wire connections seemed faulty, because I was seeing button pressed in the game while moving the wires when the programs where not running. Possible cause is that ground was not connected, only the latch, the clock and the data wires. Anyway, if someone gets it working, please contact me.

First of all, read the GPIO Sysfs Interface for Userspace. This will show you the bash commands to access the GPIO.

Also, here’s my understanding of the SNES’ communication protocol. See this reverse engineering for more info and for the complete protocol. I opened an SNES controller extension so I could see what was the wires I needed. See the image below. In my case, black is ground. I have not plugged it (even though I think I should have). Green is the +5V, I don’t need this one as the controller is already powered with my BBB. Blue is the clock (INPUT), yellow is the latch (INPUT), red is the serial data (OUTPUT).

TLDR: Latch and clock wires must be inputs with a pullup. Setting 0 to the data makes a button pressed, and 1 releases the button. Check the ascii images in the reverse engineering to figure out the communication protocol.

(You must stay as root if you want to manipulate the GPIOs)

Now, when you use the GPIO pins on the BeagleBone Black (or white), there are many things you need to know.

The first thing is that you must inform the kernel what pins you will like to use. As far as I know, this is to tell the kernel that the pins are about to be manipulated and must be activated/reserved to the user’s applications. All my learnings come from Valvers’ website.

I plugged the clock at P9_14, the latch at P9_15, and the data at P9_16 as these header pins were one of the 65 possible digital I/Os. Looking at Valvers’ cheatsheet, I saw that these corresponded respectively to gpio1[18], gpio1[16] and gpio1[19]. I also got the offsets: 848, 840 and 84C. I also knew from the reverse engineering that the three needed to be pullup enabled.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
sudo su
PINS="/sys/kernel/debug/pinctrl/44e10800.pinmux/pins"
SLOTS="/sys/devices/bone_capemgr.9/slots"

cat $PINS
pin 18 (44e10848) 00000027 pinctrl-single
0x27 = 0010 0111 -> 010111 -> output,pullup,pullenabled,mode 7
pin 19 (44e1084c) 00000027 pinctrl-single
0x27 = 0010 0111 -> 010111 -> output,pullup,pullenabled,mode 7
pin 16 (44e10840) 00000027 pinctrl-single
0x27 = 0010 0111 -> 010111 -> output,pullup,pullenabled,mode 7

# For the inputs, we would need
0x67 = 0110 0111 -> 110111 -> input,pullup,pullenabled,mode 7

cat /sys/kernel/debug/pinctrl/44e10800.pinmux/pinmux-pins
pin 18 (44e10848): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 19 (44e1084c): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 16 (44e10840): (MUX UNCLAIMED) (GPIO UNCLAIMED)

So we need to claim these GPIOs with a device tree overlay. Here’s my SNES-IO-00A0.dts file. Valvers claims that it’s very important that you keep the filename accurate. The part-number should be reflected in the filename.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/dts-v1/;
/plugin/;

/ {
    compatible = "ti,beaglebone", "ti,beaglebone-black";

    /* identification */
    part-number = "SNES-IO";
    version = "00A0";

    fragment@0 {
        target = <&am33xx_pinmux>;
        __overlay__ {
            pinctrl_snes: pinmux_pinctrl_snes {
                pinctrl-single,pins = <
                    0x040 0x67  /* P9_15 gpio1_16, INPUT PULLUP | MODE7 */
                    0x048 0x67  /* p9_14 gpio1_18, INPUT PULLUP | MODE7 */
                    0x04c 0x27  /* p9_16 gpio1_19, OUTPUT PULLUP| MODE7 */
                >;
            };
        };
    };

    fragment@1 {
        target = <&ocp>;
        __overlay__ {
            snes_helper: helper {
                compatible = "bone-pinmux-helper";
                pinctrl-names = "default";
                pinctrl-0 = <&pinctrl_snes>;
                status = "okay";
            };
        };
    };
};

Put this file on your BBB, then compile it. (Note that the offsets in this file are 0x040 instead of 0x840.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
dtc -O dtb -o SNES-IO-00A0.dtbo -b 0 -@ SNES-IO-00A0.dts
cp SNES-IO-00A0.dtbo /lib/firmware/

cat $PINS
pin 16 (44e10840) 00000027 pinctrl-single
pin 18 (44e10848) 00000027 pinctrl-single
pin 19 (44e1084c) 00000027 pinctrl-single

echo SNES-IO > $SLOTS
cat $SLOTS
    7: ff:P-O-L Override Board Name,00A0,Override Manuf,SNES-IO

cat $PINS
pin 16 (44e10840) 00000067 pinctrl-single <-------------LATCH
pin 18 (44e10848) 00000067 pinctrl-single <-------------CLOCK
pin 19 (44e1084c) 00000027 pinctrl-single <-------------DATA

cd /sys/kernel/debug/pinctrl/44e10800.pinmux
cat pingroups
group: pinmux_pinctrl_snes
pin 16 (44e10840)
pin 18 (44e10848)
pin 19 (44e1084c)

cat pinmux-pins
pin 16 (44e10840): helper.15 (GPIO UNCLAIMED) function pinmux_pinctrl_snes group pinmux_pinctrl_snes
pin 18 (44e10848): helper.15 (GPIO UNCLAIMED) function pinmux_pinctrl_snes group pinmux_pinctrl_snes
pin 19 (44e1084c): helper.15 (GPIO UNCLAIMED) function pinmux_pinctrl_snes group pinmux_pinctrl_snes

Then every time you boot your BBB, run this to setup the GPIOs.

1
2
3
sudo su
SLOTS="/sys/devices/bone_capemgr.9/slots"
echo SNES-IO > $SLOTS

OK! Now you are good to start using the GPIOs! But how does it work. First, the simple and slow way.

To use a joystick (or game controller), use this code library with this simple program (specific to my controller, a logitec precision but easily modifiable).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#include "joystick.hh"

#include <stdio.h>      /* printf */
#include <string.h>

#define B_OFFSET      0
#define Y_OFFSET      1
#define SELECT_OFFSET 2
#define START_OFFSET  3
#define UP_OFFSET     4
#define DOWN_OFFSET   5
#define LEFT_OFFSET   6
#define RIGHT_OFFSET  7
#define A_OFFSET      8
#define X_OFFSET      9
#define L_OFFSET     10
#define R_OFFSET     11


/*
b=1
y=0
select=8
start=9
up=axis1 negative
down=axis1 positive
left=axis0 negative  (-32768)
right=axis0 positive (32767)
a=2
x=3
L=4,6
R=5,7
*/

// http://www.cs.cf.ac.uk/Dave/C/node27.html
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHMSZ     27

int main(int argc, char** argv)
{

     // Name the shared memory segment
     key_t sharedMemoryKey = 5678;

     // Create the segment
     int sharedMemoryId;
     if ((sharedMemoryId = shmget(sharedMemoryKey, SHMSZ, IPC_CREAT | 0666)) < 0) {
        perror("shmget");
        exit(1);
    }

    //Attach the segment to data space
    char *sharedMemory;
    if ((sharedMemory = (char *) shmat(sharedMemoryId, NULL, 0)) == (char *) -1) {
        perror("shmat");
        exit(1);
    }

    // Now put some things into the memory for the other process to read.
    uint32_t *sharedData;
    sharedData = (uint32_t*) sharedMemory;
    *(sharedData) = 0;

    // Initialize joystick and start using it along with the shared memory
    Joystick joystick("/dev/input/js0");

    if (!joystick.isFound())
    {
        printf("open failed.\n");
        exit(1);
    }

    struct timespec req, rem;
    req.tv_sec = 0;
    req.tv_nsec = 5000000;

    uint32_t command = 0xFFFFFFFF;
    *sharedData = command;

    while (true)
    {

        nanosleep(&req, &rem); // Restrict rate

        JoystickEvent event;
        if (joystick.sample(&event))
        {
            if (event.isButton())
            {
                switch (event.number)
                {
                    case 0:
                    {
                        command = (command & ~(1<<Y_OFFSET)) | ((1<<Y_OFFSET) & ~(event.value<<Y_OFFSET));
                        break;
                    }
                    case 1:
                    {
                        command = (command & ~(1<<B_OFFSET)) | ((1<<B_OFFSET) & ~(event.value<<B_OFFSET));
                        break;
                    }
                    case 2:
                    {
                        command = (command & ~(1<<A_OFFSET)) | ((1<<A_OFFSET) & ~(event.value<<A_OFFSET));
                        break;
                    }
                    case 3:
                    {
                        command = (command & ~(1<<X_OFFSET)) | ((1<<X_OFFSET) & ~(event.value<<X_OFFSET));
                        break;
                    }
                    case 4:
                    case 6:
                    {
                        command = (command & ~(1<<L_OFFSET)) | ((1<<L_OFFSET) & ~(event.value<<L_OFFSET));
                        break;
                    }
                    case 5:
                    case 7:
                    {
                        command = (command & ~(1<<R_OFFSET)) | ((1<<R_OFFSET) & ~(event.value<<R_OFFSET));
                        break;
                    }
                    case 8:
                    {
                        command = (command & ~(1<<SELECT_OFFSET)) | ((1<<SELECT_OFFSET) & ~(event.value<<SELECT_OFFSET));
                        break;
                    }
                    case 9:
                    {
                        command = (command & ~(1<<START_OFFSET)) | ((1<<START_OFFSET) & ~(event.value<<START_OFFSET));
                        break;
                    }
                }
            }
            else if (event.isAxis())
            {
                switch (event.number)
                {
                    case 0: {

                        if (event.value == 0)
                            command |= ((1<<LEFT_OFFSET) | (1<<RIGHT_OFFSET));
                        else if ( event.value > 0 )
                            command &= ~(1<<RIGHT_OFFSET);
                        else
                            command &= ~(1<<LEFT_OFFSET);
                        break;
                    }
                    case 1:
                    {
                        if (event.value == 0)
                            command |= ((1<<UP_OFFSET) | (1<<DOWN_OFFSET));
                        else if ( event.value > 0 )
                            command &= ~(1<<DOWN_OFFSET);
                        else
                            command &= ~(1<<UP_OFFSET);
                        break;
                    }
                }
            }
            printf("Command: %u \n", command);
            *sharedData = command;
        }
    }
}

There’s a library for handling the GPIO calls for the BBB. It’s poorly written, but it still gets the job done. Watch out for unsigned int to int conversions, as they are error prone. Using it, and then refactoring it a bit to make it as fast as possible, gave this code (unfortunately, way too slow for me, but still gives simple code).

GPIO/GPIOConst.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#ifndef BEAGLEBONEBLACK_GPIO_SRC_GPIO_GPIOCONST_H_
#define BEAGLEBONEBLACK_GPIO_SRC_GPIO_GPIOCONST_H_

namespace GPIO {

enum DIRECTION {
  INPUT = 0,
  OUTPUT = 1
};

enum PIN {
  LOW = 0,
  HIGH = 1
};

enum EDGE {
  NONE = 0,
  RISING = 1,
  FALLING = 2,
  BOTH = 3
};

typedef struct pins_t {
    const char *name;
    const char *key;
    unsigned int gpio;
    int pwm_mux_mode;
    int ain;
    int isAllocatedByDefault;
} pins_t;

class GPIOConst {
public:
    GPIOConst();
    virtual ~GPIOConst();

    unsigned int getGpioByKey(const char *key);
    unsigned int getGpioByName(const char *name);
    const char* getGpioNameByPin(const unsigned int pin);
    const char* getGpioKeyByPin(const unsigned int pin);

    int isPinAllocatedByDefault(const unsigned int pin);

private:
    static pins_t pinTable[97];
};

} /* namespace GPIO */
#endif  // BEAGLEBONEBLACK_GPIO_SRC_GPIO_GPIOCONST_H_

GPIO/GPIOConst.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#include "./GPIOConst.h"
#include <stdio.h>
#include <string.h>

namespace GPIO {

/**
 * Table of values for pins.
 * Table generated based on https://raw.github.com/jadonk/bonescript/master/node_modules/bonescript/bone.js
 */
pins_t GPIOConst::pinTable[] = {
    { "USR0", "USR0", 53, -1, -1, 1 },
    { "USR1", "USR1", 54, -1, -1, 1 },
    { "USR2", "USR2", 55, -1, -1, 1 },
    { "USR3", "USR3", 56, -1, -1, 1 },
    { "DGND", "P8_1", 0, -1, -1, -1 },
    { "DGND", "P8_2", 0, -1, -1, -1 },
    { "GPIO1_6", "P8_3", 38, -1, -1, 1 },
    { "GPIO1_7", "P8_4", 39, -1, -1, 1 },
    { "GPIO1_2", "P8_5", 34, -1, -1, 1 },
    { "GPIO1_3", "P8_6", 35, -1, -1, 1 },
    { "TIMER4", "P8_7", 66, -1, -1, 0 },
    { "TIMER7", "P8_8", 67, -1, -1, 0 },
    { "TIMER5", "P8_9", 69, -1, -1, 0 },
    { "TIMER6", "P8_10", 68, -1, -1, 0 },
    { "GPIO1_13", "P8_11", 45, -1, -1, 0 },
    { "GPIO1_12", "P8_12", 44, -1, -1, 0 },
    { "EHRPWM2B", "P8_13", 23, 4, -1, 0 },
    { "GPIO0_26", "P8_14", 26, -1, -1, 0 },
    { "GPIO1_15", "P8_15", 47, -1, -1, 0 },
    { "GPIO1_14", "P8_16", 46, -1, -1, 0 },
    { "GPIO0_27", "P8_17", 27, -1, -1, 0 },
    { "GPIO2_1", "P8_18", 65, -1, -1, 0 },
    { "EHRPWM2A", "P8_19", 22, 4, -1, 0 },
    { "GPIO1_31", "P8_20", 63, -1, -1, 1 },
    { "GPIO1_30", "P8_21", 62, -1, -1, 1 },
    { "GPIO1_5", "P8_22", 37, -1, -1, 1 },
    { "GPIO1_4", "P8_23", 36, -1, -1, 1 },
    { "GPIO1_1", "P8_24", 33, -1, -1, 1 },
    { "GPIO1_0", "P8_25", 32, -1, -1, 1 },
    { "GPIO1_29", "P8_26", 61, -1, -1, 0 },
    { "GPIO2_22", "P8_27", 86, -1, -1, 1 },
    { "GPIO2_24", "P8_28", 88, -1, -1, 1 },
    { "GPIO2_23", "P8_29", 87, -1, -1, 1 },
    { "GPIO2_25", "P8_30", 89, -1, -1, 1 },
    { "UART5_CTSN", "P8_31", 10, -1, -1, 1 },
    { "UART5_RTSN", "P8_32", 11, -1, -1, 1 },
    { "UART4_RTSN", "P8_33", 9, -1, -1, 1 },
    { "UART3_RTSN", "P8_34", 81, 2, -1, 1 },
    { "UART4_CTSN", "P8_35", 8, -1, -1, 1 },
    { "UART3_CTSN", "P8_36", 80, 2, -1, 1 },
    { "UART5_TXD", "P8_37", 78, -1, -1, 1 },
    { "UART5_RXD", "P8_38", 79, -1, -1, 1 },
    { "GPIO2_12", "P8_39", 76, -1, -1, 1 },
    { "GPIO2_13", "P8_40", 77, -1, -1, 1 },
    { "GPIO2_10", "P8_41", 74, -1, -1, 1 },
    { "GPIO2_11", "P8_42", 75, -1, -1, 1 },
    { "GPIO2_8", "P8_43", 72, -1, -1, 1 },
    { "GPIO2_9", "P8_44", 73, -1, -1, 1 },
    { "GPIO2_6", "P8_45", 70, 3, -1, 1 },
    { "GPIO2_7", "P8_46", 71, 3, -1, 1 },
    { "DGND", "P9_1", 0, -1, -1, -1 },
    { "DGND", "P9_2", 0, -1, -1, -1 },
    { "VDD_3V3", "P9_3", 0, -1, -1, -1 },
    { "VDD_3V3", "P9_4", 0, -1, -1, -1 },
    { "VDD_5V", "P9_5", 0, -1, -1, -1 },
    { "VDD_5V", "P9_6", 0, -1, -1, -1 },
    { "SYS_5V", "P9_7", 0, -1, -1, -1 },
    { "SYS_5V", "P9_8", 0, -1, -1, -1 },
    { "PWR_BUT", "P9_9", 0, -1, -1, -1 },
    {"SYS_RESETn", "P9_10", 0, -1, -1, -1 },
    { "UART4_RXD", "P9_11", 30, -1, -1, 0 },
    { "GPIO1_28", "P9_12", 60, -1, -1, 0 },
    { "UART4_TXD", "P9_13", 31, -1, -1, 0 },
    { "EHRPWM1A", "P9_14", 50, 6, -1, 0 },
    { "GPIO1_16", "P9_15", 48, -1, -1, 0 },
    { "EHRPWM1B", "P9_16", 51, 6, -1, 0 },
    { "I2C1_SCL", "P9_17", 5, -1, -1, 0 },
    { "I2C1_SDA", "P9_18", 4, -1, -1, 0 },
    { "I2C2_SCL", "P9_19", 13, -1, -1, 1 },
    { "I2C2_SDA", "P9_20", 12, -1, -1, 1 },
    { "UART2_TXD", "P9_21", 3, 3, -1, 0 },
    { "UART2_RXD", "P9_22", 2, 3, -1, 0 },
    { "GPIO1_17", "P9_23", 49, -1, -1, 0 },
    { "UART1_TXD", "P9_24", 15, -1, -1, 0 },
    { "GPIO3_21", "P9_25", 117, -1, -1, 1 },
    { "UART1_RXD", "P9_26", 14, -1, -1, 0 },
    { "GPIO3_19", "P9_27", 115, -1, -1, 0 },
    { "SPI1_CS0", "P9_28", 113, 4, -1, 1 },
    { "SPI1_D0", "P9_29", 111, 1, -1, 1 },
    { "SPI1_D1", "P9_30", 112, -1, -1, 0 },
    { "SPI1_SCLK", "P9_31", 110, 1, -1, 1 },
    { "VDD_ADC", "P9_32", 0, -1, -1, -1 },
    { "AIN4", "P9_33", 0, -1, 4, -1 },
    { "GNDA_ADC", "P9_34", 0, -1, -1, -1 },
    { "AIN6", "P9_35", 0, -1, 6, -1 },
    { "AIN5", "P9_36", 0, -1, 5, -1 },
    { "AIN2", "P9_37", 0, -1, 2, -1 },
    { "AIN3", "P9_38", 0, -1, 3, -1 },
    { "AIN0", "P9_39", 0, -1, 0, -1 },
    { "AIN1", "P9_40", 0, -1, 1, -1 },
    { "CLKOUT2", "P9_41", 20, -1, -1, 0 },
    { "GPIO0_7", "P9_42", 7, 0, -1, 0 },
    { "DGND", "P9_43", 0, -1, -1, -1 },
    { "DGND", "P9_44", 0, -1, -1, -1 },
    { "DGND", "P9_45", 0, -1, -1, -1 },
    { "DGND", "P9_46", 0, -1, -1, -1 },
    { NULL, NULL, 0 }
};

GPIOConst::GPIOConst() {}

GPIOConst::~GPIOConst() {}

/**
 * Get gpio pin number by key i.e "P8_10"
 */
unsigned int GPIOConst::getGpioByKey(const char *key) {
  pins_t *p;
  for (p = pinTable; p->key != NULL; ++p) {
    if (strcmp(p->key, key) == 0) {
      return p->gpio;
    }
  }

  return 0;
}

/**
 * Get gpio number by name i.e "GPIO0_7"
 */
unsigned int GPIOConst::getGpioByName(const char *name) {
  pins_t *p;
  for (p = pinTable; p->name != NULL; ++p) {
    if (strcmp(p->name, name) == 0) {
      return p->gpio;
    }
  }

  return 0;
}

/**
 * Get GPIO name by pin number
 */
const char* GPIOConst::getGpioNameByPin(const unsigned int pin) {
  pins_t *p;

  for (p = pinTable; p->name != NULL; ++p) {
    if (p->gpio == pin) {
      return p->name;
    }
  }

  return "";
}

/**
 * Get GPIO key by pin number
 */
const char* GPIOConst::getGpioKeyByPin(const unsigned int pin) {
  pins_t *p;

  for (p = pinTable; p->name != NULL; ++p) {
    if (p->gpio == pin) {
      return p->key;
    }
  }

  return "";
}

/**
 * Is the pin allocated by default
 * refering to https://github.com/derekmolloy/boneDeviceTree/tree/master/docs
 */
int GPIOConst::isPinAllocatedByDefault(const unsigned int pin) {
  pins_t *p;

  for (p = pinTable; p->name != NULL; ++p) {
    if (p->gpio == pin) {
      return p->isAllocatedByDefault;
    }
  }

  return 0;
}
} /* namespace GPIO */

GPIO/GpioInput.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#ifndef BEAGLEBONEBLACK_GPIO_SRC_GPIO_GpioInput_H_
#define BEAGLEBONEBLACK_GPIO_SRC_GPIO_GpioInput_H_


#define SYSFS_GPIO_DIR "/sys/class/gpio"
#include <vector>

#include<iostream>
#include <sys/epoll.h>

#include "./GPIOConst.h"

namespace GPIO {

class GpioInput {
public:
    GpioInput(const unsigned int gpio, const EDGE value);
    virtual ~GpioInput();

    const PIN getValue();

    int getEdge();
    int waitForEdge();

private:
    static char const * strEdge[4];
    void exportPin();
    void setDirection();
    void setEdge(const EDGE edge);

    void unexportPin();

    const unsigned int gpio;
    char directionPath[50];
    char valuePath[50];
    char edgePath[50];

    int valueFileDescriptor, epollInstance;
    struct epoll_event expectedEvent;
};

} /* namespace GPIO */
#endif

GPIO/GpioInput.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include "./GpioInput.h"

#include "./GPIOConst.h"
#include <assert.h>

#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <fstream>
#include <algorithm>
#include <string.h>


namespace GPIO {

char const * GpioInput::strEdge[] = { "none", "rising", "falling", "both" };

GpioInput::GpioInput(const unsigned int gpio, const EDGE edge) : gpio(gpio)
{
    snprintf(directionPath, sizeof(directionPath), SYSFS_GPIO_DIR "/gpio%u/direction", gpio);
    snprintf(valuePath, sizeof(valuePath), SYSFS_GPIO_DIR "/gpio%u/value", gpio);
    snprintf(edgePath, sizeof(edgePath), SYSFS_GPIO_DIR "/gpio%d/edge", gpio);

    this->exportPin();
    this->setDirection();
    this->setEdge(edge);

    valueFileDescriptor = open(valuePath, O_RDONLY | O_NONBLOCK);
    expectedEvent.data.fd = valueFileDescriptor;
    expectedEvent.events = EPOLLIN | EPOLLET | EPOLLPRI;

    epollInstance = epoll_create(1);
    epoll_ctl(epollInstance, EPOLL_CTL_ADD, valueFileDescriptor, &expectedEvent);

    // Ignore the first read (initial value)
    struct epoll_event events;
    if ((epoll_wait(epollInstance, &events, 1, -1)) == -1) {
      throw -1;
    }
}

// equivalent to i.e echo "68" > /sys/class/gpio/export
void GpioInput::exportPin()
{
    std::ofstream stream(SYSFS_GPIO_DIR "/export");
    assert(stream >= 0);
    stream << gpio;
    stream.close();
}

// equivalent to i.e echo "in" > /sys/class/gpio68/direction
void GpioInput::setDirection()
{
    std::ofstream stream(directionPath);
    assert(stream >= 0);
    stream << "in";
    stream.close();
}

// equivalent to i.e echo "rising" > /sys/class/gpio68/edge
void GpioInput::setEdge(const EDGE value)
{
  std::ofstream stream(edgePath);
  assert(stream >= 0);
  stream << strEdge[value];
  stream.close();
}


GpioInput::~GpioInput()
{
  this->unexportPin();

  close(epollInstance);
  close(valueFileDescriptor);
}

// equivalent to i.e echo "68" > /sys/class/gpio/unexport
void GpioInput::unexportPin()
{
  std::ofstream stream(SYSFS_GPIO_DIR "/unexport");
  assert(stream >= 0);
  stream << gpio;
  stream.close();
}


// equivalent to i.e cat /sys/class/gpio68/value
const PIN GpioInput::getValue()
{
  char value;
  std::ifstream stream(valuePath);
  assert(stream >= 0);

  stream >> value;
  stream.close();
  return (value == '1') ? HIGH : LOW;
}


// equivalent to i.e cat /sys/class/gpio68/edge
int GpioInput::getEdge()
{
  char value[7];
  std::ifstream stream(edgePath);

  stream >> value;
  stream.close();

  for (int i = 0; i < static_cast<int>(sizeof(strEdge)); ++i) {
    if (strcmp(strEdge[i], value) == 0)
      return i;
  }
  return -1;
}


int GpioInput::waitForEdge()
{
    static struct epoll_event events;
    epoll_wait(epollInstance, &events, 1, -1);

    return 0;
}

} /* namespace GPIO */

GPIO/GpioOutput.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#ifndef BEAGLEBONEBLACK_GPIO_SRC_GPIO_GpioController_H_
#define BEAGLEBONEBLACK_GPIO_SRC_GPIO_GpioController_H_


#define SYSFS_GPIO_DIR "/sys/class/gpio"
#include <vector>

#include<iostream>
#include <sys/epoll.h>

#include "./GPIOConst.h"

namespace GPIO {

class GpioOutput {
public:
    GpioOutput(const unsigned int gpio);
    virtual ~GpioOutput();

    void setValue(const PIN value);
    void setValue(const uint32_t value);

private:
    void exportPin();
    void unexportPin();

    void setDirection();

    const unsigned int gpio;
    char directionPath[50];
    char valuePath[50];
};

} /* namespace GPIO */
#endif

GPIO/GpioOutput.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include "./GpioOutput.h"

#include "./GPIOConst.h"
#include <assert.h>

#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <fstream>
#include <algorithm>
#include <string.h>


namespace GPIO {

GpioOutput::GpioOutput(const unsigned int gpio) : gpio(gpio)
{
    snprintf(directionPath, sizeof(directionPath), SYSFS_GPIO_DIR "/gpio%u/direction", gpio);
    snprintf(valuePath, sizeof(valuePath), SYSFS_GPIO_DIR "/gpio%u/value", gpio);

    this->exportPin();
    this->setDirection();
}

// equivalent to i.e echo "68" > /sys/class/gpio/export
void GpioOutput::exportPin()
{
  std::ofstream stream(SYSFS_GPIO_DIR "/export");
  stream << gpio;
  stream.close();
}

// equivalent to i.e echo "in" > /sys/class/gpio68/direction
void GpioOutput::setDirection()
{
    std::ofstream stream(directionPath);
    stream << "out";
    stream.close();
}

GpioOutput::~GpioOutput()
{
    this->unexportPin();
}

// equivalent to i.e echo "68" > /sys/class/gpio/unexport
void GpioOutput::unexportPin()
{
    std::ofstream stream(SYSFS_GPIO_DIR "/unexport");
    stream << gpio;
    stream.close();
}

// equivalent to i.e echo "1" > /sys/class/gpio68/value
void GpioOutput::setValue(const PIN value)
{
    std::ofstream stream(valuePath);
    stream << value;
    stream.close();
}

void GpioOutput::setValue(const uint32_t value)
{
    std::ofstream stream(valuePath);
    stream << value;
    stream.close();
}

} /* namespace GPIO */

snesgpio.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include <stdio.h>
#include<iostream>
#include <time.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>

#include "GPIO/GpioInput.h"
#include "GPIO/GpioOutput.h"
#include "GPIO/GPIOConst.h"
#define SHMSZ     27

#define MASK      0x00000001

int main(int argc, char** argv)
{
    // Get the segment created by the server
    key_t sharedMemoryKey = 5678;

    // Locate the segment
    int sharedMemoryId;
    if ((sharedMemoryId = shmget(sharedMemoryKey, SHMSZ, 0666)) < 0) {
        perror("shmget");
        exit(1);
    }

    // Attach the segment to data space
    char *sharedMemory;
    if ((sharedMemory = (char *) shmat(sharedMemoryId, NULL, 0)) == (char *) -1) {
        perror("shmat");
        exit(1);
    }

    // Configure access to the shared value
    uint32_t *sharedData;
    sharedData = (uint32_t*) sharedMemory;
    uint32_t writevalue;

    // Obtain input and output GPIOs
    using namespace GPIO;
    GPIOConst constants;

    unsigned int clockId = constants.getGpioByKey("P9_14"); //blue = data clock = P9 14
    unsigned int latchId = constants.getGpioByKey("P9_15"); //yellow = data latch = P9 15
    unsigned int dataId = constants.getGpioByKey("P9_16"); //red = serial data = P9 16

    GpioInput clock(clockId, EDGE::RISING);
    GpioInput latch(latchId, EDGE::BOTH);
    GpioOutput data(dataId);

    // The SNES controllers drive data for the first button at the falling edge of latch.
    // Data for all other buttons is driven at the rising edge of clock
    // The controllers serially shift the latched button states on every rising edge of the clock, 
    // and the CPU samples the data on every falling edge.

/*
                           |<------------16.67ms------------>|

                           12us
                        -->|   |<--

                            ---                               ---
                           |   |                             |   |
        Data Latch      ---     -----------------/ /----------    --------...


        data clock      ----------   -   -   -  -/ /----------------   -  ...
                                  | | | | | | | |                   | | | |
                                   -   -   -   -                     -   -
                                   1   2   3   4                     1   2
                                                                           
        Serial Data         ----     ---     ----/ /           ---
                           |    |   |   |   |                 |
        (Buttons B      ---      ---     ---        ----------
        & Select        norm      B      SEL           norm
        pressed).       low                            low
                                12us
                             -->|   |<--
*/

    while(true)
    {
        // set output in waiting position until next latch up
        data.setValue(PIN::LOW);

        //Latch up --> data high
        latch.waitForEdge();
        data.setValue(PIN::HIGH);

        //register buttons pressed
        writevalue = (uint32_t) *sharedData;
        if(writevalue == 4294964211)  //L+R+Start+Select == Quit
            break;

        //latch down --> Data first button
        latch.waitForEdge();
        data.setValue(writevalue & MASK);

        //Clock 13-16 --> data high (Ensured by joystick implementation
        for (int i = 0; i < 16; i++)
        {
            writevalue >>= 1; // Prepare next value by shifting bits so it's easier to apply mask

            //clock up --> Data next button           
            clock.waitForEdge();
            data.setValue(writevalue & MASK);
        }
    }

}

As I said, this code would work perfectly for any slow application. But the SNES requires much faster speed. [This question] in stack overflow adressed a faster way of using the GPIOs, but there was many things that was left unexplained. I also found Black Swift’s blog about Working with GPIOs (C/C++) which detailed things differently. Still unsatisfied, I finally uncovered a blog that explain in great detail how to use memory mapping to gain Register access to the GPIOs. Google “technical reference manual of the AM335x-processor” for the latest manual, and find the adresses of GPIO0, GPIO1, GPIO2 and GPIO3, along with the offsets of GPIO_OE, GPIO_DATAIN and GPIO_DATAOUT.

Using this, I modified my code so it’s faster now. This last example will show you how to use the GPIOs the fast way. Enjoy!

GPIO/GPIOConst.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#ifndef BEAGLEBONEBLACK_GPIO_SRC_GPIO_GPIOCONST_H_
#define BEAGLEBONEBLACK_GPIO_SRC_GPIO_GPIOCONST_H_

#define GPIO0_BASE 0x44E07000
#define GPIO1_BASE 0x4804C000
#define GPIO2_BASE 0x481AC000
#define GPIO3_BASE 0x481AE000

#define GPIO_SIZE  0x00001000 // 0x1000 = 4096 = 4 kB following each _BASE

// offsets relative to _BASE according to AM335x Technical Reference Manual
#define GPIO_OE 0x134 
#define GPIO_IN 0x138
#define GPIO_OUT 0x13c
// Note that the offset addresses of the various registers are in bytes,
// not in registers. unsigned long is 4 bytes for Beaglebone Black
#define REG_PER_ULONG 4

namespace GPIO {

enum PIN {
  LOW = 0,
  HIGH = 1
};


class GPIOConst {
public:
    GPIOConst();
    virtual ~GPIOConst();

    volatile unsigned long * getGpioAddress(); // only GPIO1_BASE for now.

    int isPinAllocatedByDefault(const unsigned int pin);

private:
    void setupGpioAddress();
    int memFile;
    char *gpio_map;
    volatile unsigned long *gpioAddress;
};

} /* namespace GPIO */
#endif

GPIO/GPIOConst.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include "./GPIOConst.h"
#include <stdio.h>
#include <string.h>

#include <unistd.h> //open,close
#include <fcntl.h> //various defines with file manipulation
#include <sys/stat.h>
#include <sys/mman.h>

#include <stdlib.h>     /* system, NULL, EXIT_FAILURE */


namespace GPIO {

GPIOConst::GPIOConst()
{
    this->setupGpioAddress();
}

void GPIOConst::setupGpioAddress()
{
    /* open /dev/mem */
    if ((memFile = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
            printf("can't open /dev/mem \n");
            exit (-1);
    }

    /* mmap GPIO */
    gpio_map = (char *)mmap(
            0,
            GPIO_SIZE,
            PROT_READ|PROT_WRITE,
            MAP_SHARED,
            memFile,
            GPIO1_BASE
    );

    if (gpio_map == MAP_FAILED) {
            printf("mmap error %p\n", (char*)gpio_map);
            exit (-1);
    }

    // Always use the volatile pointer!
    gpioAddress = (volatile unsigned long*)gpio_map;
}

volatile unsigned long* GPIOConst::getGpioAddress()
{
    return gpioAddress;
}

GPIOConst::~GPIOConst()
{
    close(memFile);
}

}

GPIO/GpioInput.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef GPIO_GPIOINPUT_H
#define GPIO_GPIOINPUT_H

#include "./GPIOConst.h"

namespace GPIO {

class GpioInput {
public:
    GpioInput(const unsigned int gpio, GPIOConst* constants);
    virtual ~GpioInput();
    void waitUntil(const bool targetIsHigh);

private:
    void setDirection();
    void mapGpioAdress(GPIOConst* constants);
    bool isHigh() const;

    const unsigned int gpio;
    volatile unsigned long *gpioAddress;
};

} /* namespace GPIO */
#endif

GPIO/GpioInput.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include "./GpioInput.h"

namespace GPIO {

GpioInput::GpioInput(const unsigned int gpio, GPIOConst* constants)
    : gpio(gpio)
{
    this->mapGpioAdress(constants);
    this->setDirection();
}


void GpioInput::mapGpioAdress(GPIOConst* constants)
{
    gpioAddress = constants->getGpioAddress();
}


void GpioInput::setDirection()
{
    // 1 at gpio's adress means input;
    gpioAddress[GPIO_OE/REG_PER_ULONG] |= (1<<gpio);
}


bool GpioInput::isHigh() const
{
    return ( gpioAddress[GPIO_IN/REG_PER_ULONG] & (1<<gpio) ) > 0;
}


GpioInput::~GpioInput()
{
}


void GpioInput::waitUntil(const bool targetIsHigh)
{
    while (isHigh() != targetIsHigh) continue;
}

}

GPIO/GpioOutput.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#ifndef GPIO_GPIOOUTPUT_H
#define GPIO_GPIOOUTPUT_H

#include "./GPIOConst.h"

#include <stdint.h> //uint32_t

namespace GPIO {

class GpioOutput {
public:
    GpioOutput(const unsigned int gpio, GPIOConst* constants);
    virtual ~GpioOutput();

    void setValue(const uint32_t value);

private:
    void mapGpioAddress(GPIOConst* constants);
    void setDirection();

    const unsigned int gpio;
    volatile unsigned long *gpioAddress;
};

} /* namespace GPIO */
#endif

GPIO/GpioOutput.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include "./GpioOutput.h"

#include <sys/mman.h>
#include <string.h>
#include <unistd.h> //open,close
#include <fcntl.h> //various defines with file manipulation


namespace GPIO {

GpioOutput::GpioOutput(const unsigned int gpio, GPIOConst* constants)
    : gpio(gpio)
{
    this->mapGpioAddress(constants);
    this->setDirection();
}


void GpioOutput::mapGpioAddress(GPIOConst* constants)
{
    gpioAddress = constants->getGpioAddress();
}


void GpioOutput::setDirection()
{
    // 0 at gpio's adress means output
    gpioAddress[GPIO_OE/REG_PER_ULONG] &= ~(1<<gpio);;
}


GpioOutput::~GpioOutput()
{
}

void GpioOutput::setValue(const uint32_t value)
{
    if (value == 0)
    {
        gpioAddress[GPIO_OUT/REG_PER_ULONG] &= ~(1<<gpio);
    }
    else
    {
        gpioAddress[GPIO_OUT/REG_PER_ULONG] |= (1<<gpio);
    }
}

}

snesgpio.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <stdio.h>
#include<iostream>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#include "GPIO/GpioInput.h"
#include "GPIO/GpioOutput.h"
#include "GPIO/GPIOConst.h"
#define SHMSZ     27

#define MASK      0x00000001

int main(int argc, char** argv)
{
    const bool highEdge=true;
    const bool lowEdge=false;

    // Get the segment created by the server
    key_t sharedMemoryKey = 5678;

    // Locate the segment
    int sharedMemoryId;
    if ((sharedMemoryId = shmget(sharedMemoryKey, SHMSZ, 0666)) < 0) {
        perror("shmget");
        exit(1);
    }

    // Attach the segment to data space
    char *sharedMemory;
    if ((sharedMemory = (char *) shmat(sharedMemoryId, NULL, 0)) == (char *) -1) {
        perror("shmat");
        exit(1);
    }

    // Configure access to the shared value
    uint32_t *sharedData;
    sharedData = (uint32_t*) sharedMemory;
    uint32_t writevalue;

    // Obtain input and output GPIOs
    using namespace GPIO;
    GPIOConst constants;

    unsigned int clockPin = 18; //blue = data clock = P9 14 gpio1[18]
    unsigned int latchPin = 16; //yellow = data latch = P9 15 gpio1[16]
    unsigned int dataPin = 19; //red = serial data = P9 16 gpio1[19]


    GpioInput clock(clockPin, &constants);
    GpioInput latch(latchPin, &constants);
    GpioOutput data(dataPin, &constants);

    while(true)
    {
        // set output in waiting position until next latch up
        data.setValue(PIN::LOW); // data norm low

        //Latch up --> data high
        latch.waitUntil(highEdge);
        data.setValue(PIN::HIGH);

        //register buttons pressed
        writevalue = (uint32_t) *sharedData;
        if(writevalue == 4294964211)  //L+R+Start+Select == Quit
            break;

        //latch down --> Data first button
        latch.waitUntil(lowEdge);
        data.setValue(writevalue & MASK);

        //Clock 13-16 --> data high (Ensured by joystick implementation
        for (int i = 0; i < 16; i++)
        {
            writevalue >>= 1; // Prepare next value by shifting bits so it's easier to apply mask

            //clock down --> Data is fetched by snes
            clock.waitUntil(lowEdge);
            //clock up --> Data next button           
            clock.waitUntil(highEdge);
            data.setValue(writevalue & MASK);
        }
    }

}