Seit geraumer Zeit liegt ein Raspi 3 ganz einsam und verstaubt auf meinem Schreibtisch. Es ist an der Zeit, seinen gelangweilten Prozessor in Schwung zu bringen! Wie fängt man an? Natürlich mit einer LED-Bliki-App und als Special mit eigenen Linux-Treiber. Okay, ist nicht weltbewegend, aber als „Hello World App“ akzeptabel.
Inhalt und Ausblick
In diesem Beitrag gebe ich einen kurzen Überblick, wie man einen Linux-Treiber erstellt und auf einen Raspi 3 anwendet. In den folgenden Beiträgen werden ein Bustreiber und eine Qt-GUI mit Threads erstellt.
Breadboard mit Schaltung
Die Raspi-Pins sind auf ein Breadboard herausgeführt. Auf dem Breadboard befindet sich eine LED und ein Schalter nebst Entprellung. Der Taster-Eingang liegt auf Pin 5 (Breadboard: 105) und LED-Ausgang liegt auf Pin 4 (Breadboard: 104).

Linux aktualisieren
Zuerst muss der Linux-Software aktualisiert werden:
pi@raspberrypi:~ $ sudo apt-get update
pi@raspberrypi:~ $ sudo apt-get uprade
Linux-Headers sind passend zum neusten Kernel (aktuell 4.14.34-v7+) down-zu-loaden und zu überprüfen:
pi@raspberrypi:~ $ sudo apt-get install raspberrypi-kernel-headers
pi@raspberrypi:~ $ uname -r
Treiber erstellen
Nun wird die Treiberdatei led_device_driver.c erstellt. Der Treiber beinhaltet die Treiberfunktionen led_device_open(), led_device_close(), led_device_read() und led_device_write() .
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#define DEVICE_NAME "led_device"
#define CLASS_NAME "chardrv"
static dev_t first; // Globale Variable: device number
static struct cdev c_dev; // Globale Variable: character device structure
static struct class *cl; // Globale Variable: device class
static int led_device_open(struct inode *inode_str, struct file *file_str)
{
int err;
printk("led_device_open() \n");
// Pin 4 belegen
err = gpio_request(4, "rpi-gpio-4");
if(err)
{
printk("request failed %d : rpi-gpio-4 \n", err);
return -1;
}
// Pin als Output festlegen
err = gpio_direction_output(4, 0);
if(err)
{
printk("gpio_direction_output failed %d : rpi-gpio-4 \n", err);
gpio_free(4);
return -1;
}
//Pin 5 belegen
err = gpio_request(5, "rpi-gpio-5");
if(err)
{
printk("request failed %d : rpi-gpio-5 \n", err);
return -1;
}
// Pin 5 aus Input festlegen
err = gpio_direction_input(5);
if(err)
{
printk("gpio_direction_output failed %d : rpi-gpio-5 \n", err);
gpio_free(5);
return -1;
}
return 0;
}
static int led_device_close(struct inode *inode_str, struct file *file_str)
{
printk("led_device_close() \n");
//Pins freigeben
gpio_free(4);
gpio_free(5);
return 0;
}
static ssize_t led_device_read( struct file *file_str,
char __user *user,
size_t size,
loff_t *offset)
{
unsigned long not_copied, copied;
size_t delta_copied = 0;
u32 value = 0;
printk("led_device_read() \n");
//Input 5
value = gpio_get_value(5);
copied = min(size, sizeof(value));
// Pin-Wert einlesen
not_copied = copy_to_user(user, &value, copied);
delta_copied = copied - not_copied;
return delta_copied;
}
static ssize_t led_device_write( struct file *file_str,
const char __user *user,
size_t size,
loff_t *offset)
{
unsigned long not_copied, copied;
size_t delta_copied;
u32 value = 0;
printk("led_device_write() \n");
copied = min(size, sizeof(value));
// Pin-Wert setzen
not_copied = copy_from_user(&value, user, copied);
if (value == 0 )
gpio_set_value(4,0);
else
gpio_set_value(4,1);
delta_copied = copied - not_copied;
return delta_copied;
}
Nun die Funktionen den Systemcalls zuweisen:
static struct file_operations hellomike_fops =
{
.owner = THIS_MODULE,
.open = led_device_open,
.release = led_device_close,
.write = led_device_write,
.read = led_device_read,
};
Der Treiber wird mit der Funktion led_device_init() geladen und mit der Funktion led_device_exit() entladen, dazu müssen die beiden Funktionen jeweils module_init() und module_exit() zugewiesen werden. Wichtig! Die richtige Lizenz auswählen.
static int __init led_device_init(void)
{
printk("led_device_init() \n");
// Vom Kernel wird eine Grätenummer vergeben und zugewiesen
// Für Embedded Geräte kann alternativ mit MKDEV() eine feste Gerätenummer vergeben
// und register_chrdev_region() zugewiesen werden.
// Bspw. MKDEV(0,248): Gerätenummer wird aus beiden Nummern gebildet.
if (alloc_chrdev_region(&first, 0, 1, DEVICE_NAME) < 0)
{
return -1;
}
//Erzeugt: struct class structure
if ((cl = class_create(THIS_MODULE, CLASS_NAME)) == NULL)
{
unregister_chrdev_region(first, 1);
return -1;
}
// In Sys-Filsystem eintragen und Gerätedatei erzeugen.
if (device_create(cl, NULL, first, NULL, DEVICE_NAME) == NULL)
{
class_destroy(cl);
unregister_chrdev_region(first, 1);
return -1;
}
cdev_init(&c_dev, &hellomike_fops);
// Treiber beim Kernel anmelden
if (cdev_add(&c_dev, first, 1) == -1)
{
device_destroy(cl, first);
class_destroy(cl);
unregister_chrdev_region(first, 1);
return -1;
}
return 0;
}
static void __exit led_device_exit(void)
{
printk("led_device_exit() \n");
// Abmelden und freigeben
cdev_del(&c_dev);
device_destroy(cl, first);
class_destroy(cl);
unregister_chrdev_region(first, 1);
}
module_init(led_device_init);
module_exit(led_device_exit);
MODULE_LICENSE("GPL");
Makefile erstellen:
obj-m += led_device_driver.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Treiber erstellen mit make
pi@raspberrypi:~/embedded/driver/led_device_driver $ make
laden:
pi@raspberrypi:~/embedded/driver/led_device_driver $ sudo insmod led_device_driver.ko
Zugriff für andere ermöglichen:
pi@raspberrypi:~/embedded/driver/led_device_driver $ sudo chmod 777 /dev/led_device
Ist der Treiber geladen?
pi@raspberrypi:~/embedded/driver/led_device_driver $ cat /proc/devices
Treiber testen
pi@raspberrypi:~/embedded/driver/led_device_driver $ echo "test" > /dev/led_device
pi@raspberrypi:~/embedded/driver/led_device_driver $ dmesg
Applikation
Nun wird eine kleine Applikation in C erstellt.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#define BUFFER_LENGTH 1
static char switch_value[BUFFER_LENGTH];
int main(int argc, char **argv, char **envp )
{
int fd_i, led_value, switch_ret, count_i;
struct timespec timesleep_str;
switch_value[0] = 0;
// Treiben für Lesen und Schreiben öffnen
fd_i = open("/dev/led_device", O_RDWR);
if(fd_i < 0)
{
printf("Kann Treiber led_device nicht öffnen :-( !\n");
return -1;
}
// Aktuellen Wert des Tasters abfragen
switch_ret = read(fd_i, &switch_value, BUFFER_LENGTH);
if(switch_value[0] == 1)
{
printf("Ausgeschaltet !\n");
}
else
{
printf("Eingeschaltet !\n");
}
if(switch_ret < 0)
{
printf("Keine gelesende Daten !\n");
return -1;
}
// Die LED soll mit 0,5s blinken
timesleep_str.tv_sec = 0;
timesleep_str.tv_nsec = 500000000;
// Blinken für 2min oder bist der Taster betätigt wurde
while(count_i < 120 && switch_value[0] == 0)
{
// Status des Tasters abfragen
switch_ret = read(fd_i, &switch_value, BUFFER_LENGTH);
led_value = 1;
// LED einschalten
write(fd_i, &led_value, sizeof(led_value));
clock_nanosleep(CLOCK_MONOTONIC, 0, ×leep_str, NULL);
led_value = 0;
//LED ausschalten
write(fd_i, &led_value, sizeof(led_value));
clock_nanosleep(CLOCK_MONOTONIC, 0, ×leep_str, NULL);
count_i++;
}
Programm kompelieren:
pi@raspberrypi:~/embedded/apps/led-device-app $ gcc -I . -o led_device_app led_device_app.c
Programm ausführen
pi@raspberrypi:~/embedded/apps/led-device-app $ ./led_device_app
Die LED leuchtet für 2 Minuten oder bis der Taster betätigt wird.
