UART Terminal for orange pi

"screen" command not work, may be the baurate too high, orange pi is using 1.5M. so use this command

picocom -b 1500000 /dev/tty.usbserial-A50285BI

The maximum speed to toggle a pin by python running inside cm4 is just 150khz

Determine the Linux GPIO Number for GPIO3_D1

The Orange Pi CM4 uses a 40-pin header, and GPIO3_D1 corresponds to a specific Linux GPIO number. The RK3566 GPIO numbering follows the formula:

GPIO_number = (bank_number * 32) + (subgroup_letter - 'A') * 8 + pin_number

  • GPIO3_D1:
    • Bank: GPIO3 (bank number 3)
    • Subgroup: D (D - A = 3)
    • Pin: 1
    • Calculation: (3 * 32) + (3 * 8) + 1 = 96 + 24 + 1 = 121

So, GPIO3_D1 is Linux GPIO 121. According to the Orange Pi CM4 pinout, GPIO3_D1 is physical pin 18 on the 40-pin header.

import os
import sys
import time
import signal

# Configuration
GPIO_NUMBER = 121  # GPIO3_D1 (physical pin 18)
LOOP_COUNT = 10000000  # Toggle 10 million times (~5-10s)
BASE_PATH = '/sys/class/gpio'

# Global flag for graceful exit
running = True

def signal_handler(sig, frame):
    global running
    print('\nStopping toggle...')
    running = False

signal.signal(signal.SIGINT, signal_handler)

def main():
    # Check if root
    if os.geteuid() != 0:
        print("Error: Run as root (sudo).", file=sys.stderr)
        sys.exit(1)

    gpio_path = f'{BASE_PATH}/gpio{GPIO_NUMBER}'

    # Export GPIO
    if not os.path.exists(gpio_path):
        try:
            with open(f'{BASE_PATH}/export', 'w') as f:
                f.write(str(GPIO_NUMBER))
            time.sleep(0.1)  # Allow setup
        except IOError as e:
            print(f"Error exporting GPIO {GPIO_NUMBER}: {e}", file=sys.stderr)
            sys.exit(1)

    # Set as output
    try:
        with open(f'{gpio_path}/direction', 'w') as f:
            f.write('out')
    except IOError as e:
        print(f"Error setting direction: {e}", file=sys.stderr)
        sys.exit(1)

    # Open value file for fast I/O in binary mode
    try:
        value_file = open(f'{gpio_path}/value', 'wb', buffering=0)
    except IOError as e:
        print(f"Error opening value file: {e}", file=sys.stderr)
        sys.exit(1)

    print(f"Toggling GPIO {GPIO_NUMBER} (GPIO3_D1, physical pin 18)...")
    print(f"Press Ctrl+C to stop.")
    start_time = time.perf_counter()

    count = 0
    while running and (LOOP_COUNT == 0 or count < LOOP_COUNT):
        value_file.write(b'1')  # Write bytes
        value_file.write(b'0')  # Write bytes
        value_file.flush()      # Ensure write
        count += 1

    end_time = time.perf_counter()
    value_file.close()

    # Cleanup
    try:
        with open(f'{BASE_PATH}/unexport', 'w') as f:
            f.write(str(GPIO_NUMBER))
    except IOError:
        pass

    duration = end_time - start_time
    if duration > 0:
        freq_hz = (count * 2) / duration
        print(f"\nCompleted {count} toggles in {duration:.3f}s ({freq_hz:.0f} Hz).")

if __name__ == '__main__':
    main()

If using C, it reaches 352khz, 2 times faster

This command check the memory base address of GPIO

dtc -I fs /sys/firmware/devicetree/base | grep -i gpio3

Compile:
sudo apt update
sudo apt install build-essential
cat /sys/kernel/debug/gpio
gcc -o toggle_gpio3_d1 toggle_gpio3_d1.c

#include &lt;stdio.h>
#include &lt;stdlib.h>
#include &lt;fcntl.h>
#include &lt;unistd.h>
#include &lt;signal.h>

#define SYSFS_GPIO_PATH "/sys/class/gpio"
#define GPIO_NUM 121  // GPIO3_D1

volatile sig_atomic_t running = 1;

void signal_handler(int sig) {
    running = 0;
}

int main() {
    int fd_export, fd_direction, fd_value;
    char path[64];

    // Check if root
    if (geteuid() != 0) {
        fprintf(stderr, "Error: Run as root (sudo).\n");
        return 1;
    }

    // Export GPIO
    snprintf(path, sizeof(path), "%s/export", SYSFS_GPIO_PATH);
    fd_export = open(path, O_WRONLY);
    if (fd_export &lt; 0) {
        perror("Error exporting GPIO");
        return 1;
    }
    dprintf(fd_export, "%d", GPIO_NUM);
    close(fd_export);
    usleep(100000); // Wait for export

    // Set as output
    snprintf(path, sizeof(path), "%s/gpio%d/direction", SYSFS_GPIO_PATH, GPIO_NUM);
    fd_direction = open(path, O_WRONLY);
    if (fd_direction &lt; 0) {
        perror("Error setting direction");
        return 1;
    }
    write(fd_direction, "out", 3);
    close(fd_direction);

    // Open value file
    snprintf(path, sizeof(path), "%s/gpio%d/value", SYSFS_GPIO_PATH, GPIO_NUM);
    fd_value = open(path, O_WRONLY);
    if (fd_value &lt; 0) {
        perror("Error opening value file");
        return 1;
    }

    printf("Toggling GPIO3_D1 (Linux GPIO %d) at ~500 Hz to find physical pin...\n", GPIO_NUM);
    printf("Test pins 15, 16, 18 with oscilloscope. Press Ctrl+C to stop.\n");

    // Slow toggle for testing
    while (running) {
        write(fd_value, "1", 1);
        //usleep(1000); // 1ms
        write(fd_value, "0", 1);
        //usleep(1000); // 1ms
    }

    // Cleanup
    close(fd_value);
    snprintf(path, sizeof(path), "%s/unexport", SYSFS_GPIO_PATH);
    fd_export = open(path, O_WRONLY);
    if (fd_export >= 0) {
        dprintf(fd_export, "%d", GPIO_NUM);
        close(fd_export);
    }

    printf("GPIO3_D1 unexported\n");
    return 0;
}

Command to toggle pin GPIO3_D1 (GPIO 121)

# Export GPIO 121
echo 121 > /sys/class/gpio/export

# Set as output
echo out > /sys/class/gpio/gpio121/direction

# Toggle manually and check with scope
echo 1 > /sys/class/gpio/gpio121/value
echo 0 > /sys/class/gpio/gpio121/value

# Cleanup
echo 121 > /sys/class/gpio/unexport