Skip to main content

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: A0~A7, B0~B7, C0~C7, D0~D7

  • GPIO Naming Convention: GPIO pins are named as GPIO{bank}_{group}{X}, such as:

    GPIO0_A0 ~ A7 
    GPIO0_B0 ~ B7
    GPIO0_C0 ~ C7
    GPIO0_D0 ~ D7

    GPIO1_A0 ~ A7
    ....
    GPIO1_D0 ~ D7
    ....
    GPIO4_D0 ~ D7
  • GPIO Number Calculation Formula:

    pin = bank * 32 + number
    number = group * 8 + X
    pin = bank * 32 + (group * 8 + X)

    For example, calculating the pin number for GPIO2_D7_d:

    • bank :2
    • group :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

  1. Export GPIO to User Space:

    sudo su 
    echo 95 > /sys/class/gpio/export
  2. Unexport GPIO from User Space:

    echo 95 > /sys/class/gpio/unexport  

2.3.1 Device Directory and Attributes

  1. 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/gpio
    export gpio95 gpiochip0 gpiochip128 gpiochip32 gpiochip509 gpiochip64 gpiochip96 unexport
  2. The /sys/class/gpio/gpioN (N = 1, 2, 3, 5, ...) directory contains various attributes for GPIO control.

    cd /sys/class/gpio/gpio95
    root@luckfox:/sys/class/gpio/gpio95# ls
    active_low device direction edge power subsystem uevent value
    • direction: Configure GPIO as input (in) or output (out).
    • value: Read input level or control output level. Writing/reading 1 sets/reads a high level, while 0 sets/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

2.3.2 Controlling GPIO Levels

  1. 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
  2. Control GPIO Level Using the value Attribute

    # Output
    echo 1 > value
    echo 0 > value

    # Input
    cat value
  3. Possible Errors

    root@luckfox:~# echo 72 > /sys/class/gpio/export
    bash: 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 /media
    cat /media/gpio

    root@luckfox:/# cat /media/gpio
    gpiochip0: GPIOs 0-31, parent: platform/27320000.gpio, gpio0:
    gpio-9 ( |bt_default_wake_host) in lo IRQ
    gpio-14 ( |reset ) out lo ACTIVE LOW

    gpiochip1: GPIOs 32-63, parent: platform/2ae10000.gpio, gpio1:
    gpio-50 ( |bt_default_rts ) out lo
    gpio-55 ( |vcc5v0-host ) out hi
    gpio-60 ( |bt_default_wake ) out hi
    gpio-61 ( |work ) out hi

    gpiochip2: GPIOs 64-95, parent: platform/2ae20000.gpio, gpio2:

    gpiochip3: GPIOs 96-127, parent: platform/2ae30000.gpio, gpio3:
    gpio-104 ( |GTP RST PORT ) in hi
    gpio-118 ( |led ) out hi ACTIVE LOW
    gpio-120 ( |led ) out hi ACTIVE LOW
    gpio-125 ( |vbus5v0-typec ) out lo
    gpio-127 ( |reset ) out lo ACTIVE LOW

    gpiochip4: GPIOs 128-159, parent: platform/2ae40000.gpio, gpio4:
    gpio-128 ( |vcc3v3-m2 ) out hi
    gpio-148 ( |sbu1-dc ) out lo
    gpio-149 ( |sbu2-dc ) out hi

    gpiochip5: GPIOs 509-511, parent: platform/rk806-pinctrl.1.auto, rk806-gpio, can sleep:

3. Controlling I/O with Python

  1. Toggle an LED Using Python

    #!/usr/bin/python3
    from periphery import GPIO
    import time

    LED_Pin = 95

    LED_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)
    break
    except IOError:
    print ("Error")

    LED_GPIO.close()
  2. Run the Program

    chmod 777 gpio.py 
    ./gpio.py

4. Controlling I/O with C

  1. Download and extract the C code archive, and upload it to the Omni3576 board.

  2. 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 && ls
    main.c Makefile sysfs_gpio.c sysfs_gpio.h
    linaro@linaro-alip:~/Luckfox_GPIO_C$
  3. 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};
    ....
  4. Compile the program:

    root@luckfox:/home/luckfox/Luckfox_GPIO_C# make 
    cc -c sysfs_gpio.c -o sysfs_gpio.o
    cc -c main.c -o main.o
    cc sysfs_gpio.o main.o -o sysfs_gpio
  5. Run the program to toggle LEDs and debug GPIO states:

    root@luckfox:/home/luckfox/Luckfox_GPIO_C# ./sysfs_gpio
    Debug: Export: Pin18
    Debug: Pin18:Output
    Debug: Export: Pin17
    Debug: Pin17:Output
    Debug: Export: Pin70
    Debug: Pin70:Output
    Debug: Export: Pin98
    Debug: Pin98:Output
    Debug: Export: Pin99
    Debug: Pin99:Output
    Debug: Export: Pin13
    Debug: Pin13:Output
    Debug: Export: Pin24
    Debug: Pin24:Output
    Debug: Export: Pin25
    Debug: Pin25:Output
    Debug: Export: Pin23
    Debug: Pin23:Output
    Debug: Export: Pin79
    Debug: Pin79:Output
    Debug: Export: Pin71
    Debug: Pin71:Output
    ....

5. Device Tree Overview

  1. 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>;
    };
  2. If you want to configure GPIO for input and output, take controlling an LED as an example. You can define it in the device file kernel-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>;
    };
    };
    };

  3. To configure GPIO interrupts, you can define them in the device file kernel-6.1/arch/arm64/boot/dts/rockchip/luckfox-omni3576.dts as 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