Pi IoT In C Using Linux Drivers - GPIO Character Driver
Written by Harry Fairhead   
Monday, 29 March 2021
Article Index
Pi IoT In C Using Linux Drivers - GPIO Character Driver

As an example of using the contextless functions, simply toggle GPIO4 as fast as possible:

#define _GNU_SOURCE
#include <gpiod.h>
#include <stdio.h> 
#include <unistd.h>
#include <stdlib.h>
int res;
int main(int argc, char **argv) {
    for (;;) {
      res = gpiod_ctxless_set_value("0", 4, 1, 1, 
                          "output test", NULL, NULL);        

You might well conclude that this program is wrong as it sets line 4 to 1 and never sets it to 0. However, if you run the program you will discover that you do see pulses because the line is closed and hence it returns to 0 between each of the calls to the set function.


The trace is for a Pi Zero and you can see that the pulses are 30µs wide with a gap of around 290µs. For a Pi 4 pulses are 10µs wide with a gap of 74µs. The gap is the time needed to close the line file and re-open it. This is slower than using the character driver driver directly and the pulse width isn’t under our control. The line is set high for the time it takes to complete the call.

To set the pulse width you have to use a callback function to delay the closing of the file:

#include <gpiod.h>
#include <stdio.h> 
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
int res;
int delayms(int ms) {
    struct timespec delay = {0, ms * 1000*1000};
    return nanosleep(&delay, NULL);
int main(int argc, char **argv) {
  for (;;) {
   res = gpiod_ctxless_set_value("0", 4, 1, 1, 
"output test", (gpiod_ctxless_set_value_cb) delayms,
(void *) 100); res = gpiod_ctxless_set_value("0", 4, 0, 1,
"output test", (gpiod_ctxless_set_value_cb) delayms,
(void *) 100); } }

Notice that the final parameter isn’t actually being used as a pointer, it is just a way of passing an untyped value and the parameter isn’t dereferenced in the callback. This version of the program is more useful in that it does produce pulses of around 100ms. Also notice the wrapper for nanosleep because we can only pass a single parameter to the callback function. The first set makes the line go high and it stays high for the duration of the nanosleep and then the close operation resets it to input. The second set makes the line go low for the duration of the nanosleep and it stays low while the line is closed again. What this all means is that the time that the line is low is still longer than the nanosleep time because it includes the time to close and open the file.

You can see that the high time is around 100ms, but the low time is a little longer. You can either adjust the low timings or simply put up with it.

As well as single line get and set functions, there are also multiple line versions:

values[], num_lines,active_low, “consumer”,callback,param) gpiod_ctxless_get_value_multiple(“device”,offsets[],
values[], num_lines,active_low, “consumer”,callback,param)

These work in the same way, but now you can specify a set of lines as an array of line structs and an array of values to get/set them to. Of course you have to specify the number of lines and the arrays have to be the correct size.

For example, to pulse two lines out of phase you could use:

#include <gpiod.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
int res;
int delayms(int ms)
    struct timespec delay = {0, ms * 1000 * 1000};
    return nanosleep(&delay, NULL);
int main(int argc, char **argv)
    int offsets[] = {4, 27};
    int state1[] = {1, 0};
    int state2[] = {0, 1};
    for (;;) {
    gpiod_ctxless_set_value_multiple("0", offsets, 
state1, 2, 1, "output test", (gpiod_ctxless_set_value_cb)delayms,
(void *)1); gpiod_ctxless_set_value_multiple("0", offsets,
state2, 2, 1, "output test", (gpiod_ctxless_set_value_cb)delayms,
(void *)2); } }

This pulses lines 4 and 27 so that one is high while the other is low. Even though the function promises to change the lines at the same moment, there is a 10µs delay between changing the state of lines, as can be seen in the logic analyzer display below:

If you are working in milliseconds, a delay of 10µs probably isn’t important, but it is still necessary to know that the lines do not all change together and there are situations in which this matters.

The context-less functions are rarely helpful and they are only slightly easier to use than the much more powerful context-using functions which we look at next.

In book but not in this extract.

  • Context Functions
  • GPIO Chip
  • Getting Lines
  • Using Lines
  • Working With More Than One Line
  • Using GPIO Lines – Measuring R and C



  • The GPIO character driver replaces the sysfs GPIO driver and it is the one to use for all future projects.

  • The GPIOD library provides a higher level way of using the GPIO character driver, but if you don’t want the overhead of using it then the direct ioctl interface is easy to use.

  • After the library has been installed there are a number of utilities that are sometimes useful at the command line or in scripts.

  • The library has two types of function, context-less and context-using. The context-less functions can be used in an ad-hoc fashion, but the context-using functions have to be used in an organized manner.

  • The context-less functions set GPIO lines to input or output without opening them or reserving them in anyway. This makes them slow and you need to use callback functions to control their output.

  • The context-using functions do require you to open the GPIO line and configure it before you use it. Once used you have to close the line to allow another process to make use of it.

  • Context-using functions are not difficult to use and are much more flexible and fast.

  • You can work with multiple GPIO lines in a single function call. However, there are still delays between setting lines to particular values.

  • The context-using functions can create pulses as fast as 1µs to 2.0µs.

  • Using the time-to-charge you can measure either the resistance or the capacitance of a circuit using a single GPIO line.


Raspberry Pi IoT In C Using Linux Drivers

By Harry Fairhead


Buy from Amazon.


  1.  Choosing A Pi For IoT

  2. C and Visual Studio Code

  3.  Drivers: A First Program

  4.  The GPIO Character Driver
         Extract: GPIO Character Driver

  5. GPIO Using I/O Control

  6.  GPIO Events

  7.  The Device Tree
        Extract: The DHT22

  8.  Some Electronics

  9.  Pulse Width Modulation
    Extract:  The PWM Driver 

  10. SPI Devices
    Extract: The SPI Driver 

  11. I2C Basics

  12. The I2C Linux Driver ***NEW!


  13. Advanced I2C

  14. Sensor Drivers – Linux IIO & Hwmon
      Extract: Hwmon  

  15. 1-Wire Bus
      Extract: 1-Wire And The DS18B20 

  16. Going Further With Drivers

  17. Appendix I



To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.


FORTRAN and COBOL Re-enter TIOBE Index

The TIOBE Index sets out to reflect the relative popularity of  computer languages so it comes as something of a surprise to see two languages dating from the 1950's in this month's Top 20.

Linus Torvalds Over Flows On Overflows In C

You may think of Linus Torvalds as the Linux guru, but he is also a leading expert on C and often ignored and misunderstood in this role. A recent exchange on the Linux Kernel mailing list demonstrate [ ... ]

More News

raspberry pi books



or email your comment to: comments@i-programmer.info





Last Updated ( Saturday, 03 April 2021 )