Skip to content

Commit 3370f2f

Browse files
authored
Merge pull request #911 from frank-pet/lgpio-native
Use lgpio native for the Pi 5 to avoid RPi.GPIO conflicts.
2 parents 58a8e17 + c3a735b commit 3370f2f

File tree

14 files changed

+536
-89
lines changed

14 files changed

+536
-89
lines changed

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
elif b"brcm,bcm2712" in compat:
3232
board_reqs = [
3333
"rpi_ws281x>=4.0.0",
34-
"rpi-lgpio",
34+
"lgpio",
3535
"Adafruit-Blinka-Raspberry-Pi5-Neopixel",
3636
]
3737
# Pi 4 and Earlier
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# SPDX-FileCopyrightText: 2021 Melissa LeBlanc-Williams for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
"""Pin definitions for Raspberry Pi 5 models using the BCM2712."""
5+
6+
from adafruit_blinka.microcontroller.bcm2712 import pin
7+
8+
D0 = pin.D0
9+
D1 = pin.D1
10+
11+
D2 = pin.D2
12+
SDA = pin.SDA
13+
D3 = pin.D3
14+
SCL = pin.SCL
15+
16+
D4 = pin.D4
17+
D5 = pin.D5
18+
D6 = pin.D6
19+
20+
D7 = pin.D7
21+
CE1 = pin.D7
22+
D8 = pin.D8
23+
CE0 = pin.D8
24+
D9 = pin.D9
25+
MISO = pin.D9
26+
D10 = pin.D10
27+
MOSI = pin.D10
28+
D11 = pin.D11
29+
SCLK = pin.D11
30+
SCK = pin.D11
31+
32+
D12 = pin.D12
33+
D13 = pin.D13
34+
35+
D14 = pin.D14
36+
TXD = pin.D14
37+
D15 = pin.D15
38+
RXD = pin.D15
39+
# create alias for most of the examples
40+
TX = pin.D14
41+
RX = pin.D15
42+
43+
D16 = pin.D16
44+
D17 = pin.D17
45+
D18 = pin.D18
46+
D19 = pin.D19
47+
MISO_1 = pin.D19
48+
D20 = pin.D20
49+
MOSI_1 = pin.D20
50+
D21 = pin.D21
51+
SCLK_1 = pin.D21
52+
SCK_1 = pin.D21
53+
D22 = pin.D22
54+
D23 = pin.D23
55+
D24 = pin.D24
56+
D25 = pin.D25
57+
D26 = pin.D26
58+
D27 = pin.D27

src/adafruit_blinka/microcontroller/bcm2711/pin.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22
#
33
# SPDX-License-Identifier: MIT
44
"""Broadcom BCM2711 pin names"""
5-
from RPi import GPIO
6-
from adafruit_blinka.microcontroller.bcm283x.pin import Pin
75

8-
GPIO.setmode(GPIO.BCM) # Use BCM pins D4 = GPIO #4
9-
GPIO.setwarnings(False) # shh!
6+
# Use RPi.GPIO pins for Raspberry Pi 4
7+
from adafruit_blinka.microcontroller.generic_linux.rpi_gpio_pin import Pin
108

119
D0 = Pin(0)
1210
D1 = Pin(1)
File renamed without changes.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# SPDX-FileCopyrightText: 2021 Melissa LeBlanc-Williams for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
"""Broadcom BCM2712 pin names"""
5+
6+
# Use lgpio pins for Raspberry Pi 5
7+
from adafruit_blinka.microcontroller.generic_linux.lgpio_pin import Pin
8+
9+
D0 = Pin(0)
10+
D1 = Pin(1)
11+
12+
D2 = Pin(2)
13+
SDA = Pin(2)
14+
D3 = Pin(3)
15+
SCL = Pin(3)
16+
17+
D4 = Pin(4)
18+
D5 = Pin(5)
19+
D6 = Pin(6)
20+
21+
D7 = Pin(7)
22+
CE1 = Pin(7)
23+
D8 = Pin(8)
24+
CE0 = Pin(8)
25+
D9 = Pin(9)
26+
MISO = Pin(9)
27+
D10 = Pin(10)
28+
MOSI = Pin(10)
29+
D11 = Pin(11)
30+
SCLK = Pin(11) # Raspberry Pi naming
31+
SCK = Pin(11) # CircuitPython naming
32+
33+
D12 = Pin(12)
34+
D13 = Pin(13)
35+
36+
D14 = Pin(14)
37+
TXD = Pin(14)
38+
D15 = Pin(15)
39+
RXD = Pin(15)
40+
41+
D16 = Pin(16)
42+
D17 = Pin(17)
43+
D18 = Pin(18)
44+
D19 = Pin(19)
45+
MISO_1 = Pin(19)
46+
D20 = Pin(20)
47+
MOSI_1 = Pin(20)
48+
D21 = Pin(21)
49+
SCLK_1 = Pin(21)
50+
SCK_1 = Pin(21)
51+
D22 = Pin(22)
52+
D23 = Pin(23)
53+
D24 = Pin(24)
54+
D25 = Pin(25)
55+
D26 = Pin(26)
56+
D27 = Pin(27)
57+
D28 = Pin(28)
58+
D29 = Pin(29)
59+
D30 = Pin(30)
60+
D31 = Pin(31)
61+
D32 = Pin(32)
62+
D33 = Pin(33)
63+
D34 = Pin(34)
64+
D35 = Pin(35)
65+
D36 = Pin(36)
66+
D37 = Pin(37)
67+
D38 = Pin(38)
68+
D39 = Pin(39)
69+
D40 = Pin(40)
70+
MISO_2 = Pin(40)
71+
D41 = Pin(41)
72+
MOSI_2 = Pin(41)
73+
D42 = Pin(42)
74+
SCLK_2 = Pin(42)
75+
SCK_2 = Pin(43)
76+
D43 = Pin(43)
77+
D44 = Pin(44)
78+
D45 = Pin(45)
79+
80+
# ordered as spiId, sckId, mosiId, misoId
81+
spiPorts = (
82+
(0, SCLK, MOSI, MISO),
83+
(1, SCLK_1, MOSI_1, MISO_1),
84+
(2, SCLK_2, MOSI_2, MISO_2),
85+
(3, D3, D2, D1),
86+
(4, D7, D6, D5),
87+
(5, D15, D14, D13),
88+
)
89+
90+
# ordered as uartId, txId, rxId
91+
uartPorts = ((1, TXD, RXD),)
92+
93+
# These are the known hardware I2C ports / pins.
94+
# For software I2C ports created with the i2c-gpio overlay, see:
95+
# https://github.com/adafruit/Adafruit_Python_Extended_Bus
96+
i2cPorts = (
97+
(1, SCL, SDA),
98+
(0, D1, D0), # both pi 1 and pi 2 i2c ports!
99+
(10, D45, D44), # internal i2c bus for the CM4
100+
)

src/adafruit_blinka/microcontroller/bcm283x/pin.py

Lines changed: 2 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,73 +2,10 @@
22
#
33
# SPDX-License-Identifier: MIT
44
"""Broadcom BCM283x pin names"""
5-
from RPi import GPIO
65

7-
GPIO.setmode(GPIO.BCM) # Use BCM pins D4 = GPIO #4
8-
GPIO.setwarnings(False) # shh!
6+
# Use RPi.GPIO pins for Raspberry Pi 1-3B+
7+
from adafruit_blinka.microcontroller.generic_linux.rpi_gpio_pin import Pin
98

10-
11-
class Pin:
12-
"""Pins dont exist in CPython so...lets make our own!"""
13-
14-
IN = 0
15-
OUT = 1
16-
LOW = 0
17-
HIGH = 1
18-
PULL_NONE = 0
19-
PULL_UP = 1
20-
PULL_DOWN = 2
21-
22-
id = None
23-
_value = LOW
24-
_mode = IN
25-
26-
def __init__(self, bcm_number):
27-
self.id = bcm_number
28-
29-
def __repr__(self):
30-
return str(self.id)
31-
32-
def __eq__(self, other):
33-
return self.id == other
34-
35-
def init(self, mode=IN, pull=None):
36-
"""Initialize the Pin"""
37-
if mode is not None:
38-
if mode == self.IN:
39-
self._mode = self.IN
40-
GPIO.setup(self.id, GPIO.IN)
41-
elif mode == self.OUT:
42-
self._mode = self.OUT
43-
GPIO.setup(self.id, GPIO.OUT)
44-
else:
45-
raise RuntimeError("Invalid mode for pin: %s" % self.id)
46-
if pull is not None:
47-
if self._mode != self.IN:
48-
raise RuntimeError("Cannot set pull resistor on output")
49-
if pull == self.PULL_UP:
50-
GPIO.setup(self.id, GPIO.IN, pull_up_down=GPIO.PUD_UP)
51-
elif pull == self.PULL_DOWN:
52-
GPIO.setup(self.id, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
53-
else:
54-
raise RuntimeError("Invalid pull for pin: %s" % self.id)
55-
56-
def value(self, val=None):
57-
"""Set or return the Pin Value"""
58-
if val is not None:
59-
if val == self.LOW:
60-
self._value = val
61-
GPIO.output(self.id, val)
62-
elif val == self.HIGH:
63-
self._value = val
64-
GPIO.output(self.id, val)
65-
else:
66-
raise RuntimeError("Invalid value for pin")
67-
return None
68-
return GPIO.input(self.id)
69-
70-
71-
# Pi 1B rev1 only?
729
D0 = Pin(0)
7310
D1 = Pin(1)
7411

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# SPDX-FileCopyrightText: 2025 Melissa LeBlanc-Williams for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
"""A Pin class for use with lgpio."""
5+
6+
from pathlib import Path
7+
import lgpio
8+
9+
10+
def _get_gpiochip():
11+
"""
12+
Determines the handle of the GPIO chip device to access.
13+
14+
iterate through sysfs to find a GPIO chip device with a driver known to be
15+
used for userspace GPIO access.
16+
"""
17+
for dev in Path("/sys/bus/gpio/devices").glob("gpiochip*"):
18+
drivers = set((dev / "of_node/compatible").read_text().split("\0"))
19+
# check if driver names are intended for userspace control
20+
if drivers & {
21+
"raspberrypi,rp1-gpio",
22+
"raspberrypi,bcm2835-gpio",
23+
"raspberrypi,bcm2711-gpio",
24+
}:
25+
return lgpio.gpiochip_open(int(dev.name[-1]))
26+
# return chip0 as a fallback
27+
return lgpio.gpiochip_open(0)
28+
29+
30+
CHIP = _get_gpiochip()
31+
32+
33+
class Pin:
34+
"""Pins dont exist in CPython so...lets make our own!"""
35+
36+
LOW = 0
37+
HIGH = 1
38+
OFF = LOW
39+
ON = HIGH
40+
41+
# values of lg mode constants
42+
PULL_NONE = 0x80
43+
PULL_UP = 0x20
44+
PULL_DOWN = 0x40
45+
ACTIVE_LOW = 0x02
46+
47+
# drive mode lg constants
48+
OPEN_DRAIN = 0x04
49+
IN = 0x0100
50+
OUT = 0x0200
51+
52+
# LG mode constants
53+
_LG_ALERT = 0x400
54+
_LG_GROUP = 0x800
55+
_LG_MODES = IN | OUT | _LG_ALERT | _LG_GROUP
56+
_LG_PULLS = PULL_NONE | PULL_UP | PULL_NONE | ACTIVE_LOW
57+
_LG_DRIVES = OPEN_DRAIN
58+
59+
id = None
60+
_value = LOW
61+
_mode = IN
62+
63+
# we want exceptions
64+
lgpio.exceptions = True
65+
66+
def __init__(self, bcm_number):
67+
self.id = bcm_number
68+
69+
def __repr__(self):
70+
return str(self.id)
71+
72+
def __eq__(self, other):
73+
return self.id == other
74+
75+
def init(self, mode=IN, pull=None):
76+
"""Initialize the Pin"""
77+
if mode is not None:
78+
if mode == Pin.IN:
79+
self._mode = Pin.IN
80+
self._set_gpio_mode_in()
81+
elif mode == self.OUT:
82+
self._mode = Pin.OUT
83+
Pin._check_result(lgpio.gpio_claim_output(CHIP, self.id, Pin.LOW))
84+
else:
85+
raise RuntimeError(f"Invalid mode for pin: {self.id}")
86+
if pull is not None:
87+
if self._mode != Pin.IN:
88+
raise RuntimeError("Can only set pull resistor on input")
89+
if pull in {Pin.PULL_UP, Pin.PULL_DOWN, Pin.PULL_NONE}:
90+
self._set_gpio_mode_in(lflags=pull)
91+
else:
92+
raise RuntimeError(f"Invalid pull for pin: {self.id}")
93+
94+
def value(self, val=None):
95+
"""Set or return the Pin Value"""
96+
if val is not None:
97+
if val == Pin.LOW:
98+
self._value = val
99+
Pin._check_result(lgpio.gpio_write(CHIP, self.id, val))
100+
elif val == Pin.HIGH:
101+
self._value = val
102+
Pin._check_result(lgpio.gpio_write(CHIP, self.id, val))
103+
else:
104+
raise RuntimeError("Invalid value for pin")
105+
return None
106+
return Pin._check_result(lgpio.gpio_read(CHIP, self.id))
107+
108+
@staticmethod
109+
def _check_result(result):
110+
"""
111+
convert any result other than zero to a text message and pass it back
112+
as a runtime exception. Typical usage: use the lgpio call as the
113+
argument.
114+
"""
115+
if result < 0:
116+
raise RuntimeError(lgpio.error_text(result))
117+
return result
118+
119+
def _set_gpio_mode_in(self, lflags=0):
120+
"""
121+
claim a gpio as input, or modify the flags (PULL_UP, PULL_DOWN, ... )
122+
"""
123+
# This gpio_free may seem redundant, but is required when
124+
# changing the line-flags of an already acquired input line
125+
try:
126+
lgpio.gpio_free(CHIP, self.id)
127+
except lgpio.error:
128+
pass
129+
Pin._check_result(lgpio.gpio_claim_input(CHIP, self.id, lFlags=lflags))

0 commit comments

Comments
 (0)