The GPIO port is one of the main ways to establish connection between the board and external circuit. The process I followed to setup such connnection with a Linux kernel module is documented here.
Beaglebone Black, PC running on Linux system (Ubuntu ver 20.04)
Serial to USB cable, USB type A to mini B cable
Jump wires, LED, transistor (BC547), resistors

The module is cross compiled on the host. mydev.c:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/device.h>
#define NUM 48 // P9_15 GPIO1_16
#define SIZE 10
#define LABEL "P9_15"
static dev_t dev_id;
static struct cdev *mydev;
static struct class *mydev_class;
static char buff[SIZE];
ssize_t mydev_write(struct file *file, const char __user *data, size_t count, loff_t *loff) {
printk("mydev_write\n");
int state = 1;
if(count >= SIZE) {
count = SIZE;
}
state = copy_from_user(buff, data, count);
if(buff[0] == '0') {
gpio_set_value(NUM, 0);
} else {
gpio_set_value(NUM, 1);
}
return 0;
}
int mydev_open(struct inode *inode, struct file *file) {
printk("mydev_open\n");
return 0;
}
static struct file_operations mydev_fops = {
.owner = THIS_MODULE,
.open = mydev_open,
.write = mydev_write,
};
static int __init mydev_init(void) {
if(!gpio_is_valid(NUM)) {
printk("Invalid GPIO number.\n");
return -ENODEV;
}
gpio_request(NUM, LABEL);
gpio_direction_output(NUM, 0);
alloc_chrdev_region(&dev_id, 1, 1, "mydev");
mydev = cdev_alloc();
cdev_init(mydev, &mydev_fops);
cdev_add(mydev, dev_id, 1);
printk("major: %d; minor: %d\n", MAJOR(dev_id), MINOR(dev_id));
mydev_class = class_create(THIS_MODULE, "mydev");
device_create(mydev_class, NULL, dev_id, NULL, "mydev");
return 0;
}
static void __exit mydev_exit(void) {
gpio_set_value(NUM, 0);
gpio_free(NUM);
device_destroy(mydev_class, dev_id);
class_destroy(mydev_class);
cdev_del(mydev);
kfree(mydev);
unregister_chrdev_region(dev_id, 1);
printk("mydev unloaded\n");
}
module_init(mydev_init);
module_exit(mydev_exit);
MODULE_LICENSE("GPL");
Makefile:
KERN_DIR := /home/usr/git/linux-5.10.168-ti-r71/linux
PWD := $(shell pwd)
obj-m := mydev.o
all:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KERN_DIR) M=$(PWD) modules
clean:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KERN_DIR) M=$(PWD) clean
mydev_test.c:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
char val[2];
val[0] = '1';
val[1] = '\0';
int fd = open("/dev/mydev", O_RDWR);
printf("Open\n");
write(fd, &val, sizeof(val));
printf("Write\n");
close(fd);
printf("Closed\n");
return 0;
}
It is compiled on the board with gcc:
gcc mydev_test.c -o mydev_test
User application needs to be called with sudo:
sudo ./mydev_test
The LED will be turned on after running this command.
- http://derekmolloy.ie/kernel-gpio-programming-buttons-and-leds
- https://docs.kernel.org/driver-api/gpio/legacy.html
- https://www.farnell.com/datasheets/410427.pdf
- https://www.smartdraw.com/circuit-diagram/schematic-diagram-software.htm
- https://vadl.github.io/beagleboneblack/2016/07/29/setting-up-bbb-gpio
- https://cdn-shop.adafruit.com/datasheets/BBB_SRM.pdf