Page 1 of 4 For maximum speed you need to work directly with the GPIO hardware - find out how. This is an extract from the newly-published Raspberry Pi 5 IoT In C.
Raspberry Pi 5 IoT In C Drivers and Gpio5
By Harry Fairhead
 Buy from Amazon.
Contents
- The Pi 5 For The IoT
- C and Visual Studio Code
- Drivers: A First Program
- The GPIO Character Drive
- GPIO Using I/O Control
- GPIO Events
- GPIO Hardware With Gpio5
Extract: GPIO Registers ***NEW!
- Some Electronics
- The Device Tree
- Pulse Width Modulation
- SPI Devices
- I2C Driver and Gpio5
- Sensor Drivers – Linux IIO & hwmon
- 1-Wire Bus
- The PIO 313
- Going Further With Drivers
- Appendix I Gpio5
<ASIN:1871962943>
GPIO Hardware With Gpio5
The Pi 5 uses a custom RP1 chip to provide the connection to all of the peripherals. This is very different from the bcm2835 which is used in the earlier versions of the Pi. The RP1 may be new, but it is essentially a modified RP2040, the chip that powers the Pico. What this means is that the Pico SDK contains software that, with some modification, can be used with the Pi 5’s peripherals. In this chapter, we look at how to get started with the GPIO hardware, which is very different from the implemented simple concept of on/off GPIO lines and its complexity can be something of a shock. Then we use this knowledge to implement Gpio5, a library of GPIO functions implemented for the purpose of this book that is modeled on the Pico SDK. With its help you can write fast GPIO programs and even port Pico GPIO programs with little change. This is an open-source project that you can fork and modify and can be found at Github https://github.com/IOPress/Gpio5. A full listing can also be found on the book’s web page at www.iopress.info.
Accessing Memory
The Pi 5 has a huge peripheral area starting at 0x40000000. This is the physical address which is translated to 0x1f00000000 in the 40-bit address space. The usual way of accessing memory under Linux is a strange roundabout way of sticking with the “everything is a file” idea. There are drivers that make memory look like a file and you can, in principle, read and write memory locations by reading and writing the file. In practice the files are provided, but you generally cannot use them by reading and writing to them.
What you have to do instead is use another Linux feature, memory mapping, to make the file accessible in memory. This allows you to take any file, load it into memory and then access it directly without needing to read and write the actual physical file. This is implemented mainly for efficiency. If you have a file that needs to be worked with as fast as possible you can memory-map it and work with it at the speed of main memory. Putting this together with the fact that we have files that represent memory we can access areas of memory directly by first opening a memory file and then mapping it into an address space so we can access it.
This seems like a silly idea at first – take memory, convert it into a file and then take that file and convert it back into memory – but it has security advantages and you can map the memory to a new address range.
The Pi 5 has a memory file called mem in the dev directory. The /dev/mem file can be used in the usual way, but you have to be running with root privileges. The /dev/gpiomem0 file only maps the GPIO area starting at 0x400d0000, or 0x1f000d0000, but it doesn’t need root privileges. To use the GPIO file all you need is to be a member of the gpio user group. In the examples that follow, the mem file is used with root privileges because it works with peripherals other than the GPIO lines. If you want to use the gpiomem0 file you will be restricted to only accessing the GPIO lines. Gpio5 tries to access mem and if this fails falls back to gpiomem0.
To access the GPIO area of memory you can map the entire peripherals area into user space using:
int memfd = open("/dev/mem", O_RDWR | O_SYNC);
uint32_t *map = (uint32_t *)mmap(
NULL,
64 * 1024* 1024,
(PROT_READ | PROT_WRITE),
MAP_SHARED,
memfd,
0x1f00000000
);
if (map == MAP_FAILED)
{
printf("mmap failed: %s\n", strerror(errno));
return (-1);
};
close(memfd);
This maps the entire 64MByte area. If you only want to use the GPIO lines then you can make this smaller, but there is no problem accommodating it in the 4,096MByte, 32-bit address space. Also notice that we only load the entire memory starting from 0x1f00000000, i.e. the start of the peripheral area. This means we can use the offsets from the start of the peripheral area as offsets from the return value of map.
For example, the GPIO control registers have an address of 0x400d0000, which gives an offset of 0xd0000 from the start of the peripherals area. As the pointers are to uint32_t we have to remember to divide by 4 so that the correct offset is added. That is, the GPIO registers are located at:
uint32_t *PERIBase = map;
uint32_t *GPIOBase = PERIBase + 0xd0000 / 4;
You can check that GPIOBase references a location in user space 0xd000 higher than PERIBase.
|