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:
1 |
pi@raspberrypi:~ $ sudo apt-get update |
1 |
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:
1 |
pi@raspberrypi:~ $ sudo apt-get install raspberrypi-kernel-headers |
1 |
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() .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
#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:
1 2 3 4 5 6 7 8 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
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:
1 2 3 4 5 6 7 |
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
1 |
pi@raspberrypi:~/embedded/driver/led_device_driver $ make |
laden:
1 |
pi@raspberrypi:~/embedded/driver/led_device_driver $ sudo insmod led_device_driver.ko |
Zugriff für andere ermöglichen:
1 |
pi@raspberrypi:~/embedded/driver/led_device_driver $ sudo chmod 777 /dev/led_device |
Ist der Treiber geladen?
1 |
pi@raspberrypi:~/embedded/driver/led_device_driver $ cat /proc/devices |
Treiber testen
1 |
pi@raspberrypi:~/embedded/driver/led_device_driver $ echo "test" > /dev/led_device |
1 |
pi@raspberrypi:~/embedded/driver/led_device_driver $ dmesg |
Applikation
Nun wird eine kleine Applikation in C erstellt.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
#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:
1 |
pi@raspberrypi:~/embedded/apps/led-device-app $ gcc -I . -o led_device_app led_device_app.c |
Programm ausführen
1 |
pi@raspberrypi:~/embedded/apps/led-device-app $ ./led_device_app |
Die LED leuchtet für 2 Minuten oder bis der Taster betätigt wird.