Pi IoT In Python Using Linux Drivers - GPIO Using Ioct
Written by Mike James & Harry Fairhead   
Monday, 29 July 2024
Article Index
Pi IoT In Python Using Linux Drivers - GPIO Using Ioct
The struct module
GPIO Output
The complete program

The struct module can help you create a buffer of the right length and initialize it with appropriate data, for details see Programmer’s Python: Everything is Data, ISBN:9781871962595. There are many ways to use the struct module but the most efficient is to create a struct object:

mystruct = struct(format)

where format is a string that defines the type of each part of the buffer and, implicitly, its length. The Python documentation gives a list of allowable format characters but the most useful are:

Symbol

C Type

Python Type

Size

c b

char or signed char

byte

1 byte

B

unsigned char

byte

1 byte

ns

char[n]

byte sequence

n bytes

h H

short unsigned short

numeric

2 bytes

i l

int or long int

numeric

4 bytes

I L

unsigned int or unsigned long

numeric

4 bytes

q

long long

numeric

8 bytes

Q

unsigned long long

numeric

8 bytes

Notice that int/long int and unsigned int are the same type on most systems, but if in doubt use long and unsigned long to make sure you get 4 bytes of storage.

The format you use sets the size of the buffer and the way Python data is loaded into it and unloaded from it. For example, the struct given earlier can be implemented as:

gpiochip_info= struct.Struct("32s 32s L")

This creates a Struct object with a 68-byte buffer with the first 32 and second 32 bytes regarded as simple character data and the last 4 bytes as a 32-bit unsigned integer.

Once you have the Struct object you can transfer data into the buffer using its pack method. For example:

buffer = gpiochip_info.pack(b"Hello",b"World",42)

returns a buffer with the character codes for Hello packed into the first five bytes, those for World packed into the five bytes starting at byte 32 and the final four bytes set to the binary representation of 42. Notice that the character data has to be specified as a byte data by a leading b and is automatically packed out with zeros to create 32-byte, C-style, null-terminated strings.

To get data out of the Struct object you use the unpack method, for example:

first,second,third=gpiochip_info.unpack(buffer)

If you try printing out the strings, you will discover that they are in fact byte data:

print(first)

produces:

b'Hello\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
x00\x00\x00\x00\x00'

i.e. a null-terminated byte character sequence.

You can remove the nulls and convert to a standard Python string in a number of ways. For example:

print(first.rstrip(b'\0').decode("utf-8"))

produces:

Hello 

Using gpiod With ioctl

The gpiod character device works entirely in terms of the ioctl system call, not by reading or writing the files as was the case with sysfs which it replaces. What you need to know to use the driver directly are the request codes and the structures that are passed.

These are not defined for Python in the documentation, but the C linux/gpio.h header contains all of the information necessary. The problem is that you have to read the C to find out what the request constants and structs used actually are.

Working with the GPIO chip and the GPIO lines follows the same standard steps:

  1. Find the request constant you need to use

  2. Find the struct that is used with that constant and convert it to a Python struct object

  3. Open the appropriate file

  4. Make an ioctl call using the constant and the struct

  5. Process the resulting bytes.

 

Getting Chip Info

As a first simple example, let’s get some information about the GPIO chip.

This involves using Python to make an ioctl call that is only documented as a C header file – linux/gpio.h.

If you look through the header file you will find the request constant defined as:

GPIO_GET_CHIPINFO_IOCTL=0x8044B401

You will also find the definition of a struct which is used to return the information about the GPIO chip:

struct gpiochip_info {
	char name[32];
	char label[32];
	__u32 lines;
};

This is the struct used in the previous examples. Even if you don’t know C, you can probably work out that the struct consists of a name of 32 characters, a label of 32 characters and an unsigned 32-bit integer. This can be implemented using:

gpiochip_info= struct.Struct("32s 32s L")

With this information we can open the file and make the ioctl call:

import fcntl, struct,io
GPIO_GET_CHIPINFO_IOCTL=0x8044B401
f = io.open("/dev/gpiochip0", "rb",buffering=0)
gpiochip_info= struct.Struct("32s 32s L")
buffer=gpiochip_info.pack(b' ',b' ',0)
result=fcntl.ioctl(f, GPIO_GET_CHIPINFO_IOCTL,buffer)
name,label,lines=gpiochip_info.unpack(result)
print(name.rstrip(b'\0').decode("utf-8"))
print(label.rstrip(b'\0').decode("utf-8"))
print(lines)

As with all good, easy-to-understand, examples, error handling has been omitted. If you run this program on any Pi but a Pi 5 you will see:

pinctrl-bcm2835
gpiochip0
54

To run it on a Pi 5 change gpiochip0 to gpiochip4 and you will see:

gpiochip4
pinctrl-rp1
54

DriverPython2e360

 


Last Updated ( Tuesday, 30 July 2024 )