-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Edge detection does not work on Raspbian Bookworm #62
Comments
I think the problem is that starting with Bookworm sysfs GPIO has been removed. It might affect other distributions as well. At least all Raspberries are affected. |
Can you test if b69b28c fixes the problem? If so, I'll do a release. |
On the Pi 2 I'm getting a lot of errors now:
Not sure if it is related but header-list no longer returns functions:
|
@fischerman host/gpioioctl/example_test.go |
I want to see this fixed so I ran a gpio out on my RPI 5, using the branch you provided @maruel pi@worker-pi-1:~/GraftXL/cmd/run3 $ go get periph.io/x/host/v3@b69b28c4e40f9f233222c9d7ea610ef0fcc8a54b
go: downloading periph.io/x/host/v3 v3.8.3-0.20240918234808-b69b28c4e40f which quickly died: 2024/10/08 20:13:09 chip gpiochip4 gpioreg.Register(line) {
"Line": 47,
"Name": "2712_WAKE",
"Consumer": "",
"Direction": "NotSet",
"Pull": "PullNoChange",
"Edges": "NoEdge"
} returned gpioreg: can't register pin "2712_WAKE" twice; already registered as "{\n \"Line\": 8,\n \"Name\": \"2712_WAKE\",\n \"Consumer\": \"\",\n \"Direction\": \"NotSet\",\n \"Pull\": \"PullNoChange\",\n \"Edges\": \"NoEdge\"\n}"
Failed to set pin as output: bcm283x-gpio (GPIO17): subsystem gpiomem not initialized and sysfs not accessible Using this example code for my motor that works on the RPI4: package main
import (
"fmt"
"time"
"periph.io/x/conn/v3/gpio"
"periph.io/x/host/v3"
"periph.io/x/host/v3/rpi"
)
func main() {
if _, err := host.Init(); err != nil {
fmt.Println("Failed to initialize periph:", err)
return
}
stepPin := rpi.P1_11 // Use GPIO17
// Set the pin as an output
if err := stepPin.Out(gpio.Low); err != nil {
fmt.Println("Failed to set pin as output:", err)
return
}
pulseCount := 800
pulseDuration := 1000 * time.Microsecond
// Generate pulses
fmt.Printf("Sending %d pulses...\n", pulseCount)
for i := 0; i < pulseCount; i++ {
if err := stepPin.Out(gpio.High); err != nil {
fmt.Println("Failed to set pin high:", err)
return
}
time.Sleep(pulseDuration / 2)
if err := stepPin.Out(gpio.Low); err != nil {
fmt.Println("Failed to set pin low:", err)
return
}
time.Sleep(pulseDuration / 2)
}
fmt.Println("Finished sending pulses.")
} I hope this helps in someway! |
There are 4 GPIO chips on the Pi 5, and they all export a pin named "2712_WAKE". This causes the error you're seeing. Take the return out of the host.Init() statement and see what it does. |
Did that, removed the return.
|
Here is a basic program using the gpioioctl code that demonstrates edge detection. It assumes you have a jumper connecting GPIO2 and GPIO 10. This was run on a Raspberry Pi 5: gsexton@raspberrypi:~ $ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 12 (bookworm)
Release: 12
Codename: bookworm
gsexton@raspberrypi:~ $ uname -a
Linux raspberrypi 6.6.45-v8-16k+ #1791 SMP PREEMPT Tue Aug 13 12:52:29 BST 2024 aarch64 GNU/Linux To build this, you'll need the commit that has gpioioctl in it: gsexton@raspberrypi:~ $ go get periph.io/x/host/v3@b69b28c4e40f9f233222c9d7ea610ef0fcc8a54b main.go package main
import (
"fmt"
"time"
"periph.io/x/conn/v3/gpio"
"periph.io/x/host/v3"
"periph.io/x/host/v3/gpioioctl"
)
func main() {
if _, err := host.Init(); err != nil {
fmt.Println("Failed to initialize periph:", err)
// return
}
gpiochip:=gpioioctl.Chips[0]
outPin:=gpiochip.ByName("GPIO2")
inPin:=gpiochip.ByName("GPIO10")
inPin.In(gpio.PullDown,gpio.RisingEdge)
fmt.Println("inPin=",inPin)
fmt.Println("outPin=",outPin)
l:=gpio.Low
go func() {
for {
l=!l
fmt.Println("setting outPin to ",l)
outPin.Out(l)
time.Sleep(1000 * time.Millisecond)
}
}()
for {
if inPin.WaitForEdge(0){
fmt.Println("Received edge")
} else {
fmt.Println("WaitForEdge() unblocked without receiving edge.")
}
}
} Sample Output:
|
@gsexton Your example works on my RPi2. I compared it to my program and what seems to break it is:
|
@fischerman I've looked into this. The problem is the bcm283x driver is running after the gpioioctl register code. That driver explicitly unregisters the gpioioctl registered GPIO pins values and supplies non-working sysfs based pins, causing things to break (line 1395 of host/bcm283x/gpio.go). @maruel do you have any ideas on the correct fix? |
bcm283x needs to condition on which driver is loaded and act accordingly. |
Is this then solved by #63 ? |
@damdo Could you test this on your configuration using the current head? @fischerman and @Evert-Arends Could you test this and see if your issue is resolved? |
I've tested the following program with commit package main
import (
"fmt"
"log"
"time"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/gpio/gpioreg"
"periph.io/x/host/v3"
)
func main() {
if _, err := host.Init(); err != nil {
fmt.Println("Failed to initialize periph:", err)
// return
}
outPin := gpioreg.ByName("GPIO2")
if outPin == nil {
log.Fatal("Failed to find GPIO2")
}
inPin := gpioreg.ByName("GPIO10")
if inPin == nil {
log.Fatal("Failed to find GPIO10")
}
inPin.In(gpio.PullDown, gpio.RisingEdge)
fmt.Println("inPin=", inPin)
fmt.Println("outPin=", outPin)
l := gpio.Low
go func() {
for {
l = !l
fmt.Println("setting outPin to ", l)
outPin.Out(l)
time.Sleep(1000 * time.Millisecond)
}
}()
for {
if inPin.WaitForEdge(0) {
fmt.Println("Received edge")
} else {
fmt.Println("WaitForEdge() unblocked without receiving edge.")
}
}
} It works. However, I still see a lot of warnings:
|
@fischerman These errors are not supported to be there, that's weird. |
I guess we need to discuss how to handle this. There's a lot going on. Today I noticed that in a pi5 I have running, there's a symlink for /dev/gpiochip4 -> /dev/gpiochip0. That's probably causing most of the duplication. There's some discussion of that here: http://git.munts.com/muntsos/doc/AppNote11-link-gpiochip.pdf The pi5 exports 5 total GPIO chips. Some of the lines have duplicate names. I see two chips (not counting the symlink) that have 2712_WAKE lines. So, we can't depend upon the exported line/pin names to be unique. Looking at the output of the chips dumped (now you know why I went wild on my String() implementation :) ). I can see that perhaps the parser should deterministically make a chip with label "pinctrl" the first one to be processed. Some of the chip labels appear to be pointers. For example: "Label": "gpio-brcmstb@107d508500" btw. So, our options are:
Alternatively, we can stick everything that is not labeled pinctrl into the registry with as label+line. -- It's really sort of a mess. My recommendation is Option 2, and add code to handle duplicate chips caused by symlinks. Examine the /dev/ entry and skip if if it's a symlink. This is unfortunately a Pi specific answer. For a non-pi specific answer, my recommendation is: 1) Do a chip named "pinctrl-" first if found, and the rest in lexical order of device name. Skip symlinks. If a name is a collision, prefix it with the chip label. I've attached a copy of the chip output from a pi5 running the lastest kernel if you'd like to look at it. Let me know what you would like and I'll create another PR to implement it. |
Option 2 sounds fine with me. Thanks! |
Describe the bug
The pin cannot be associated with a sysfs pin.
To Reproduce
Steps to reproduce the behavior:
Expected behavior
No error on
In
.Platform:
Additional context
I'm not aware of any prerequisites that must be met. I tried running the program as root as well, but I get the same error.
The text was updated successfully, but these errors were encountered: