Bananapif3 gpio wiringpi, gpiod, python3-periphery, adafruit blinka and luma.examples

Build Armbian Ubuntu Mantic CLI image

$ git clone https://github.com/BPI-SINOVOIP/armbian-build.git -b v24.04.30 armbian-build
$ cd armbian-build
$ ./compile.sh BOARD=bananapif3 BRANCH=legacy RELEASE=mantic BUILD_DESKTOP=no BUILD_MINIMAL=no KERNEL_CONFIGURE=no INSTALL_HEADERS=yes COMPRESS_OUTPUTIMAGE=sha,xz

More info about Armbian build, Please refer to Armbian.

After build finish, Flash armbian-build/output/images/Armbian-bpi-SpacemiT_24.5.0-trunk_Bananapif3_mantic_legacy_6.1.15.img.xz to sdcard with BalenaEtcher.

Insert sdcard to bananapif3 board, power on and finish the Setup Wizard.

Enable 26pin I2C4, SPI3, UART5 and PWM7

The latest Spacemit bsp is not support kernel DTS Overlay, so modify the dtb with dtc command to enable 26pin i2c4, spi3, uart5(pin8 and pin10 R_UART0 not support), pwm7.

If you are familiar with armbian build source code and compilation process, you can add a kernel patch that enables the relevant 26-pin functions to your local code, allowing you to skip this step.

If future versions support DTS overlay, the 26-pin functions can be enabled through a DTS overlay.

root@bananapif3:~# cd /boot/dtb/spacemit
root@bananapif3:~# cp k1-x_deb1.dtb k1-x_deb1.dtb.org
root@bananapif3:~# dtc -I dtb -O dts -o k1-x_deb1.dts k1-x_deb1.dtb
root@bananapif3:~# vi k1-x_deb1.dts

	//i2c4
	i2c@d4012800 {
		status = "okay";
	};
	
	//spi3
	spi@d401c000 {
		status = "okay";
	};
	
	//pwm7
	pwm@d401bc00 {
		status = "okay";
	};
	
	//uart5
	uart@d4017400 {
		status = "okay";
	};

root@bananapif3:~# dtc -I dts -O dtb -o k1-x_deb1.dtb k1-x_deb1.dts
root@bananapif3:~# reboot

Confirm I2C4, SPI3, UART5, PWM7 enabled

root@bananapif3:~# ls /dev/i2c-4
/dev/i2c-4

root@bananapif3:~# ls /dev/spidev3.0 
/dev/spidev3.0

root@bananapif3:~# ls /dev/ttyS4
/dev/ttyS4

root@bananapif3:~# ls -l /sys/class/pwm/pwmchip0/
total 0
lrwxrwxrwx 1 root root    0 Sep 10 16:11 device -> ../../../d401bc00.pwm
--w------- 1 root root 4096 Sep 10 16:11 export
-r--r--r-- 1 root root 4096 Sep 10 16:11 npwm
drwxr-xr-x 2 root root    0 Sep 10 16:09 power
lrwxrwxrwx 1 root root    0 Sep 10 16:08 subsystem -> ../../../../../../class/pwm
-rw-r--r-- 1 root root 4096 Sep 10 16:08 uevent
--w------- 1 root root 4096 Sep 10 16:11 unexport

WiringPi

root@bananapif3:~# git clone https://github.com/Dangku/WiringPi
root@bananapif3:~# cd WiringPi
root@bananapif3:~# ./build

gpio command

root@bananapif3:~/WiringPi# gpio -v
gpio version: 3.6
Copyright (c) 2012-2024 Gordon Henderson and contributors
This is free software with ABSOLUTELY NO WARRANTY.
For details type: gpio -warranty

Hardware details:
  Type: k1-x deb1, Revision: 01, Memory: 8192MB
  Maker: Bananapi, Chip-Vendor: Spacemit

System details:
  Kernel version: 6.1.15-legacy-k1
  Device tree present.
  Model: spacemit k1-x deb1 board
  Root or sudo required for direct GPIO access.

root@bananapif3:~/WiringPi# gpio readall

gpio set phy pin.26 high/low

root@bananapif3:~/WiringPi# gpio mode 11 out
root@bananapif3:~/WiringPi# gpio write 11 1
root@bananapif3:~/WiringPi# gpio write 11 0

spi ssd1306 oled example

root@bananapif3:~/WiringPi# cd examples
root@bananapif3:~/WiringPi/examples# make oled
root@bananapif3:~/WiringPi/examples# ./oled

i2c lcd1602 oled example

root@bananapif3:~/WiringPi# cd examples
root@bananapif3:~/WiringPi/examples# make lcd-adafruit 
root@bananapif3:~/WiringPi/examples# ./lcd-adafruit 3

Libgpiod

libgpiod is a library specifically designed for interacting with the Linux GPIO subsystem, offering a more modern and efficient way compared to the traditional sysfs interface. It also provides several command-line tools for controlling GPIOs.

Installing gpiod packages

root@bananapif3:~# sudo apt -y install gpiod libgpiod-dev

gpiodetect - get gpiochip list

root@bananapif3:~# gpiodetect
gpiochip0 [k1x-gpio] (128 lines)
gpiochip1 [spacemit-pinctrl@spm8821] (6 lines)

gpioinfo - get all gpio pins, 128 lines corresponding to 128 GPIOs, line number is gpio number.

root@bananapif3:~# gpioinfo
gpiochip0 - 128 lines:

	      line   0: "GMAC0_RXDV"       unused   input  active-high 
	      line   1: "GMAC0_RXD0"       unused   input  active-high 
	      line   2: "GMAC0_RXD1"       unused   input  active-high 
	      line   3: "GMAC0_RXCLK"      unused   input  active-high
	      …
gpiochip1 - 6 lines:
	      line   0:      unnamed       unused  output  active-high 
	      line   1:      unnamed       unused  output  active-high 
	      line   2:      unnamed       unused  output  active-high 
	      line   3:      unnamed       unused  output  active-high
	      …

gpioget - get gpio level, gpio 50 is 26pin phy pin.26

root@bananapif3:~# gpioget gpiochip0 50
1

gpioset - set gpio output level

root@bananapif3:~# gpioset gpiochip0 50=0

gpiomon​ - monitor GPIO event

root@bananapif3:~# gpiomon gpiochip0 50
(GPIO event output)
event: FALLING EDGE offset: 50 timestamp: [     647.533655138]
event: FALLING EDGE offset: 50 timestamp: [     647.533674597]
…

gpiofind​ - get 26pin phy pin.26 gpio number by gpio line name

root@bananapif3:~# gpiofind CON_P26
gpiochip0 50

Python3-libgpiod

python3-libgpiod is the Python binding library for libgpiod, which allows the use of libgpiod’s features to control and monitor GPIO pins in Python, only support gpio input/output control

Install python3-libgpiod

root@bananapif3:~# apt install python3-libgpiod

gpio_get.py, get gpio pin level

import gpiod
	
# 26pin, phy.26, gpio50
LINE_OFFSET = 50
	
# chip number and pin number
chip = gpiod.Chip("0", gpiod.Chip.OPEN_BY_NUMBER)
line = chip.get_line(LINE_OFFSET)
	
# input
line.request(consumer='gpio', type=gpiod.LINE_REQ_DIR_IN)
	
# get gpio level
value = line.get_value()
print(f"GPIO value is {value}")

gpio_set.py, set gpio output level

import gpiod
	
# 26pin, phy.26, gpio50
LINE_OFFSET = 50
	
# chip number and pin number
chip = gpiod.Chip('gpiochip0')
line = chip.get_line(LINE_OFFSET)
	
# output
line.request(consumer='example', type=gpiod.LINE_REQ_DIR_OUT)
	
# high level
line.set_value(1)

gpio_monitor.py, monitor gpio pin event

import gpiod
	
# 26pin, phy.26, gpio50
LINE_OFFSET = 50
	
# chip number and pin number
chip = gpiod.Chip('gpiochip0')
line = chip.get_line(LINE_OFFSET)

# request trigger type(rising and falling)
line.request(consumer='example', type=gpiod.LINE_REQ_EV_BOTH_EDGES)
	
print("Waiting for events...")
while True:
    event = line.event_wait(sec=5) 
    if event:
        evt = line.event_read()
        if evt.type == gpiod.LineEvent.RISING_EDGE:
            print("Rising edge detected")
        elif evt.type == gpiod.LineEvent.FALLING_EDGE:
            print("Falling edge detected")
    else:
       print("No event detected in the last 5 seconds")

Python3-periphery

python3-periphery is a Python library used for controlling various hardware peripherals. It supports interaction with hardware interfaces like GPIO, I2C, SPI, serial (UART), and MMIO (memory-mapped I/O). The library is designed to provide a simple and consistent way to access these interfaces, allowing Python programs to easily control hardware devices.

Install python3-periphery

$ sudo apt install python3-periphery

gpio.py, gpio set and get test

from periphery import GPIO
	
# gpiochip0
CHIP = "/dev/gpiochip0"

# 26pin phy.7, gpio70
LINE_OFFSET = 70
	
# set output
gpio = GPIO(CHIP, LINE_OFFSET, "out")
	
# set high level
gpio.write(True)
	
# get gpio level
print("GPIO state:", gpio.read())

gpio.close()

i2c.py, read I2C device register test

enable 26pin phy pin.3 and pin.5 I2C4 functions before i2c test

from periphery import I2C
	
# i2c4, slave address 0x50
I2C_DEV = "/dev/i2c-4"
SLAVE_ADDR = 0x50
	
# intial i2c dev
i2c = I2C(I2C_DEV)
	
# read reg
msg = [I2C.Message([0x10]), I2C.Message([0x00], read=True)]
i2c.transfer(SLAVE_ADDR, msg)

print("I2C data read: 0x{:02x}".format(msg[1].data[0]))

i2c.close()

spi.py, spi loopback test, short connect 26pin,phy.19(SPI3_MOSI) and phy.21(SPI3_MISO)

enable 26pin phy pin.18, pin21, pin23, and pin.24 SPI3 functions before spi test

from periphery import SPI
	
# spi dev
SPI_DEV = "/dev/spidev3.0"
	
# send data
data_out = [0xAA, 0xBB, 0xCC, 0xDD]

try:
    # request spi,open spidev3.0,mode 0, freqency 1MHz
    spi = SPI(SPI_DEV, 0, 1000000)
	
    # send data and receive data
    data_in = spi.transfer(data_out)

    print("send: [0x{:02x}, 0x{:02x}, 0x{:02x}, 0x{:02x}]".format(*data_out))
    print("receive: [0x{:02x}, 0x{:02x}, 0x{:02x}, 0x{:02x}]".format(*data_in))
finally:
    spi.close()

uart.py, uart read and write test

enable 26pin phy pin.7, and pin.11 UART5 functions before uart test

from periphery import Serial
	
# uart device and baudrate
UART_DEV = "/dev/ttyS4"
BAUDRATE = 115200
	
# initial uart
serial = Serial(UART_DEV, BAUDRATE)
	
# send data
serial.write(b"Hello, World!\n")
	
# receive data
data = serial.read(128)  # 读取最多128字节

print("Received:", data)

serial.close()

pwm.py, pwm test

enable 26pin phy pin.18 PWM7 functions before pwm test

from periphery import PWM
import time
	
# PWM7, pwmchip0, channel 0 ,26pin, phy.18, pwm7(0xd401bc00)
PWM_CHIP = 0
PWM_CH = 0
	
# initial pwm
pwm = PWM(PWM_CHIP, PWM_CH) 

# 2500Hz ( period_ns limit to 400000ns)
pwm.frequency = 2500 

# duty 50% percentage
pwm.duty_cycle = 0.5
	
# set period_ns 20,000 ns
# pwm.period_ns = 20000
	
# set duty_cycle_ns 10,000 ns (50%)
# pwm.duty_cycle_ns = 10000

# enable pwm
pwm.enable()

time.sleep(5)
	
# set 10% percentage
pwm.duty_cycle = 0.1
	
# set duty_cycle_ns 2000ns (10%)
# pwm.duty_cycle_ns = 2000

time.sleep(5)
	
# disable pwm
pwm.disable()
	
# unexport PWM
pwm.close()

Adafruit Blinka

Adafruit Blinka is similar to python-periphery in functionality, providing control over GPIO, I2C, SPI, PWM, and UART interfaces. More info please ref to Adafruit wiki and adafruit github

Install packages

root@bananapif3:~# apt install git gpiod python3-dev python3-libgpiod python3-periphery python3-pip python3-venv libjpeg-dev zlib1g-dev libfreetype-dev libfreetype6 fonts-dejavu

Install Adafruit Blinka locally from PyPI.

root@bananapif3:~# pip3 install Adafruit-Blinka

Install Adafruit Blinka from git source

root@bananapif3:~# pip3 install git+https://github.com/adafruit/Adafruit_Blinka

To install in a virtual environment in your current project:

oot@bananapif3:~# mkdir project-name
root@bananapif3:~# cd project-name
root@bananapif3:~# python3 -m venv myenv
root@bananapif3:~# source myenv/bin/activate
(myenv) root@bananapif3:~#

Install gpiod python binding library

(myenv) root@bananapif3:~# pip3 install gpiod

virtual env Install Adafruit Blinka from PyPI.

(myenv) root@bananapif3:~# pip3 install Adafruit-Blinka

virtual env Install Adafruit Blinka from git source

(myenv) root@bananapif3:~# pip3 install git+https://github.com/adafruit/Adafruit_Blinka

Blinka Test examples

create a new file called blinkatest.py with nano or your favorite text editor and put the following in:

import board
import digitalio
import busio

print("Hello, blinka!")

# pin.26, Try to create a Digital input
pin = digitalio.DigitalInOut(board.D26)
print("Digital IO ok!")

# Try to create an I2C device
i2c = busio.I2C(board.SCL, board.SDA)
print("I2C ok!")

# Try to create an SPI device
spi = busio.SPI(board.SCLK, board.MOSI, board.MISO)
print("SPI ok!")

print("done!")

Save it and run at the command line with:

(myenv) root@bananapif3:~# python3 blinkatest.py
Hello, blinka!
Digital IO ok!
I2C ok!
SPI ok!
done!
(myenv) root@bananapif3:~#

create blinka_led.py example that blinks the LED on and off once a second

import time
import board
import digitalio

print("hello blinky!")

# pin.26
led = digitalio.DigitalInOut(board.D26)
led.direction = digitalio.Direction.OUTPUT

while True:
    led.value = True
    time.sleep(0.5)
    led.value = False
    time.sleep(0.5)

create blinka_button example that control the led turns on whenever the button is pressed

import time
import board
import digitalio

print("press the button!")

# pin.26
led = digitalio.DigitalInOut(board.D26)
led.direction = digitalio.Direction.OUTPUT

button = digitalio.DigitalInOut(board.D22)
button.direction = digitalio.Direction.INPUT
button.pull = digitalio.Pull.UP

while True:
    led.value = not button.value # light when button is pressed!

I2C rgb lcd test for MCP23017 I2C Lcd1602 Module

Installing libraries from git repo, these two repo forked from Adafruit_CircuitPython_CharLCD and Adafruit_CircuitPython_MCP230xx, modified to match the hardware MCP23017 I2C Lcd1602 Module. Examples in these two repo also should be modified to match the hardware design

(myenv) root@bananapif3:~# pip3 install git+https://github.com/Dangku/Adafruit_CircuitPython_CharLCD
(myenv) root@bananapif3:~# pip3 install git+https://github.com/Dangku/Adafruit_CircuitPython_MCP230xx

Running test example

(myenv) root@bananapif3:~# wget https://raw.githubusercontent.com/Dangku/Adafruit_CircuitPython_CharLCD/main/examples/charlcd_i2c_rgb_simpletest.py
(myenv) root@bananapif3:~# python3 charlcd_i2c_rgb_simpletest.py

SPI oled test for SSD1306 SPI Oled Module

Installing the library from git repo, this repo forked from Adafruit_CircuitPython_SSD1306, libs and examples modified to match the hardware SSD1306 SPI Oled Module.

(myenv) root@bananapif3:~# pip3 install git+https://github.com/Dangku/Adafruit_CircuitPython_SSD1306

Installing pillow for some examples which import PIL, pip3 install Pillow command causes ImportError: cannot import name ‘_imagingft’ from ‘PIL’, so instead with the following command.

(myenv) root@bananapif3:~# pip3 install Pillow --no-binary :all:

Running test example

(myenv) root@bananapif3:~# wget https://raw.githubusercontent.com/Dangku/Adafruit_CircuitPython_SSD1306/main/examples/ssd1306_stats.py
(myenv) root@bananapif3:~# python3 ssd1306_stats.py

I2C oled test for SSD1306 I2C Oled Module

lib and examples source files are the same as spi ssd1306, but example must be modified to enable i2c interface and disable spi interface

# Create the I2C interface.
i2c = board.I2C()

# Create the SSD1306 OLED class.
# The first two parameters are the pixel width and pixel height.  Change these
# to the right size for your display!
disp = adafruit_ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c)

# Use for SPI
#spi = board.SPI()
# disp_cs = digitalio.DigitalInOut(board.CS)
#disp_cs = None
#disp_dc = digitalio.DigitalInOut(board.D18)
#disp_reset = digitalio.DigitalInOut(board.D22)
#disp = adafruit_ssd1306.SSD1306_SPI(WIDTH, HEIGHT, spi, disp_dc, disp_reset, dii
sp_cs)

Running test example

(myenv) root@bananapif3:~# python3 ssd1306_stats.py

Luma.examples

luma.examples is a companion repo for running examples against the luma.oled, luma.lcd, luma.led_matrix and luma.emulator display drivers.

Install packages

root@bananapif3:~# apt-get install libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev

Running in virtual environment created before

root@bananapif3:~# source myenv/bin/activate
(myenv) root@bananapif3:~#

Install RPi.GPIO from git repo

(myenv) root@bananapif3:~# pip3 install git+https://github.com/Dangku/RPi.GPIO.git

Clone Luma.examples and install libs

(myenv) root@bananapif3:~# git clone https://github.com/rm-hull/luma.examples.git
(myenv) root@bananapif3:~# cd luma.examples
(myenv) root@bananapif3:~# pip3 install -e .

Confirm packages installed successfully

(myenv) root@bananapif3:~# pip3 list | grep luma
luma.core                                2.4.2
luma.emulator                            1.5.0
luma.lcd                                 2.11.0
luma.led-matrix                          1.7.1
luma.oled                                3.13.0

i2c ssd1306 oled test

create conf/ssd1306_i2c.conf, i2c device is /dev/i2c-4

--display=ssd1306
--interface=i2c
--i2c-port=4
--i2c-address=0x3C
--width=128
--height=64

(myenv) root@bananapif3:~# cd examples
(myenv) root@bananapif3:~# python3 bounce.py --config ../conf/ssd1306_i2c.conf

spi ssd1306 oled test

create conf/ssd1306_spi.conf, spi device is /dev/spi3.0

--display=ssd1306
--interface=spi
--spi-port=3
--spi-device=0
--spi-bus-speed=52000000
--width=128
--height=64

(myenv) root@bananapif3:~# cd examples
(myenv) root@bananapif3:~# python3 bounce.py --config ../conf/ssd1306_spi.conf

spi ili9341 lcd test

create conf/ili9341.conf, spi device is /dev/spi3.0, gpio-reset is phy pin.18, gpio-dc is phy pin.16, gpio-backlight is phy pin.12

--display=ili9341
--interface=spi
--spi-port=3
--spi-device=0
--spi-bus-speed=52000000
--gpio-reset=24
--gpio-data-command=23
--gpio-backlight=18
--width=320
--height=240
--backlight-active=high
--gpio-reset-hold-time=0.1
--gpio-reset-release-time=0.1

(myenv) root@bananapif3:~# cd examples
(myenv) root@bananapif3:~# python3 bounce.py --config ../conf/ili9341.conf

1 Like