GPIO
1. GPIO Subsystem
GPIO (General-Purpose Input/Output) is a versatile pin that can be controlled by a microcontroller (MCU) or CPU. It serves various functions, such as detecting high and low signal levels and controlling output. In Linux, GPIO pins can be exported to user space and controlled via the sysfs filesystem. This allows GPIO pins to be used for multiple purposes, including serial communication, I2C, network communication, and voltage detection.
Linux provides a dedicated GPIO subsystem driver framework to handle GPIO devices. Through this framework, users can easily interact with the CPU’s GPIO pins. The driver framework supports basic input and output functionality, including interrupt detection for inputs. This flexibility makes it easy for developers to repurpose GPIO pins for various tasks. For more detailed information on the GPIO subsystem, refer to <Linux kernel source>/Documentation/gpio.
2.GPIO Control (Shell)
2.1 Pin Layout
To export GPIO pins to user space, the pin number is often required, which can be identified from the interface diagram. Detailed pin functions can be found in the "Data Download" section, including the 40-pin layout or Datasheet manual.Luckfox Omni3576 Pin Diagram:

2.2 GPIO Number Calculation
GPIO pin numbers are labeled in the pin diagram, which can be directly used or calculated using the method below.
-
GPIO has 5 banks: GPIO0
GPIO4, each bank is divided into 4 groups, with a total of 32 pins: A0A7, B0B7, C0C7, D0~D7 -
GPIO Naming Convention: GPIO pins are named as
GPIO{bank}_{group}{X}, such as:GPIO0_A0 ~ A7GPIO0_B0 ~ B7GPIO0_C0 ~ C7GPIO0_D0 ~ D7GPIO1_A0 ~ A7....GPIO1_D0 ~ D7....GPIO4_D0 ~ D7 -
GPIO Number Calculation Formula:
pin = bank * 32 + numbernumber = group * 8 + Xpin = bank * 32 + (group * 8 + X)For example, calculating the pin number for GPIO2_D7_d:
bank:2group:3 (A=0, B=1, C=2, D=3)X:7
Pin number: 2 x 32 + ( 3x 8 + 7) = 95
2.3 Controlling GPIO via Sysfs
-
Export GPIO to User Space:
sudo suecho 95 > /sys/class/gpio/export -
Unexport GPIO from User Space:
echo 95 > /sys/class/gpio/unexport
2.3.1 Device Directory and Attributes
-
When you write a GPIO number to
/sys/class/gpio/export, the kernel exports this GPIO to user space, making it accessible through the/sys/class/gpio/gpio<N>directory for subsequent operations (e.g., setting direction, setting or reading level).root@luckfox:/home/luckfox# ls /sys/class/gpioexport gpio95 gpiochip0 gpiochip128 gpiochip32 gpiochip509 gpiochip64 gpiochip96 unexport -
The
/sys/class/gpio/gpioN(N = 1, 2, 3, 5, ...) directory contains various attributes for GPIO control.cd /sys/class/gpio/gpio95root@luckfox:/sys/class/gpio/gpio95# lsactive_low device direction edge power subsystem uevent valuedirection: Configure GPIO as input (in) or output (out).value: Read input level or control output level. Writing/reading1sets/reads a high level, while0sets/reads a low level.edge: Configure interrupt trigger type (used only for GPIO set as interrupts):- Rising edge:
rising - Falling edge:
falling - Both edges:
both - Disable interrupt:
none
- Rising edge:
2.3.2 Controlling GPIO Levels
- Set Direction
root@luckfox:/sys/class/gpio/gpio95# pwd # Ensure you're in the target GPIO directory
/sys/class/gpio/gpio95
echo out > direction # Set GPIO as output
echo in > direction # Set GPIO as input
-
Control GPIO Level Using the
valueAttribute# Outputecho 1 > valueecho 0 > value# Inputcat value -
Possible Errors
root@luckfox:~# echo 72 > /sys/class/gpio/exportbash: echo: write error: Device or resource busy- This error may occur if the GPIO is already claimed by a driver. Use the following command to check:
mount -t debugfs none /mediacat /media/gpioroot@luckfox:/# cat /media/gpiogpiochip0: GPIOs 0-31, parent: platform/27320000.gpio, gpio0:gpio-9 ( |bt_default_wake_host) in lo IRQgpio-14 ( |reset ) out lo ACTIVE LOWgpiochip1: GPIOs 32-63, parent: platform/2ae10000.gpio, gpio1:gpio-50 ( |bt_default_rts ) out logpio-55 ( |vcc5v0-host ) out higpio-60 ( |bt_default_wake ) out higpio-61 ( |work ) out higpiochip2: GPIOs 64-95, parent: platform/2ae20000.gpio, gpio2:gpiochip3: GPIOs 96-127, parent: platform/2ae30000.gpio, gpio3:gpio-104 ( |GTP RST PORT ) in higpio-118 ( |led ) out hi ACTIVE LOWgpio-120 ( |led ) out hi ACTIVE LOWgpio-125 ( |vbus5v0-typec ) out logpio-127 ( |reset ) out lo ACTIVE LOWgpiochip4: GPIOs 128-159, parent: platform/2ae40000.gpio, gpio4:gpio-128 ( |vcc3v3-m2 ) out higpio-148 ( |sbu1-dc ) out logpio-149 ( |sbu2-dc ) out higpiochip5: GPIOs 509-511, parent: platform/rk806-pinctrl.1.auto, rk806-gpio, can sleep:
3. Controlling I/O with Python
-
Toggle an LED Using Python
#!/usr/bin/python3from periphery import GPIOimport timeLED_Pin = 95LED_GPIO = GPIO(LED_Pin, "out")while True:try:LED_GPIO.write(True)time.sleep(0.5)LED_GPIO.write(False)time.sleep(0.5)except KeyboardInterrupt:LED_GPIO.write(False)breakexcept IOError:print ("Error")LED_GPIO.close() -
Run the Program
chmod 777 gpio.py./gpio.py
4. Controlling I/O with C
-
Download and extract the C code archive, and upload it to the Omni3576 board.
-
Enter the project folder. There are four files in the project: sysfs_gpio.c, sysfs_gpio.h, main.c and Makefile:
linaro@linaro-alip:~$ cd Luckfox_GPIO_C && lsmain.c Makefile sysfs_gpio.c sysfs_gpio.hlinaro@linaro-alip:~/Luckfox_GPIO_C$ -
The physical code of the Omni3576 pins is defined in the sysfs_gpio.h file and called in the main function main.c. You can modify it according to your needs:
#sysfs_gpio.h....#define I2C0_SDA 18 // 3,18#define I2C0_SCL 17 // 5,17#define UART8_TX 70 // 7,70#define GPIO17 98 //11,98#define GPIO27 99 //13,99#define GPIO22 13 //15.13#define SPI0_MOSI 24 //19,24#define SPI0_MISO 25 //21,25#define SPI0_CLK 23 //23,23#define I2C8_SDA 79 //27,79#define UART8_RX 71 //29,71#define GPIO6 151 //31,151#define GPIO13 97 //33,97#define SPI4_MISO 138 //35,138#define GPIO26 72 //37,72#define UART2_TX 141 //8,141#define UART2_RX 140 //10,140#define PWM2 27 //12,27#define GPIO23 12 //16,12#define GPIO24 21 //18,21#define GPIO25 77 //22,77#define SPI0_CS0 22 //24,22#define SPI0_CS1 19 //26,19#define I2C8_SCL 78 //28,78#define GPIO12 96 //32,96#define GPIO16 95 //36,95#define SPI4_MOSI 137 //38,137#define SPI4_CLK 136 //40,136....#main.c....int x[28] = {I2C0_SDA, I2C0_SCL, UART8_TX, GPIO17, GPIO27,GPIO22, SPI0_MOSI, SPI0_MISO, SPI0_CLK, I2C8_SDA,UART8_RX,GPIO6, GPIO13, SPI4_MISO, GPIO26,UART2_TX, UART2_RX, PWM2,GPIO23, GPIO24, GPIO25, SPI0_CS0, SPI0_CS1, I2C8_SCL, GPIO12,GPIO16, SPI4_MOSI, SPI4_CLK};.... -
Compile the program:
root@luckfox:/home/luckfox/Luckfox_GPIO_C# makecc -c sysfs_gpio.c -o sysfs_gpio.occ -c main.c -o main.occ sysfs_gpio.o main.o -o sysfs_gpio -
Run the program to toggle LEDs and debug GPIO states:
root@luckfox:/home/luckfox/Luckfox_GPIO_C# ./sysfs_gpioDebug: Export: Pin18Debug: Pin18:OutputDebug: Export: Pin17Debug: Pin17:OutputDebug: Export: Pin70Debug: Pin70:OutputDebug: Export: Pin98Debug: Pin98:OutputDebug: Export: Pin99Debug: Pin99:OutputDebug: Export: Pin13Debug: Pin13:OutputDebug: Export: Pin24Debug: Pin24:OutputDebug: Export: Pin25Debug: Pin25:OutputDebug: Export: Pin23Debug: Pin23:OutputDebug: Export: Pin79Debug: Pin79:OutputDebug: Export: Pin71Debug: Pin71:Output....
5. Device Tree Overview
-
The GPIO DTS source file is already defined in
kernel-6.10/arch/arm64/boot/dts/rockchip/rk3576.dtsi, and we can use it directly.gpio2: gpio@2ae20000 {compatible = "rockchip,gpio-bank";reg = <0x0 0x2ae20000 0x0 0x200>;interrupts = <GIC_SPI 161 IRQ_TYPE_LEVEL_HIGH>;clocks = <&cru PCLK_GPIO2>, <&cru DBCLK_GPIO2>;gpio-controller;#gpio-cells = <2>;gpio-ranges = <&pinctrl 0 64 32>;interrupt-controller;#interrupt-cells = <2>;}; -
If you want to configure
GPIOfor input and output, take controlling an LED as an example. You can define it in the device filekernel-6.1/arch/arm64/boot/dts/rockchip/luckfox-omni3576.dts:/ {model = "Luckfox Omni3576";compatible = "luckfox,omni3576", "rockchip,rk3576";gpio2pb0:gpio2pb0 {pinctrl-names = "default";pinctrl-0 = <&gpio2_pb0>;gpios = <&gpio2 RK_PB0 GPIO_ACTIVE_HIGH>;};};&pinctrl {gpio2-pb0 {gpio2_pb0:gpio2-pb0 {rockchip,pins = <2 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>;};};}; -
To configure
GPIOinterrupts, you can define them in the device filekernel-6.1/arch/arm64/boot/dts/rockchip/luckfox-omni3576.dtsas follows:/ {model = "Luckfox Omni3576";compatible = "luckfox,omni3576", "rockchip,rk3576";gpio2pb0:gpio2pb0 {compatible = "rockchip,gpio-bank";pinctrl-names = "default";pinctrl-0 = <&gpio2_pb0>;interrupt-parent = <&gpio2>;interrupts = <RK_PB0 IRQ_TYPE_EDGE_RISING>;};};&pinctrl {gpio2-pb0 {gpio2_pb0:gpio2-pb0 {rockchip,pins = <2 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>;};};};-
IRQ_TYPE_NONE: No defined interrupt trigger type -
IRQ_TYPE_EDGE_RISING: Triggered on rising edge -
IRQ_TYPE_EDGE_FALLING: Triggered on falling edge -
IRQ_TYPE_EDGE_BOTH: Triggered on both rising and falling edges -
IRQ_TYPE_LEVEL_HIGH: Triggered on high level -
IRQ_TYPE_LEVEL_LOW: Triggered on low level
-