the c version is here: bb.org-overlays/tools/pmunts_muntsos at master · beagleboard/bb.org-overlays · GitHub
the shell version is here: bb.org-overlays/tools/beaglebone-universal-io at master · beagleboard/bb.org-overlays · GitHub
Regards
the c version is here: bb.org-overlays/tools/pmunts_muntsos at master · beagleboard/bb.org-overlays · GitHub
the shell version is here: bb.org-overlays/tools/beaglebone-universal-io at master · beagleboard/bb.org-overlays · GitHub
Regards
Thank you, Robert.
I’ll try it out tomorrow.
Best,
Ilan
@RobertCNelson any chance you have an example of a device tree overlay for the Beaglebone Black that can configure the direction and pull up state for GPIO pins (on Bookworm with kernel 6.6)?
Everything you could ever want to know is in here:
I’ve spent quite a bit of time sifting through that repository. There were quite a few false leads when looking at the ocp pinmux configurations. I eventually found that the bone-pinmux-helper that all of these relied on was removed from the kernel (which I assume is why config-pin doesn’t work any more). That’s when I reached out to Robert for a simple example.
Anyway, I took a fresh look today and noticed that a number of the overlays use gpio-leds for configuring outputs and gpio-keys for configuring inputs and was able to get those working. If anyone has any concerns about doing it this way, I’m all ears. I also noticed that none of the overlays use the helpful macros defined in #include <dt-bindings/board/am335x-bone-pins.h>, but I was determined to use them.
This is what I was able to put together. I hope it helps someone.
First clone the device trees repo (I imagine you want to select the branch that goes with your kernel version):
git clone --single-branch --branch v6.6.x-Beagle https://github.com/beagleboard/BeagleBoard-DeviceTrees.git
Put this in BeagleBoard-DeviceTrees/src/arm/overlays/SIMPLE-GPIO.dtso (or name it what you’d like).
/dts-v1/;
/plugin/;
#include <dt-bindings/pinctrl/am33xx.h>
#include <dt-bindings/board/am335x-bone-pins.h>
#include <dt-bindings/pinctrl/omap.h>
#include <dt-bindings/gpio/gpio.h>
/*
* Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/
*/
&{/chosen} {
overlays {
SIMPLE-GPIO = __TIMESTAMP__;
};
};
/* Define the pin modes here, inputs in the input section and outputs in the output section, setting pull ups/pull downs how you like */
&am33xx_pinmux {
my_input_pins: my_input_pins {
pinctrl-single,pins = <
/* PIN_INPUT, PIN_INPUT_PULLDOWN or PIN_INPUT_PULLUP */
P8_14(PIN_INPUT_PULLUP | MUX_MODE7)
>;
};
my_output_pins: my_output_pins {
pinctrl-single,pins = <
/* PIN_OUTPUT, PIN_OUTPUT_PULLDOWN or PIN_OUTPUT_PULLUP are all options, but these
don't seem to matter, use the default-state when defining the leds below */
P8_17(PIN_OUTPUT | MUX_MODE7)
>;
};
};
&{/} {
gpio-leds {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&my_output_pins>;
/* Define each input here with a label and its gpio, see dt-bindings/board/am335x-bone-pins.h for all the definitions,
but they all follow the gpio_P8or9_# pattern.*/
my-led {
label = "my-led";
gpios = <gpio_P8_17 GPIO_ACTIVE_HIGH>;
default-state = "on"; /* on, off, or keep */
};
};
gpio-keys {
compatible = "gpio-keys";
pinctrl-names = "default";
pinctrl-0 = <&my_input_pins>;
/* Define each output here with a label and its gpio, see dt-bindings/board/am335x-bone-pins.h for all the definitions,
but they all follow the gpio_P8or9_# pattern.*/
my-button {
label = "my-button";
gpios = <gpio_P8_14 GPIO_ACTIVE_HIGH>;
linux,code = <100>; /* key code that is emitted with a key event */
debounce_interval = <0>; /* time in ms to debounce the input */
gpio-key;
};
};
};
/* With this setup, after booting you can run gpioinfo and you'll see your labels defined next to the GPIOs you defined with
the proper direction set */
Compile, install and reboot
cd BeagleBoard-DeviceTrees
make
sudo make install
sudo reboot
On reboot:
gpioinfo
Will output your labels next to the pins used and whether they are configured as inputs or outputs.
line 26: "P8_14" "my-button" input active-high [used]
line 27: "P8_17" "my-led" output active-high [used]
I also wanted to note that most people likely won’t want to do it this way. I want to do it this way because I use low level GPIO drivers specific to the Beaglebone Black that get and set the GPIO pins using direct memory access through /dev/mem and I’d like to avoid messing with libgpiod if I can just have the pins set up the way I need them at boot.
I do have concerns about all the key events that could get generated on a high frequency input (such as from a hall sensor on a motor that will trigger once or multiple times per revolution of the motor). I’m not sure how much of a performance impact that will have.
So, @pocketnc_john ,
No external kernel modules? This is just DTS based and informs the kernel after a build of the DTS that the files are listed and ready for use?
Seth
It uses the gpio-keys and gpio-leds kernel drivers, but no I’m not using any external drivers. The gpio inputs will generate keyboard events and the leds can be driven by using /sys/class/leds/name-of-led:
echo 0 > /sys/class/leds/my-led/brightness
echo 1 > /sys/class/leds/my-led/brightness
I’m still wondering if there’s a kernel driver out there that doesn’t necessarily claim the pins so libgpiod could still be used to get/set them, but are configured a certain way at boot.
I’ve also tried out the libgpiod (1.6.3, the API changes in version 2) route and wrote this Python script that will configure them. I haven’t had much luck configuring the pins as inputs. Instead, I configure them as outputs and set the value high or low based on whether we want them to be pulled up or down.
#!/usr/bin/env python3
import time
import gpiod
import os
def configure_pins(map):
lines = []
for (p, (dir,pull_up_state)) in map.items():
line = gpiod.find_line(p)
try:
# This doesn't seem to work with inputs
# line.request("my-application", dir, pull_up_state)
# Seems like we have to set them to outputs and set a value based on the pull up state
line.request("my-application", gpiod.LINE_REQ_DIR_OUT, pull_up_state)
if dir == gpiod.LINE_REQ_DIR_IN:
if pull_up_state == gpiod.LINE_REQ_FLAG_BIAS_PULL_UP:
line.set_value(1)
elif pull_up_state == gpiod.LINE_REQ_FLAG_BIAS_PULL_DOWN:
line.set_value(0)
lines.append(line)
except:
print(f"Failed to request pin {p}")
return lines
pin_map = {
"P8_14": (gpiod.LINE_REQ_DIR_IN, gpiod.LINE_REQ_FLAG_BIAS_PULL_UP),
"P8_17": (gpiod.LINE_REQ_DIR_OUT, gpiod.LINE_REQ_FLAG_BIAS_DISABLE),
}
lines = configure_pins(pin_map)
while True:
time.sleep(10000)
#include <gpiod.h>
#include <stdio.h>
#include <unistd.h>
#ifndef CONSUMER
#define CONSUMER "Consumer"
#endif
int main(int argc, char **argv)
{
char *chipname = "gpiochip0";
unsigned int line_num = 24; // GPIO Pin #24
int val;
struct gpiod_chip *chip;
struct gpiod_line *line;
int i, ret;
chip = gpiod_chip_open_by_name(chipname);
if (!chip) {
perror("Open chip failed\n");
goto end;
}
line = gpiod_chip_get_line(chip, line_num);
if (!line) {
perror("Get line failed\n");
goto close_chip;
}
ret = gpiod_line_request_input(line, CONSUMER);
if (ret < 0) {
perror("Request line as input failed\n");
goto release_line;
}
/* Read input 20 times */
for (i = 20; i > 0; i--) {
val = gpiod_line_get_value(line);
if (val < 0) {
perror("Read line input failed\n");
goto release_line;
}
printf("Intput %d on line #%u\n", val, line_num);
sleep(1);
}
release_line:
gpiod_line_release(line);
close_chip:
gpiod_chip_close(chip);
end:
return 0;
}
That source is a bit older but I do not think that gpiod for C/C++ or python3 has changed all that much since it was written.
Use the consumer line if all else fails for input… I do not think they are tied to one another but consumer is needed I think.
Seth
P.S. From the docs, I found this short on it: lines.request(consumer='pushbutton.py', type=gpiod.LINE_REQ_DIR_IN).
That should do it for input.
So, instead of this like that which is why I had trouble with input, I used type= instead of just plain, ole gpiod.LINE_REQ_FLAG_BIAS_PULL_UP.
Yes, make sure your version of python and the bindings are correct for that v2.0. That will have you chasing your tail, because some of the differences will build and others will break. I don’t recall the exact version matches, it might be on their website.
Depending on how many revolutions we’re talking about,
you might or might not need to take a closer look at the eCAP module, section 15.3.
Thats a really good question. I haven’t tried to go beyond 5.10 because of the dreaded “eMMC killer patch” that Robert has been tracking down, but it stands to reason that it is somehow possible.
The trick is to get the PINMUX settings registered, without assigning the PAD to any specific device driver, like the gpio-leds f. ex.
I wonder if it’s possible to write a similar overlay for u-boot to use for setting the PINMUX registers, even before the kernel gets loaded in. @RobertCNelson, what do you think?
Here is another option with a device tree overlay, but requires more knowledge about the specific GPIO bank and line number. I still am unable to properly read from the button when I configure it as an input with a pull up. gpioinfo will show that it is configured properly, but when I try to read that pin it is always high, even when I’m pushing the button. There are two device tree overlays below, one that I would expect to work (but doesn’t), when I set P8.14 as an input pull up. The other does work, but P8.14 is configured as an output-high and I am able to read the button as high when not pressed and low when pressed (the button is wired to ground when pressed).
/dts-v1/;
/plugin/;
#include <dt-bindings/gpio/gpio.h>
/*
* Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/
*/
&{/chosen} {
overlays {
SIMPLE-GPIO-HOG = __TIMESTAMP__;
};
};
&gpio0 {
my-led {
gpio-hog;
gpios = <27 0>;
output-high;
line-name = "my-led";
};
my-button {
gpio-hog;
gpios = <26 GPIO_PULL_UP>;
input;
line-name = "my-button";
};
};
Looks right in gpioinfo output but I’m unable to read anything but a low value:
line 26: "P8_14" "my-button" input active-high [used pull-up]
line 27: "P8_17" "my-led" output active-high [used]
This one works, but both the LED and button are configured as outputs (I also had to do this workaround when using libgpiod in Python, see previous post):
/dts-v1/;
/plugin/;
/*
* Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/
*/
&{/chosen} {
overlays {
SIMPLE-GPIO-HOG = __TIMESTAMP__;
};
};
&gpio0 {
my-led {
gpio-hog;
gpios = <27 0>;
output-high;
line-name = "my-led";
};
my-button {
gpio-hog;
gpios = <26 0>;
output-high;
line-name = "my-button";
};
};
gpioinfo output reflects how its configured, but now I can properly read the button as low when it is pressed.
line 26: "P8_14" "my-button" output active-high [used]
line 27: "P8_17" "my-led" output active-high [used]
Since those pins are claimed, I can’t get/set them with libgpiod. I’ve stripped down the driver I use to read and write to pins directly. This program will read the P8.14 pin state:
read_button.c:
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#define GPIO_SIZE 0x2000
#define GPIO_SET_OFFSET 0x194
#define GPIO_CLR_OFFSET 0x190
#define GPIO_DATA_OFFSET 0x138
#define GPIO0 0x44E07000
#define P8_14_LINE 26
int main(int argc, char** argv) {
int fd = open("/dev/mem", O_RDWR);
void* port_addr = mmap(0, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO0);
close(fd);
uint32_t *set_addr = port_addr + GPIO_SET_OFFSET;
uint32_t *clr_addr = port_addr + GPIO_CLR_OFFSET;
uint32_t *data_addr = port_addr + GPIO_DATA_OFFSET;
uint32_t data = *(data_addr);
uint32_t p8_14_value = ((data & (1 << P8_14_LINE)) >> P8_14_LINE);
printf("%d\n", p8_14_value);
return 0;
}
Compile it like so:
gcc -o read_button read_button.c
Running requires elevated permissions:
sudo ./read_button
It will print out a 1 if the button is not pressed (it’s properly pulled up) and 0 if the button is pressed (pin is shorted to ground) when it is configured as an output-high, but not when configured as an input with a pull up. Any ideas what’s going on there?