Skip to content

Instantly share code, notes, and snippets.

@Zixuan-Qiao
Created March 21, 2024 19:45
Show Gist options
  • Save Zixuan-Qiao/e0e307d2a95f75218553354f03622fe4 to your computer and use it in GitHub Desktop.
Save Zixuan-Qiao/e0e307d2a95f75218553354f03622fe4 to your computer and use it in GitHub Desktop.
Testing GPIO ports on Beaglebone Black

Beaglebone Black - Testing GPIO Ports with Linux Kernel Module

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.

Required Hardware

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

Schematic

1

Linux Kernel Module

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

User Space Program

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.

References

  1. http://derekmolloy.ie/kernel-gpio-programming-buttons-and-leds
  2. https://docs.kernel.org/driver-api/gpio/legacy.html
  3. https://www.farnell.com/datasheets/410427.pdf
  4. https://www.smartdraw.com/circuit-diagram/schematic-diagram-software.htm
  5. https://vadl.github.io/beagleboneblack/2016/07/29/setting-up-bbb-gpio
  6. https://cdn-shop.adafruit.com/datasheets/BBB_SRM.pdf
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment