diff --git a/i2c-scheduler-rr.excalidraw b/i2c-scheduler-rr.excalidraw index 9af1990..dc94992 100644 --- a/i2c-scheduler-rr.excalidraw +++ b/i2c-scheduler-rr.excalidraw @@ -3,6 +3,51 @@ "version": 2, "source": "https://excalidraw.com", "elements": [ + { + "id": "o0TImuY5NBuVnjNlXMAzk", + "type": "arrow", + "x": 1701.3300555241196, + "y": 300.032479332732, + "width": 1.3300555241196435, + "height": 739.967520667268, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b04", + "roundness": { + "type": 2 + }, + "seed": 1454586268, + "version": 70, + "versionNonce": 1445870372, + "isDeleted": false, + "boundElements": [], + "updated": 1768903438971, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -1.3300555241196435, + 739.967520667268 + ] + ], + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, { "id": "dFrrZb58NASi_ovlDPlr8", "type": "arrow", @@ -167,51 +212,6 @@ "autoResize": true, "lineHeight": 1.25 }, - { - "id": "o0TImuY5NBuVnjNlXMAzk", - "type": "arrow", - "x": 1701.3300555241196, - "y": 300.032479332732, - "width": 1.3300555241196435, - "height": 739.967520667268, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#b2f2bb", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b0B", - "roundness": { - "type": 2 - }, - "seed": 1454586268, - "version": 69, - "versionNonce": 1608574364, - "isDeleted": false, - "boundElements": [], - "updated": 1768867948553, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -1.3300555241196435, - 739.967520667268 - ] - ], - "startBinding": null, - "endBinding": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false - }, { "id": "-casMJtwPjypfg0mk5nHO", "type": "rectangle", @@ -834,8 +834,8 @@ "type": 3 }, "seed": 1744692380, - "version": 45, - "versionNonce": 871659940, + "version": 47, + "versionNonce": 766960036, "isDeleted": false, "boundElements": [ { @@ -843,7 +843,7 @@ "id": "PgDfZjA4Q7u8kt6vwTGzx" } ], - "updated": 1768867714260, + "updated": 1768903373256, "link": null, "locked": false }, @@ -906,8 +906,8 @@ "type": 3 }, "seed": 253023780, - "version": 104, - "versionNonce": 1629844764, + "version": 106, + "versionNonce": 1020116772, "isDeleted": false, "boundElements": [ { @@ -915,7 +915,7 @@ "id": "utn7zQfYBfbFTRgfoV5GA" } ], - "updated": 1768867894355, + "updated": 1768903372699, "link": null, "locked": false }, @@ -1013,11 +1013,11 @@ "type": 2 }, "seed": 1181892004, - "version": 163, - "versionNonce": 439755044, - "isDeleted": false, + "version": 164, + "versionNonce": 455788316, + "isDeleted": true, "boundElements": null, - "updated": 1768867969455, + "updated": 1768903375842, "link": null, "locked": false, "points": [ @@ -1095,16 +1095,16 @@ "type": 3 }, "seed": 1182313500, - "version": 59, - "versionNonce": 2068777508, - "isDeleted": false, + "version": 60, + "versionNonce": 1625923492, + "isDeleted": true, "boundElements": [ { "type": "text", "id": "DCXi27QHl9Zsqz5FCQgzM" } ], - "updated": 1768867500161, + "updated": 1768903353395, "link": null, "locked": false }, @@ -1128,11 +1128,11 @@ "index": "b0UV", "roundness": null, "seed": 1952001436, - "version": 3, - "versionNonce": 414597916, - "isDeleted": false, + "version": 4, + "versionNonce": 867998876, + "isDeleted": true, "boundElements": null, - "updated": 1768867500581, + "updated": 1768903353395, "link": null, "locked": false, "text": "1", @@ -1167,11 +1167,11 @@ "type": 2 }, "seed": 325594908, - "version": 185, - "versionNonce": 527280036, - "isDeleted": false, + "version": 186, + "versionNonce": 1776276380, + "isDeleted": true, "boundElements": null, - "updated": 1768867917738, + "updated": 1768903351390, "link": null, "locked": false, "points": [ @@ -1367,8 +1367,8 @@ { "id": "a1AK0dTA94waRfKIGtjcX", "type": "rectangle", - "x": 1406, - "y": 519, + "x": 1500, + "y": 520, "width": 80, "height": 80, "angle": 0, @@ -1386,8 +1386,8 @@ "type": 3 }, "seed": 804000548, - "version": 78, - "versionNonce": 116530980, + "version": 87, + "versionNonce": 990199460, "isDeleted": false, "boundElements": [ { @@ -1395,15 +1395,15 @@ "id": "TQknVVA73H8eY5J6Q8sd9" } ], - "updated": 1768867501892, + "updated": 1768903357536, "link": null, "locked": false }, { "id": "TQknVVA73H8eY5J6Q8sd9", "type": "text", - "x": 1436.1000003814697, - "y": 536.5, + "x": 1530.1000003814697, + "y": 537.5, "width": 19.799999237060547, "height": 45, "angle": 0, @@ -1419,11 +1419,11 @@ "index": "b0Z", "roundness": null, "seed": 1620658332, - "version": 3, - "versionNonce": 680017436, + "version": 12, + "versionNonce": 1554893348, "isDeleted": false, "boundElements": null, - "updated": 1768867502318, + "updated": 1768903357536, "link": null, "locked": false, "text": "2", @@ -1526,7 +1526,7 @@ { "id": "EXd8E-Umx_78sjBL4Ki6W", "type": "rectangle", - "x": 1400, + "x": 1500, "y": 720, "width": 80, "height": 80, @@ -1545,8 +1545,8 @@ "type": 3 }, "seed": 2140102940, - "version": 16, - "versionNonce": 1305571492, + "version": 21, + "versionNonce": 1730875164, "isDeleted": false, "boundElements": [ { @@ -1554,14 +1554,14 @@ "id": "y-9Stf17pe9fWn0kSmWVL" } ], - "updated": 1768867618260, + "updated": 1768903389485, "link": null, "locked": false }, { "id": "y-9Stf17pe9fWn0kSmWVL", "type": "text", - "x": 1430.1000003814697, + "x": 1530.1000003814697, "y": 737.5, "width": 19.799999237060547, "height": 45, @@ -1578,11 +1578,11 @@ "index": "b0cV", "roundness": null, "seed": 761306908, - "version": 3, - "versionNonce": 579937436, + "version": 8, + "versionNonce": 1035127708, "isDeleted": false, "boundElements": null, - "updated": 1768867618828, + "updated": 1768903389485, "link": null, "locked": false, "text": "2", @@ -1598,7 +1598,7 @@ { "id": "Yg-CjxTPXE94lHk3dKGUd", "type": "rectangle", - "x": 1300, + "x": 1400, "y": 720, "width": 80, "height": 80, @@ -1617,8 +1617,8 @@ "type": 3 }, "seed": 483230628, - "version": 14, - "versionNonce": 1058383268, + "version": 19, + "versionNonce": 287193244, "isDeleted": false, "boundElements": [ { @@ -1626,14 +1626,14 @@ "id": "Nx15pnUHSCimdfJ5cArXf" } ], - "updated": 1768867620980, + "updated": 1768903391770, "link": null, "locked": false }, { "id": "Nx15pnUHSCimdfJ5cArXf", "type": "text", - "x": 1330.1000003814697, + "x": 1430.1000003814697, "y": 737.5, "width": 19.799999237060547, "height": 45, @@ -1650,11 +1650,11 @@ "index": "b0dV", "roundness": null, "seed": 1757256220, - "version": 3, - "versionNonce": 137101212, + "version": 8, + "versionNonce": 1652868380, "isDeleted": false, "boundElements": null, - "updated": 1768867622323, + "updated": 1768903391770, "link": null, "locked": false, "text": "3", @@ -1689,16 +1689,16 @@ "type": 3 }, "seed": 511833380, - "version": 93, - "versionNonce": 76819364, - "isDeleted": false, + "version": 94, + "versionNonce": 315212316, + "isDeleted": true, "boundElements": [ { "type": "text", "id": "m6RMkBWOX7Oe1Yb5XJQKL" } ], - "updated": 1768867581029, + "updated": 1768903387547, "link": null, "locked": false }, @@ -1722,11 +1722,11 @@ "index": "b0f", "roundness": null, "seed": 1280290980, - "version": 18, - "versionNonce": 1339496228, - "isDeleted": false, + "version": 19, + "versionNonce": 692059556, + "isDeleted": true, "boundElements": [], - "updated": 1768867581029, + "updated": 1768903387547, "link": null, "locked": false, "text": "2", @@ -1796,11 +1796,11 @@ "type": 2 }, "seed": 1406049828, - "version": 149, - "versionNonce": 1677336092, - "isDeleted": false, + "version": 150, + "versionNonce": 1672236188, + "isDeleted": true, "boundElements": [], - "updated": 1768867910921, + "updated": 1768903382929, "link": null, "locked": false, "points": [ @@ -1913,11 +1913,11 @@ "type": 2 }, "seed": 966452508, - "version": 140, - "versionNonce": 1302865572, - "isDeleted": false, + "version": 141, + "versionNonce": 1517442724, + "isDeleted": true, "boundElements": null, - "updated": 1768867963536, + "updated": 1768903385286, "link": null, "locked": false, "points": [ @@ -1995,16 +1995,16 @@ "type": 3 }, "seed": 441688228, - "version": 49, - "versionNonce": 261033884, - "isDeleted": false, + "version": 50, + "versionNonce": 1106456092, + "isDeleted": true, "boundElements": [ { "type": "text", "id": "CXeyVA3UWQkIqE8L_SeKy" } ], - "updated": 1768867720892, + "updated": 1768903394836, "link": null, "locked": false }, @@ -2028,11 +2028,11 @@ "index": "b0o", "roundness": null, "seed": 1205857316, - "version": 36, - "versionNonce": 717405212, - "isDeleted": false, + "version": 37, + "versionNonce": 1823691172, + "isDeleted": true, "boundElements": [], - "updated": 1768867720892, + "updated": 1768903394836, "link": null, "locked": false, "text": "2", @@ -2048,7 +2048,7 @@ { "id": "zjodJTNahFy_DgdrmZAkO", "type": "rectangle", - "x": 1400, + "x": 1520, "y": 920, "width": 80, "height": 80, @@ -2067,8 +2067,8 @@ "type": 3 }, "seed": 618055588, - "version": 47, - "versionNonce": 273105180, + "version": 53, + "versionNonce": 1123443740, "isDeleted": false, "boundElements": [ { @@ -2076,14 +2076,14 @@ "id": "phHSsWqMJl40hT-lEiI8k" } ], - "updated": 1768867718993, + "updated": 1768903396886, "link": null, "locked": false }, { "id": "phHSsWqMJl40hT-lEiI8k", "type": "text", - "x": 1430.1000003814697, + "x": 1550.1000003814697, "y": 937.5, "width": 19.799999237060547, "height": 45, @@ -2100,11 +2100,11 @@ "index": "b0q", "roundness": null, "seed": 90583844, - "version": 36, - "versionNonce": 410566044, + "version": 42, + "versionNonce": 256606364, "isDeleted": false, "boundElements": [], - "updated": 1768867718993, + "updated": 1768903396886, "link": null, "locked": false, "text": "3", @@ -2207,7 +2207,7 @@ { "id": "hlPVtk_Zvb9ax1XWhuVNm", "type": "rectangle", - "x": 1300, + "x": 1420, "y": 920, "width": 80, "height": 80, @@ -2226,8 +2226,8 @@ "type": 3 }, "seed": 662725404, - "version": 54, - "versionNonce": 1945111708, + "version": 64, + "versionNonce": 2065799836, "isDeleted": false, "boundElements": [ { @@ -2235,14 +2235,14 @@ "id": "DkDXjRsX0WzuKE7k0n4_C" } ], - "updated": 1768867831673, + "updated": 1768903401020, "link": null, "locked": false }, { "id": "DkDXjRsX0WzuKE7k0n4_C", "type": "text", - "x": 1330.1000003814697, + "x": 1450.1000003814697, "y": 937.5, "width": 19.799999237060547, "height": 45, @@ -2259,11 +2259,11 @@ "index": "b0u", "roundness": null, "seed": 1883086748, - "version": 44, - "versionNonce": 2036784796, + "version": 54, + "versionNonce": 969664284, "isDeleted": false, "boundElements": [], - "updated": 1768867834133, + "updated": 1768903401020, "link": null, "locked": false, "text": "4", @@ -2275,6 +2275,58 @@ "originalText": "4", "autoResize": true, "lineHeight": 1.25 + }, + { + "id": "IUHJRD7drxv1P2qzgkr9c", + "type": "arrow", + "x": 1469.29567331078, + "y": 391.89155029829396, + "width": 150.7043266892199, + "height": 28.108449701706036, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 90, + "groupIds": [], + "frameId": null, + "index": "b0v", + "roundness": { + "type": 2 + }, + "seed": 181702948, + "version": 67, + "versionNonce": 396271132, + "isDeleted": true, + "boundElements": null, + "updated": 1768903373255, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 150.7043266892199, + 28.108449701706036 + ] + ], + "startBinding": { + "elementId": "Ny8zM8hZc1_aIC5xBaIFI", + "mode": "orbit", + "fixedPoint": [ + 0.8487681898891537, + 0.8487681898891537 + ] + }, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false } ], "appState": { diff --git a/i2c-scheduler-rr.excalidraw.png b/i2c-scheduler-rr.excalidraw.png index cd11bc0..0e54752 100644 Binary files a/i2c-scheduler-rr.excalidraw.png and b/i2c-scheduler-rr.excalidraw.png differ diff --git a/main.typ b/main.typ index 74e4274..81cc8b2 100644 --- a/main.typ +++ b/main.typ @@ -1,5 +1,7 @@ #import "@preview/ilm:1.4.2": * +#let link-text(body) = text(blue, body) + #set text(lang: "de") #show: ilm.with( @@ -9,34 +11,31 @@ date-format: "[day padding:zero].[month repr:short].[year repr:full]", raw-text: (use-typst-defaults: true), bibliography: bibliography("refs.bib"), - figure-index: (enabled: true, title: "Bilderverzeichnis"), ) -#let link-text(body) = text(blue, body) - #counter(page).update(1) = Einleitung -Damit Computersysteme mit der Umwelt interagieren können, muss mit externen Sensoren und Aktoren kommuniziert werden. -Eine der Aufgaben eines Betriebssystems ist es, gemeinsame Schnittstellen für verschiedene Geräte darzustellen; Unter Linux werden dafür sogenannte _device files_ verwendet, so dass über klassische Dateioperationen auf diese Geräte zugegriffen werden kann. +Damit Computersysteme mit ihrer Umwelt interagieren können, ist die Kommunikation mit externen Sensoren und Aktoren erforderlich. +Eine der Aufgaben eines Betriebssystems besteht darin, gemeinsame Schnittstellen für verschiedene Geräte bereitzustellen@os-tasks. Unter Linux werden hierfür sogenannte _device files_@device-files verwendet, die den Zugriff auf diese Geräte über klassische Dateioperationen ermöglichen. == Gerätezugriff per Dateisystem -Nach der UNIX-Philosophie #quote("Everything is a file") melden Gerätetreiber spezielle Dateien im virtuellen Dateisystem an (in der Regel im Verzeichnis `/dev` oder `/sys`). -Wird auf dieser Datei zum Beispiel `write()` aufgerufen, so wird mit den geschriebenen Daten eine Funktion im Kerneltreiber aufgerufen, der sie dann an das physische Gerät weitergibt. +Nach der UNIX-Philosophie #quote("Everything is a file")@everythings-a-file melden Gerätetreiber spezielle Dateien im virtuellen Dateisystem an (in der Regel im Verzeichnis `/dev` oder `/sys`). +Wird auf einer solchen Datei ein Systemaufruf wie `write()` ausgeführt, so wird eine Funktion im Kerneltreiber aufgerufen, die die 'geschriebenen' Daten an das physische Gerät weiterleitet. === Device-Dateien -Folgender Beispielcode zeigt die Kommunikation mit einem BME280-Sensor mithilfe des Userspace I²C-Treibers auf einem Raspberry Pi: +Folgender Beispielcode@i2c-example zeigt die Kommunikation mit einem BME280-Sensor mithilfe des Userspace I²C-Treibers auf einem Raspberry Pi: ```c int main() { // Öffne die Device-Datei int driver = open("/dev/i2c-1"); // (1) - // Setze die Slave-Addresse, die für die nachfolgenden Transaktionen verwendet wird + // Setze die Slave-Adresse für nachfolgende Transaktionen ioctl(driver, I2C_SLAVE, 0x76); // (2) - // Schreibe die Addresse für das ID-Register + // Schreibe die Adresse für das ID-Register uint8_t const write_buf[] = {0xd0}; write(driver, write_buf, sizeof(write_buf)); // (3) @@ -49,17 +48,19 @@ int main() { ``` Der Beispielcode zeigt die vier typischen Datei-Operationen bei der Arbeit mit Device-Dateien: -1. Wie jede Datei muss auch die Device-Datei geöffnet werden. Der Treiber setzt die notwendigen Buchhaltungsstrukturen für eine weitere Anwendung auf. -2. Der `ioctl`-Syscall wird verwendet, um Treibereinstellungen zu ändern oder Operationen auszuführen, die nicht mit `read` oder `write` dargestellt werden können. Die Flags und Parameter für `ioctl` sind treiberabhängig. -3. Der `write`-Syscall sendet eine schreibende Transaktion auf den I²C-Bus. In der Regel wird `write` verwendet, um Daten auf Busse zu schreiben oder Ausgänge zu schalten. +1. Wie jede andere Datei muss die Device-Datei geöffnet werden. Der Treiber richtet dabei die notwendigen Verwaltungsstrukturen für die weitere Nutzung durch die Anwendung ein. +2. Der `ioctl`-Systemaufruf dient dazu, Treibereinstellungen zu ändern oder Operationen auszuführen, die nicht über `read` oder `write` abgebildet werden können. Die Flags und Parameter für `ioctl` sind treiberabhängig. +3. Der `write`-Systemaufruf sendet eine schreibende Transaktion auf den I²C-Bus. In der Regel wird `write` verwendet, um Daten auf Busse zu schreiben oder Ausgänge zu schalten. 4. `read` führt eine lesende Transaktion auf dem I²C-Bus aus. `read` wird typischerweise verwendet, um Gerätedaten auszulesen oder von Bussen zu empfangen. +Typischerweise stellt der Kernel eine Begleitbibliothek wie `libi2c` für I²C-Operationen bereit, um versionsabhängige Unterschiede zu abstrahieren. Diese Bibliotheken werden hier nicht näher beschrieben, stellen jedoch die empfohlene Schnittstelle dar. + === Zugriff über Sysfs Einige Gerätetreiber registrieren keine Dateien in `/dev`, sondern werden über Dateien im virtuellen Dateisystem unter `/sys` kontrolliert. -Die Konvention dafür ist, dass I/O-Geräte typsicherweise in `/dev` registriert werden, während andere Geräte über `/sys` konfiguriert werden. Außerdem wird `/sys` verwendet um Geräte zu finden. +Die Konvention dafür ist, dass für Eingabe-/Ausgabe-Operationen mit Geräten die Dateien in `/dev` verwendet werden, während `/sys` für strukturierte Zugriffe und Konfiguration verwendet wird@devfs-vs-sysfs. -Geräte-Dateien in `/sys` haben eine String-basierte Schnittstelle, es werden also Menschenlesbare Werte in verschiedenen Dateien geschrieben. Das macht die Interaktion mit `/sys`-Dateien in der Shell attraktiv. +Geräte-Dateien in `/sys` haben eine String-basierte Schnittstelle, es werden also menschenlesbare Werte in verschiedenen Dateien geschrieben. Das macht die Interaktion mit `/sys`-Dateien in der Shell attraktiv. Folgender Shell-Code liest die momentane Batteriespannung meines Laptops aus. ```sh @@ -83,7 +84,7 @@ Wie in der Einleitung beschrieben stellt der I²C-Treiber device-Dateien unter ` - Implementiert in #link("https://github.com/torvalds/linux/blob/master/drivers/i2c/i2c-dev.c", link-text[`drivers/i2c/i2c-dev.c`]) - #link("https://www.kernel.org/doc/html/latest/i2c/dev-interface.html", link-text[Offizielle Dokumentation]) -Implementierte Syscalls: +Implementierte Systemaufrufe: #figure( table( @@ -107,7 +108,7 @@ Der GPIO-Treiber stellt zwei Schnittstellen bereit, eine unter `/dev` und eine v - Implementiert in #link("https://github.com/torvalds/linux/blob/master/drivers/gpio/gpiolib-cdev.c", link-text(`drivers/gpio/gpiolib-cdev.c`)); - #link("https://www.kernel.org/doc/html/latest/userspace-api/gpio/chardev.html", link-text([Offizielle Dokumentation])). -Implementierte Syscalls: +Implementierte Systemaufrufe: #figure( table( @@ -117,7 +118,7 @@ Implementierte Syscalls: [`ioctl(, GPIO_GET_CHIPINFO_IOCTL, )`], [Informationen über einen Gpio-Chip holen], [`ioctl(, GPIO_GET_LINEINFO_UNWATCH_IOCTL , )`], [Stoppt das Beobachten eines GPIO-Pins], [`ioctl(, GPIO_V2_GET_LINEINFO_IOCTL, )`], [Beschafft Informationen über einen spezifischen GPIO-Pin], - [`ioctl(, GPIO_V2_GET_LINEINFO_WATCH_IOCTL, )`], [Beschafft Informationen über einen GPIO-Pin und], + [`ioctl(, GPIO_V2_GET_LINEINFO_WATCH_IOCTL, )`], [Beschafft Informationen über einen GPIO-Pin und macht nachfolgende Änderungen über `read` verfügbar], [`ioctl(, GPIO_V2_GET_LINE_IOCTL, )`], [Reserviert und konfiguriert einen GPIO-Pin für das aufrufende Programm], [`ioctl(, GPIO_V2_LINE_SET_CONFIG_IOCTL, )`], [Setzt Attribute für einen Pin, zum Beispiel Input/Output oder active LOW/HIGH], [`ioctl(, GPIO_V2_LINE_GET_VALUES_IOCTL, )`], [Liest Werte von mehreren Eingangs-Pins], @@ -129,17 +130,16 @@ Implementierte Syscalls: == ADC -ADCs werden in Linux nicht direkt als eigene Geräteklasse verwaltet, sondern sind in der Regel als _Hardware Monitoring_ (Überwachung) oder _Industrial I/O_ (iio). - -Als Beispiel wird hier der Kernel-eigene Treiber für den #link("https://www.kernel.org/doc/html/v6.12/iio/ep93xx_adc.html", link-text[ADC des Cirrus Logic EP93xx SoC]) genutzt. -Hier wird für jeden der ADC-Pins ein eigener Eintrag unter `/sys/bus/iio/devices/iio:device/` angelegt, wobei $N$ die Geräte-ID ist: +ADCs werden in Linux nicht direkt als eigene Geräteklasse verwaltet, sondern sind in der Regel als _Hardware Monitoring_ (Überwachung) oder _Industrial I/O_ (iio) gelistet. +Als Beispiel wird hier der Kernel-eigene Treiber für den ADC des Cirrus Logic EP93xx SoC@adc-driver genutzt. +Dabei wird für jeden der ADC-Pins ein eigener Eintrag unter `/sys/bus/iio/devices/iio:device/` angelegt, wobei $N$ die Geräte-ID ist: #figure( table( columns: (auto, 1fr), align: horizon, - table.header([Sysfs-Eintrag], [Pin-Name]), + table.header([Sysfs-Eintrag], [Name des gesampleten Pins]), [in_voltage0_raw], [`Y-`], [in_voltage1_raw], [`sX+`], [in_voltage2_raw], [`sX-`], @@ -152,15 +152,105 @@ Hier wird für jeden der ADC-Pins ein eigener Eintrag unter `/sys/bus/iio/device caption: [Sysfs-Einträge des ADC-Treibers] ) -Das Auslesen einer dieser Datein startet führt synchron eine ADC-Umwandlung durch und gibt den ganzzahligen µV-Wert als String aus. +Das Auslesen einer dieser Datein führt synchron eine ADC-Umwandlung durch. Das Format der gelesenen Daten ist nicht klar dokumentiert. = Design einer Hardwareschnittstelle für AT91SAM7-Timer -Der AT91SAM7-Mikrocontroller stellt das _Timer Counter_ Peripheral bereit; Drei 16-bit Zähler +Der AT91SAM7-Mikrocontroller@sam7s-datasheet stellt das _Timer Counter Peripheral_ bereit; +Drei unabhängige 16-bit Zähler, Kanäle genannt, mit einstellbaren Taktgeschwindigkeiten, Überlaufgrenzen und _Triggern_. == Features -TODO +Jeder Kanal kann in einem der folgenden Modi sein: +- _Capture_ zum Festhalten von Zeitpunkten, zu denen Eingänge geschaltet wurden +- _Waveform_ zum Erzeugen von einstellbaren Rechtecksignalen + +Außerdem hat jeder Kanal drei Eingangssignale `XC0-2`, zwei Ausgangssignale `A/B` und kann einen von fünf Vorteilern wählen. + +=== Capture-Modus + +Im _Capture_-Modus zählt der Zähler kontinuierlich und es wir bei einem konfigurierbaren _Event_ (eine Flanke auf `TIOA` oder `TIOB`) der Zählerstand in eins der Register geschrieben. + +Dieser Modus ist unter anderem für die Bestimmung von Frequenz, Pulszeit und Pahsenbestimmung eins oder mehrerer anliegender Signale gedacht. + +=== Waveform-Modus + +Dieser Modus ist für die Erzeugung von Rechtecksignalen gedacht. Es gibt vier Untermodi: + +#figure( + table( + columns: (auto, auto, 1fr), + align: horizon, + table.header([Modus], [Zählrichtung], [Verhalten wenn $="RC"$]), + [`00`], [Hoch], [Nichts, nur durch Überlauf zurückgesetzt], + [`10`], [Hoch], [Zurücksetzen auf 0], + [`01`], [Hoch, dann Runter], [Nichts, Richtungswechsel wenn $=0$ oder $="0xFFFF"$], + [`11`], [Hoch, dann Runter], [Richtungswechsel], + ), + caption: [Wellenmodi im Waveform-Modus] +) + +Außerdem wird der Zählerwert immer mit den Werten in den Registern `RA/RB/RC` auf Gleichheit verglichen. +Die daraus entstehenden Trigger-Signale können dann die Ausganspins `A/B` jeweils entweder einschalten, ausschalten oder umschalten. + +== Umsetzung + +Die API ist an der Struktur der GPIO-API orientiert. + +Jeder Kanal muss mit `REQ_CHANNEL` vom Kernel angefragt werden, damit ein Kanal von genau einem Prozess verwaltet wird. +Mithilfe der `SET_MODE_CAPUTE` und `SET_MODE_WAVE` `ioctl`s wird der Kanal in den jeweiligen Modus versetzt und konfiguriert. +Der `TIMER_START`-Befehl startet einen einzelnen Kanal. +Wenn der aufrufende Prozess alle Kanäle kontrolliert, kann `TIMER_START` auf dem Timer selbst aufgerufen werden, was das SYNC-Signal für alle Kanäle setzt. + +Folgend eine Beispielanwendung: +```c +int main() { + int timer_fd = open("/dev/timer0"); + + int ch0 = ioctl(timer_fd, REQ_CHANNEL_IOCTL, 0); + int some_free_channel = ioctl(timer_fd, REQ_CHANNEL_IOCTL, -1); + + struct capture_config capture_config = { + .clock = CLOCK_1, // = TIMER_CLOCK1 + .clock_burst = CLOCK_BURST_NONE, // Oder CLOCK_BURST_TIOA0/1/2 + .clock_invert = false, + .a_edge = EDGE_RISING, + .b_edge = EDGE_NONE, + .external_trigger = EXT_TRIGGER_A, + .interrupt_on = INT_LDRA | INT_LDRB | INT_OVF, // Aktivierte interrupts + .compare = -1, //Deaktiviert CPCTRG, >0 aktiviert CPCTRG + }; + + ioctl(ch0, SET_MODE_CAPTURE, &capture_config); + + struct wave_config wave_config = { + .clock = CLOCK_TIOA2, //-EINVAL wenn nicht verfügbar + .clock_invert = true, + .wave_mode = WAVE_MODE_UP_RC_TRIGGER, // WAVSEL = 10 + .ra = 100, + .rb = 0x4000, + .rc = 0x9fff, + .tioa = (struct mtio) { + .a_mode = MTIO_MODE_SET, + .b_mode = MTIO_MODE_CLEAR, + .c_mode = MTIO_MODE_TOGGLE, + .sw_mode = MTIO_MODE_NONE, + } + .tiob = (struct mtio) {0}, // TIOB ist deaktiviert + }; + ioctl(some_free_channel, SET_MODE_WAVE, &wave_config); + + //Würde mit dem SYNC-Signal alle Kanäle starten, + //allerdings hat dieser Prozess nicht alle Kanäle angefragt. + //Der Aufruf würde also fehlschlagen + //ioctl(timer_fd, TIMER_START); + + ioctl(ch0, TIMER_START); //Setzt SWTRG + struct capture_event capture_event; + // Blockiert bis mindestens eins der Signale in interrupt_on ausgelöst wurde + read(ch0, &capture_event, sizeof(capture_event)); +} +``` = Scheduling bei geteilten Bussystemen @@ -181,20 +271,22 @@ Zudem sollen in regelmäßigen Abständen Temperatur und Luftfeuchtigkeit vom Se Wie in @fig-i2c-starvation gezeigt, wird durch die häufigen Display-Übertragungen der Temperatur-Sensor "ausgehungert" (schraffierter Hintergrund) und kann seine Daten nicht rechtzeitig übertragen. -Bei geteilten Ressourcen wie Bussen tritt dieses Problem häufig auf, weswegen im nächsten Schritt ein typsicher Lösungsansatz besprochen wird. +Dieses Problem gehört zur Klasse der _Scheduling_-Aufgaben. Ein klassischer Lösungsansatz wird im nächsten Abschnitt besprochen == Lösungsansatz -Da hier eine geteilte Ressource (der Bus) _fair_ zwischen mehreren Clients (den Treibern) verteilt werden soll, bietet sich ein #link("https://de.wikipedia.org/wiki/Prozess-Scheduler", link-text([Scheduling Verfahren])) an. +Da hier eine geteilte Ressource (der Bus) *fair* zwischen mehreren Clients (den Treibern) verteilt werden soll, bietet sich ein _Scheduling_-Verfahren@wiki-scheduling an. -Fragt ein Client einen I²C-Transfer an, so wird er nicht direkt ausgeführt, sondern mit anderen ausstehenden Anfragen in einer Warteschlange (Queue) gespeichert. -Nun kann der I²C-Scheduler die nächste anstehende Transaktion nach einem Scheduling-Verfahren wie dem Completely Fair Scheduler@wiki-cfs aussuchen und durchführen, um Aushungern zu vermeiden. - -Nachfolgend ist der Ablauf mit dem simplen Round-Robin-Verfahren@wiki-round-robin gezeigt: +Fragt ein Client einen I²C-Transfer an, so wird er nicht direkt ausgeführt, sondern mit anderen ausstehenden Anfragen in einer _Queue_ (dt. Warteschlange)@wiki-queue gespeichert. +Nun kann der I²C-Scheduler die nächste anstehende Transaktion nach einem Scheduling-Verfahren wie dem _Completely Fair Scheduler_@wiki-cfs aussuchen und durchführen, um Aushungern zu vermeiden. +Nachfolgend ist der Ablauf mit dem simplen _Round-Robin-Verfahren_@wiki-round-robin gezeigt, das *keine* Fairness garantiert: #figure( image("./i2c-scheduler-rr.excalidraw.png"), caption: [I²C-Scheduling mit Round Robin], alt: "A vertical scheduling diagram showing round robin scheduling", ) + += Quelltext +Der Quelltext dieser Arbeit ist unter #link-text([https://git.veltko.de/Weckyy702/uc-ausarbeitung-linux-treiber]) mit der GPL lizensiert zu finden diff --git a/refs.bib b/refs.bib index 5514ac2..3062c3f 100644 --- a/refs.bib +++ b/refs.bib @@ -8,7 +8,7 @@ } @misc{wiki-round-robin, - author = {Wikipedia contributors}, + author = {wikipedia contributors}, title = {Round Robin (Informatik)}, howpublished = {\url{https://de.wikipedia.org/w/index.php?title=Round_Robin_(Informatik)&oldid=249025330}}, year = {2024}, @@ -17,10 +17,85 @@ } @misc{wiki-cfs, - author = {Wikipedia contributors}, + author = {wikipedia contributors}, title = {Completely Fair Scheduler}, howpublished = {\url{https://en.wikipedia.org/w/index.php?title=Completely_Fair_Scheduler&oldid=1329198135}}, year = {2025}, month = {12}, note = {Zuletzt aufgerufen: 2026-01-20}, } + +@misc{wiki-scheduling, + author = {wikipedia contributors}, + title = {Prozess-Scheduler}, + howpublished = {\url{https://de.wikipedia.org/w/index.php?title=Prozess-Scheduler&oldid=251680761}}, + year = {2024}, + month = {12}, + note = {Zuletzt aufgerufen: 2026-01-20}, +} + +@misc{wiki-queue, + author = {wikipedia contributors}, + title = {Queue (abstract data type)}, + howpublished = {\url{https://en.wikipedia.org/w/index.php?title=Queue_(abstract_data_type)&oldid=1332703688}}, + year = {2026}, + month = {01}, + note = {Zuletzt aufgerufen: 2026-01-20}, +} + +@misc{os-tasks, + author = {Javeria Aiman}, + title = { 5 Functions of an Operating System }, + howpublished = {\url{https://mindscribes.com/5-functions-of-an-operating-system/}}, + year = {2025}, + month = {07}, + note = {Zuletzt aufgerufen: 2026-01-20}, +} + +@misc{everythings-a-file, + author = {Neil Brown}, + title = {Ghosts of Unix Past: a historical search for design patterns}, + howpublished = {\url{https://lwn.net/Articles/411845/}}, + year = {2010}, + month = {10}, + note = {Zuletzt aufgerufen: 2026-01-20}, +} + +@misc{device-files, + author = {linux kernel contributors}, + title = {Character device drivers}, + howpublished = {\url{https://linux-kernel-labs.github.io/refs/heads/master/labs/device_drivers.html}}, + note = {Zuletzt aufgerufen: 2026-01-20}, +} + +@manual{sam7s-datasheet, + title = {AT91SAM ARM-based Flash MCU}, + organization = {Microchip Technology Inc.}, + address = {2355 West Chandler Blvd. Chandler, Arizona, USA}, + edition = {6175L}, + year = {2012}, + month = {10}, +} + +@misc{i2c-example, + author = {Konstantin Veltmann}, + howpublished = {\url{https://git.veltko.de/Weckyy702/uc-ausarbeitung-linux-treiber/src/commit/d6e0fb978ee99b3bdf1b6b6fe73da930a2b59bbc/examples/i2c.c}}, + year = {2026}, + month = {01}, +} + +@misc{devfs-vs-sysfs, + author = {Youssef Salem}, + title = {The Difference Between /dev and /sys/class}, + howpublished = {\url{https://www.baeldung.com/linux/dev-sys-class-differences}}, + year = {2024}, + month = {08}, + note = {Zuletzt aufgerufen: 2026-01-22}, +} + +@misc{adc-driver, + author = {the kernel development community}, + title = {Cirrus Logic EP93xx ADC driver}, + howpublished = {\url{https://www.kernel.org/doc/html/v6.12/iio/ep93xx_adc.html}}, + note = {Zuletzt aufgerufen: 2026-01-22}, +}