Um die 7-Segemntanzeige an einem Raspberry Pi zu betreiben müssen die GPIO-Pins (engl. GPIO – general purpose input/output) über den Treiber angesprochen und verwaltet werden dies geschieht im folgenden Abschnitt.
Als erstes müssen die Pins initialisiert werden:
```c
voidseven_gpio_init(void){
voidseven_gpio_init(void){//Initialisiert die GPIO-Pins (gpio_request)
printk(KERN_INFO"SEVENSEG: starting gpio...");
gpio_request(A1,"A1");
gpio_request(A2,"A2");
gpio_request(A3,"A3");
gpio_request(A4,"A4");
gpio_request(A5,"A5");
gpio_request(A6,"A6");
gpio_request(A7,"A7");
gpio_request(A8,"A8");
gpio_direction_output(A1,0);
gpio_direction_output(A2,0);
gpio_direction_output(A3,0);
gpio_direction_output(A4,0);
gpio_direction_output(A5,0);
gpio_direction_output(A6,0);
gpio_direction_output(A7,0);
gpio_direction_output(A8,0);
gpio_request(Sa,"Sa");
gpio_request(Sb,"Sb");
gpio_request(Sc,"Sc");
gpio_request(Sd,"Sd");
gpio_request(Se,"Se");
gpio_request(Sf,"Sf");
gpio_request(Sg,"Sg");
gpio_request(Sp,"Sp");
gpio_direction_output(Sa,0);//Legt fest das der Pin als Output-Pin ansprechbar ist (gpio_direction_output)
gpio_direction_output(Sb,0);
gpio_direction_output(Sc,0);
gpio_direction_output(Sd,0);
gpio_direction_output(Se,0);
gpio_direction_output(Sf,0);
gpio_direction_output(Sg,0);
gpio_direction_output(Sp,0);
printk(KERN_INFO"SEVENSEG: starting gpio done.");
}
```
Die GPIO-Pins müssen zuerst für das Modul reserviert werden, dies wird mit `gpio_request(unsigned int gpio, const char *label)` erreicht.
Um mit den Pins arbeiten zu können muss noch festgelegt werden, dass es Output-Pins sind was, mit `gpio_direction_output(unsigned int gpio, int value)` bewerkstelligt wird.
Um mit den Pins arbeiten zu können muss noch festgelegt werden, dass es Output-Pins sind, was mit `gpio_direction_output(unsigned int gpio, int value)` bewerkstelligt wird.
Wenn man das Modul entladen will müssen die GPIO-Pins wieder freigegeben werden was im folgenden Codeabschnitt durch `gpio_free(const int gpio)` realisiert wurde:
```c
voidseven_gpio_exit(void){//Beim Rausladen des LKM werden die Pins wieder freigegeben (gpio_free)
gpio_set_value(A5,1);//default alle Pins auf LOW gesetzt
gpio_set_value(A6,1);
gpio_set_value(A7,1);
gpio_set_value(A8,1);
gpio_set_value(Sa,1);
gpio_set_value(Sb,1);
gpio_set_value(Sc,1);
gpio_set_value(Sd,1);
gpio_set_value(Se,1);//default alle Pins auf LOW gesetzt
gpio_set_value(Sf,1);
gpio_set_value(Sg,1);
gpio_set_value(Sp,1);
break;
}
}
```
Nachdem die Funktion mit der gewünschten Ausgabe `seven_status(int display)` aufgerufen wurde, werden in der switch-case-Funktion die GPIO-Pins angesprochen
...
...
@@ -234,7 +302,7 @@ Quelle: [GPIO in the kernel] (https://lwn.net/Articles/532714/)
Um später mit dem Treiber im Kernel-Space kommunizieren zu können, wird ein Character Device Driver benötigt, der im Kernel-Space eine Node-Datei erstellt, auf die der User
mit `& echo "display" >> /dev/sevenseg` schreiben kann und das Modul die Eingabe verarbeitet.
Als erstes wird hierzu folgende globale Variablen und Strukturfunktionen:
Als erstes werden hierzu folgende globale Variablen und Strukturfunktionen erstellt:
```c
/* Globale Variablen für die Chracter-Device-Schnittstelle */
@@ -256,16 +324,16 @@ Man kann seine eigene Major Nummer bestimmen muss aber darauf achten das sie noc
`register_chrdev(unsigned int major, const char *name, struct file_operations *fops)` bei `unsigned int major` 0 ein und lässt sich dann durch die Rückgabe eine freie Major Nummer zuteilen
wie im folgenden Code-Abschnitt:
```c
intseven_dev_init(void)
intseven_dev_init(void)//Wird aufgerufen wenn das Modul geladen wird
{
Major=register_chrdev(0,DEVICE_NAME,&seven_fops);
Major=register_chrdev(0,DEVICE_NAME,&seven_fops);//Registrierung eines Chardevice
if(Major<0){
printk(KERN_ALERT"SEVENSEG: Registering char device failed with %d\n",Major);
returnMajor;
}
printk(KERN_INFO"SEVENSEG: sevenseg driver loaded with major %d\n",Major);
printk(KERN_INFO"SEVENSEG: >> $ mknod /dev/%s c %d 0\n",DEVICE_NAME,Major);//Info welche Nodedatei mit welchen Parametern erstellt werden soll
printk(KERN_INFO"SEVENSEG: >> $ mknod /dev/%s c %d 0\n",DEVICE_NAME,Major);//Info welche Nodedatei mit welchen parametern erstellt werden soll
msg=(char*)kmalloc(32,GFP_KERNEL);//Speicherreservierung fuer die Uebertragung von Kernelspace zu Userspace
if(msg!=NULL)
...
...
@@ -275,11 +343,11 @@ int seven_dev_init(void)
```
Mit der KERN_INFO lassen wir uns hier den genauen Pfad ausgeben in der die Node Datei erstellt werden soll in unserem Fall wird mit
> $ mknod /dev/sevenseg 257 0
> $ mknod /dev/sevenseg 243 0
eine Datei mit Majornummer 257 und Minor-Nummer 0 angelegt. Die Minor Nummer wird nur verwendet wenn ein Treiber mehrere Geräte ansprechen soll.
eine Datei mit Major Nummer 243 und Minor-Nummer 0 angelegt. Die Minor Nummer wird nur verwendet wenn ein Treiber mehrere Geräte ansprechen soll.
Zu guter letzt muss noch Speicher für die Übertragung des Puffers reserviert werden da die Eingaben in die Nodedatei nur indirekt ausgelesen werden können.
Zu guter Letzt muss noch Speicher für die Übertragung des Puffers reserviert werden da die Eingaben in die Node Datei nur indirekt ausgelesen werden können.
In der folgenden Funktion verwenden wir die bereits erstellte Struktur der write-Funktion:
Hier wird durch die Funktion `unregister_chrdev(unsigned int major, const char *name)` der Treiber abgemeldet in den Parametern wird die Majornummer und der DEVICE_NAME angegeben.
Hier wird durch die Funktion `unregister_chrdev(unsigned int major, const char *name)` der Treiber abgemeldet, in den Parametern wird die Major Nummer und der DEVICE_NAME angegeben.
Zu guter letzt müssen alle Initialisierungsfunktionen über die Modulinitialiesierung aufgerufen werden damit beim Laden des Treibers alle Funktionen garantiert sind:
Zu guter Letzt müssen alle Initialisierungsfunktionen über die Modulinitialisierung aufgerufen werden damit beim Laden des Treibers alle Funktionen garantiert sind:
module_init(seven_init);//Alle Funktionen in led_init werden im Modul geladen
module_exit(seven_exit);//Alle Funktionen in led_init werden entladen
```
Die Makros `module_init(seven_init)` und `module_exit(seven_exit)` ermöglichen es die Initialisierungs-Funktionen seperat in eigene Funktionen zu schreiben. Man muss beachten,
dass die Funktionen `seven_init()` und `seven_exit()` ein __init und __exit in der Deklaration haben und vor den Modulmakros aufgerufen werden müssen, da sonst Probleme beim Kompilieren entstehen.