GPIO Sysfs Interface
The GPIO sysfs interface has been deprecated since 2016 and was expected to have been removed from the kernel by now. It should not be used for new projects, but it has been so extensively used in the past that you might need some knowledge of how it works for some time to come. This appendix is a brief overview.
Note: Only GPIO sysfs was has been deprecated, the rest of sysfs is perfectly usable.
Working With Sysfs
Sysfs is a virtual file system that provides all sorts of access to hardware and the operation of the Linux kernel. You can spend a lot of time exploring sysfs, but the part we are interested in here is the gpio folder. Sysfs is usually mounted in the sys folder and the folder that corresponds to the GPIO device is usually:
/sys/class/gpio
To see what is in the folder, simply list it:
ls /sys/class/gpio
The list includes the GPIO lines that are already in use by some process or other. Notice that the gpio numbers are not external pin numbers, but internal gpio numbers. So, for example, to use the GPIO line that is on physical pin 7 on the connector you need to refer to GPIO4.
The steps in using a line are always the same:
- Reserve or "export" the GPIO line so that no other process can use it
- Set its direction and read or write it
- Unreserve it or unexport it
You can do these steps from any language that supports file operations, including the shell.
You might ask why you have to "export" or reserve a GPIO line rather than just use it? The answer is that the export operation will only work if the OS, or some other process, hasn't claimed the GPIO line for its own use. You can think of the export/unexport process as making sure that you don't misuse GPIO lines and that you don't share them with other processes.
To reserve a GPIO line you have to write its number to the export folder and you can do this using the shell command. For example, assuming we want to work with GPIO 4:
echo 4 > /sys/class/gpio/export
You can of course change 4 to any valid gpio number.
You can do the same job in C:
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv) {
int gpio = 4;
FILE* fd = fopen("/sys/class/gpio/export", "w");
fprintf(fd, "%d", gpio);
fclose(fd);
return 0;
}
The fopen function opens export for write, the fprintf string prints the number of the gpio line and then the file is closed with fclose. If you are not familiar with C file operations, see Fundamental C: Getting Closer To The Machine, ISBN:9781871962604 or, at a more advanced level, Applying C For The IoT With Linux, ISBN:9781871962611.
Once you have the pin reserved, you will see a gpio4 folder corresponding to it in /sys/class/gpio. Now you can set its direction and read or write it. To do this you have to read from or write to the appropriate subfolder of the gpio folder just created.
If you list all of the folders in gpio4 you will see:
Each of these folders controls some aspect of the GPIO line’s functioning. The most important are direction, in which the line can be set to in or out, and value, which can be set to 0 or 1 for output and 0 or 1 for input. There is also active_low, which determines which way the logic operates and whether the line low corresponds to a 1 or a 0.
For example, to read GPIO 4 from the command line use:
echo "in" > /sys/class/gpio/gpio4/direction
cat /sys/class/gpio/gpio4/value
and to set it as output and to go high and then low use:
echo "out" > /sys/class/gpio/gpio4/direction
echo 1 > /sys/class/gpio/gpio4/value
echo 0 > /sys/class/gpio/gpio4/value
You can do the same using C, but it is slightly more verbose due to the need to open and close files and build the appropriate strings.
Toggling A Line
As an example, consider the following C program which sets GPIO 4 to output and then toggles it high and low as fast as possible:
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv) {
int gpio = 4;
char buf[100];
FILE* fd = fopen("/sys/class/gpio/export", "w");
fprintf(fd, "%d", gpio);
fclose(fd);
sprintf(buf, "/sys/class/gpio/gpio%d/direction", gpio);
fd = fopen(buf, "w"); fprintf(fd, "out");
fclose(fd);
sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio);
fd = fopen(buf, "w");
for (;;) {
fd = fopen(buf, "w");
fprintf(fd, "1"); fclose(fd);
fd = fopen(buf, "w");
fprintf(fd, "0");
fclose(fd);
}
return 0;
}
The program first exports gpio4 and then writes out to its direction folder to set the line to output. After this the value file is open for writing and 1 and 0 are written to the file repeatedly. Notice the use of sprintf to create strings which incorporate the number of the gpio line you are using so that you can open the correct folder.
You might be puzzled by the loop that opens the file, writes a value and then closes it. Why not just keep the file open? The reason is that the file buffer isn't flushed unless the file is closed. This is the usual way of dealing with the problem, but it is not very fast and it is part of the reason that sysfs has a bad reputation.
If you try the program you will discover that the pulse train has a frequency of around 4kHz and a pulse width of 100µs on a Pi Zero and 18Khz and 28µs respectively on a Pi 4.
We can, however, do much better by not closing the file every time we write to it. Instead we can use fflush to flush the file buffer by changing the for loop to:
for (;;) {
fprintf(fd, "1");
fflush(fd);
fprintf(fd, "0");
fflush(fd);
}
The difference is quite amazing. Now, on a Pi Zero, the frequency is 130Khz and the pulse width is 3.6µs and, on a Pi 4, 450Khz and 1.1µs. This is still about 10 times slower than using the library, but useful for some applications.
Sysfs GPIO may be obsolete, but it was simple and logical.