General Purpose IO Driver

Serge Vakulenko edited this page Apr 4, 2015 · 5 revisions

General purpose I/O signals

Gpio driver provides a service for user applications to access general purpose I/O signals of the chip.

Only programms with root privileges are allowed to open gpio devices.

Kernel configuration

To enable gpio driver in the kernel, add the following lines to your kernel configuration file:

# General purpose I/O ports for 144-pin chip
device      gpio0   flags 0xc6ff    # port A
device      gpio1   flags 0xffff    # port B
device      gpio2   flags 0xf01e    # port C
device      gpio3   flags 0xfeff    # port D
device      gpio4   flags 0x03ff    # port E
device      gpio5   flags 0x313f    # port F
device      gpio6   flags 0xf3c3    # port G
device      gpio7   flags 0xffff    # port H
device      gpio8   flags 0xffff    # port J
device      gpio9   flags 0x00ff    # port K

Every gpioN device corresponds to one 16-bit i/o port. Flags specify a bitmask of available signals. The mask depends on which signals are supported in hardware, and which of them you want to allow for the user applications. For chips with lower pin count, fewer signals are available, and ports H, J and K not present. It makes sense as well to disable signals allocated for UARTs, SPI ports, SD card etc.

When kernel starts, it prints the configuration of i/o ports. For example, for chipKIT WiFire board:

gpio0 at portA, pins -i---ii-iiiiiiii
gpio1 at portB, pins i---i-iiiiiiiiii
gpio2 at portC, pins iiii---------i--
gpio3 at portD, pins --ii------iiii-i
gpio4 at portE, pins ------iiiiiiiiii
gpio5 at portF, pins --ii--------i---
gpio6 at portG, pins iiii--iii-------

GPIO devices

For every i/o port, two devices are created, /dev/confX and /dev/portX:

# ls -l /dev/conf? /dev/port?
crw-rw-rw-  1 root  operator   18,  64 Apr  3 18:48 /dev/confa
crw-rw-rw-  1 root  operator   18,  65 Apr  3 18:48 /dev/confb
crw-rw-rw-  1 root  operator   18,  66 Apr  3 18:48 /dev/confc
crw-rw-rw-  1 root  operator   18,  67 Apr  3 18:48 /dev/confd
crw-rw-rw-  1 root  operator   18,  68 Apr  3 18:48 /dev/confe
crw-rw-rw-  1 root  operator   18,  69 Apr  3 18:48 /dev/conff
crw-rw-rw-  1 root  operator   18,  70 Apr  3 18:48 /dev/confg
crw-rw-rw-  1 root  operator   18,  71 Apr  3 18:48 /dev/confh
crw-rw-rw-  1 root  operator   18,  72 Apr  3 18:48 /dev/confj
crw-rw-rw-  1 root  operator   18,  73 Apr  3 18:48 /dev/confk
crw-rw-rw-  1 root  operator   18,   0 Apr  3 18:48 /dev/porta
crw-rw-rw-  1 root  operator   18,   1 Apr  3 18:48 /dev/portb
crw-rw-rw-  1 root  operator   18,   2 Apr  3 18:48 /dev/portc
crw-rw-rw-  1 root  operator   18,   3 Apr  3 18:48 /dev/portd
crw-rw-rw-  1 root  operator   18,   4 Apr  3 18:48 /dev/porte
crw-rw-rw-  1 root  operator   18,   5 Apr  3 18:48 /dev/portf
crw-rw-rw-  1 root  operator   18,   6 Apr  3 18:48 /dev/portg
crw-rw-rw-  1 root  operator   18,   7 Apr  3 18:48 /dev/porth
crw-rw-rw-  1 root  operator   18,   8 Apr  3 18:48 /dev/portj
crw-rw-rw-  1 root  operator   18,   9 Apr  3 18:48 /dev/portk

I/O configuration device

Device /dev/confX defines the direction of the signals (input or output) and optional open-drain mode for outputs. You can display the current configuration by reading the devices, for example:

# cat /dev/conf?
-i---ii-iiiiiiii
o---o-oiooioooii
iiii---------i--
--io------iooo-i
------iiiiiiiiii
--ii--------i---
ooio--oio-------

Here you can see that some signals are configured as inputs "i", and some as outputs "o". Open drain signals would be marked as "d".

You can change the configuration by writing appropriate string to the device. For example, to configure pins PA0 and PA1 as input, PA2 as output and PA3 as open-drain output:

# echo .............doii > /dev/confa

Write to /dev/confX:

  • 'i' - configure the corresponding port pin as an input;
  • 'o' - configure the corresponding port pin as an output;
  • 'd' - configure the corresponding port pin as an open-drain output;
  • '.' - no action.

I/O port device

Device /dev/portX specifies the values of input or output signals. You can read the devices to view the current status af all pins, for example:

# cat /dev/port?
-1---10-11001101
0---0-0000000000
1000---------0--
--11------1011-1
------0000001111
--00--------0---
0111--000-------

To modify the output value of some signals, write the needed string to the device. For example, to set pins A2 and A3 to high level:

# echo .............11.. > /dev/porta

Write to /dev/portX:

  • '0' - set output pin low;
  • '1' - set output pin high;
  • '+' - invert the value of output pin;
  • '.' - no action.

/usr/bin/portio utility

Command portio is a simple command line utility for more sophisticated control of the i/o ports.

The arguments consist of a list of actions. Option -v enables verbose mode:

    portio [-v] action...
Action Description
-i port... configure ports as input
-o port... configure ports as output
-d port... configure ports as open-drain output
-a port number assign port value
-s port... set ports to 1
-c port... clear ports (set to 0)
-r port... reverse ports (invert)
-g port... get port values
-m number delay in milliseconds

Parameter "port" is a single pin name, or list of pin names, or a port name:

Port Description
a0 single pin
b3-7,11 list of pins
c same as c0-15

Here is an example of multiple actions per single portio invocation:

# portio -o a1-2 c15 -i b0-3 d -s a1 c15 -c a2 -g b d

This call performs the following actions:

  • Configure pins RA1, RA2 and RC15 as outputs
  • Configure pins RB0-RB3 and RD0-RD15 as inputs
  • Set pins RA1 and RC15 high
  • Set pin RA2 low
  • Get values of ports B and D (pins RB0-RB15 and RD0-RD15)

Option -m inserts a delay by given number of milliseconds.

Programming interface

From user application, the I/O pins can be controlled via ioctl() system call. You can use any of /dev/portX or /dev/confX devices, it ir irrelevant which one to open.

The generic interface is:

    #include <fcntl.h>
    #include <machine/gpio.h>
    ...
    int fd = open("/dev/porta", O_RDWR);

    ioctl(fd, port_ident | operation, pinmask);

Port identifier is:

  • GPIO_PORTA or GPIO_PORT(0) for port A;
  • GPIO_PORTB or GPIO_PORT(1) for port B;
  • GPIO_PORTC or GPIO_PORT(2) for port C;
  • GPIO_PORTD or GPIO_PORT(3) for port D;
  • GPIO_PORTE or GPIO_PORT(4) for port E;
  • GPIO_PORTF or GPIO_PORT(5) for port F;
  • GPIO_PORTG or GPIO_PORT(6) for port G;
  • GPIO_PORTH or GPIO_PORT(7) for port H;
  • GPIO_PORTJ or GPIO_PORT(8) for port J;
  • GPIO_PORTK or GPIO_PORT(9) for port K.

Operation is one or multiple of:

Operation Description
GPIO_CONFIN configure pins as input
GPIO_CONFOUT configure pins as output
GPIO_CONFOD configure pins as open drain output
GPIO_STORE set all output pins to given value
GPIO_SET set output pins to 1
GPIO_CLEAR set output pins to 0
GPIO_INVERT invert output pins by mask
GPIO_POLL get input values (pinmask ignored)

Pins are specified as bitmask: bit 0 corresponds to pin 0, bit 1 - pin 1 etc.

A few examples:

    // Configure pins RA1, RA2 and RC15 as outputs
    ioctl(fd, GPIO_PORTA | GPIO_CONFOUT, 1<<1 | 1<<2);
    ioctl(fd, GPIO_PORTC | GPIO_CONFOUT, 1<<15);

    // Configure pins RB0-RB3 and RD0-RD15 as inputs
    ioctl(fd, GPIO_PORTB | GPIO_CONFIN, 0x000f);
    ioctl(fd, GPIO_PORTD | GPIO_CONFIN, 0xffff);

    // Set pins RA1 and RC15 high
    ioctl(fd, GPIO_PORTA | GPIO_SET, 1<<1);
    ioctl(fd, GPIO_PORTC | GPIO_SET, 1<<15);

    // Set pin RA2 low
    ioctl(fd, GPIO_PORTA | GPIO_CLEAR, 1<<2);

    // Get values of ports B and D
    portb = ioctl(fd, GPIO_PORTB | GPIO_POLL, 0);
    portd = ioctl(fd, GPIO_PORTD | GPIO_POLL, 0);

Several operations for the same port can be combined in a single call. For example, to toggle pin A2 high then low, and get value of all PORTA pins:

val = ioctl(fd, GPIO_PORTA | GPIO_SET | GPIO_CLEAR | GPIO_POLL, 1 << 3);