Prof. J. Walter - Informationstechnik, Mikrocomputertechnik, Digitale Medien Softwaredoku
Hochschule Karlsruhe Logo Entwicklungsprojekt Master
FT32 - Positionserfassung
Sommersemester 2019
Alexander Haaf
Johannes Marquart

Software Dokumentation

Auf dieser Seite soll ein Überblick gegeben werden über die zentralen Softwareteile, die zur Ansteuerung des PAW3204DB-TJ3R entstanden sind, bzw. die Ansteuerung in weiterführenden Programmen erleichtern.
Im unteren Teil ist auch eine Beschreibung eines weiteren Programms, welches die Sensoransteuerung nutzt um das Fischertechnik-Fahrzeug vorgegebene Strecken zu fahren.

Funktion writeRegister()

Parameter: byte address (Adresse des Registers), byte data (zu übertragende Daten)

Ein Register wird zum Schreiben mit einer 8-Bit Adresse angesprochen, das erste Bit zeigt dabei an, ob es sich um ein Lesezugriff (0) oder Schreibzugriff (1) handelt. Das Adressbyte wird seriell synchron zu einem Taktsignal an den Sensor gesendet. Für die Taktgenerierung setzt das Programm den Pegel am Taktpin (SCLK) wechselnd auf High bzw. Low. Der Pegel am Datenpin (SDIO) muss bei steigender Flanke anliegen, dann liest der Sensor den Wert korrekt ein.
Anschließend folgt die Übertragung des Datenbytes, welches ebenfalls seriell synchron zum Taktsignal gesendet wird.

Der Ablauf, wie links dargestellt, ergibt eine Übertragungsrate von ca. 2,6MBit/s. Laut Datenblatt ist lediglich eine Begrenzung der maximalen Taktfrequenz von 10MHz angegeben, nach unten ist die Übertragungsrate durch den Timeout begrenzt (wenn der Pegel mindestens 1,7ms bei der Übertragung auf High liegt).
Ablauf writeData()
Ablauf writeData()

Funktion readRegister()

Parameter: byte address (Adresse des Registers)
Return: byte data (ausgelesene Daten des Registers)

Wie oben bei "writeRegister()" wird zum auslesen eines Registers zuerst die Adresse angesprochen. Anschließend muss das Taktsignal für mindestens 3µs pausieren. Für die Datenübertragung wird der Datenpin auf Eingang (floating, d.h. one Pull-Up/Down) eingestellt. Dann wird der Takt erneut vom ESP32 aus gestartet und vorgegeben, die Festlegung der Pegel auf der Datenleitung werden vom Maussensor übernommen.
Der Datenpin des ESP32 (SDIO) liest die Pegel nach einer steigenden Taktflanke ein und speichert diese bitweise ab. Nach der vollständigen Übertragung gibt die Funktion das eingelesene Byte zurück an die übergeordnete Funktion.
Ablauf readData()
Ablauf readData()

Funktion reset()

Diese Funktion wird bisher nur zu Beginn des Programms genutzt. Dabei wird das Reset-Bit des Configuration-Registers auf 1 gesetzt um alle vorher getroffenen Einstellungen auf default-Werte zurückzusetzen. Das Register selbst wird nach den Reset wieder auf die eigenen default-Werte gesetzt um sicher zu gehen, dass der Sensor sich nicht in einer Resetschleife aufhängt (da das Reset-Bit dauerhaft auf 1 liegt). Für das Beschreiben des Registers wird die oben beschriebene Funktion writeRegister() genutzt.
Die reset()-Funktion kann aufgerufen werden, wenn starke Signalstörungen bei einer Übertragung einen undefinierten Zustand eines oder mehrerer Register verursacht haben.

Beispielprogramm MouseVehicle4

Das Beispielprogramm "ESP32-mouseVehicle4" verwendet die oben beschriebenen Funktionen "writeRegister()", "readRegister()" und "reset()". Diese werden in einem eigenen Thread aufgerufen, um den Maussensor kontinuierlich und unabhängig von Befehlen der Fahrzeugsteuerung in der "loop()" auszulesen (z.B. eine Ausgabe über Serial.print() etc. benötigt sehr viel Zeit).
Folgende Grafik gibt eine Übersicht über den Programmablauf:

Vereinfachter Programmablauf
Vereinfachter Programmablauf

Blaue Blöcke: Funktionen / Zusammenfassungen von Funktionen
Graue Blöcke: Arduino-Funktionen - bei Arduino-C++ läuft das Programm innerhalb der setup(), loop() und ggf. weiteren Tasks ab.
Grüner Block: shared memory - gemeinsamer Speicher zur Kommunikation zwischen "loop()" und "readMouseSensor()"
Schwarze Pfeile: Programmablaufpfade
Grüne Pfeile: Datenpfade
Grauer Pfeil: Das shared memory wird zu Programmbeginn erstellt

Kommentare zum Programmablauf:
• In der Setup wird mit "xTaskCreatePinnedToCore()" die Funktion "readMouseSensor" in einem neuen Thread auf einem speziell angegebenen Core gestartet. Da der ESP32 zwei Rechenkerne bietet und die setup() wie loop() auf Core1 laufen, wird "readMouseSensor()" auf Core0 gestartet um weitestgehend unabhängig von den Arduino-Funktionen zu werden.
• Das "shared memory" (SHM) wird auf den (dynamischen) Heap gelegt, sodass beide Threads (loop() und readMouseSensor()) zugriff darauf haben. Der gemeinsame Zugriff wird durch einene Semaphore (bool newData) geschützt bzw. synchronisiert.
• In der Funktion "readMouseSensor()" wird nach der Überprüfung der Verbindung zum Sensor (Abfrage der Sensor ID) dieser konfiguriert: Mit Register "Configruation" die Sensorauflösung auf 1600 DPI (dots per inch) gesetzt (maximale Auflösung des Sensors), mit Register "Operation Mode" die Sleep-Funktionen abgeschalten (Sleep-Funktionen sind nur zum Energiesparen relevant, haben aber beim "Aufwecken" eine leicht erhöhte Reaktionszeit). Anschließend fragt der ESP32 zyklisch den Maussensor, ob neue Bewegungsdaten vorliegen. Wenn ja, werden die Daten ausgelesen (readRegister()) und in das SHM übertragen. Bei der Umrechnung der Rohwerte in [mm/s] werden auch weitere Kalibrierfaktoren (z.B. aufgrund der DPI-Einstellung) einberechnet.
• Mit den Funktionen "driveDist()" und "rotateAngle()" kann der Programmierer in der loop() angeben, wie der Roboter fahren soll. Mit "driveDist()" fährt der Roboter eine vorgegebene Länge geradeaus, "rotateAngle()" lässt den Roboter sich um einen angegebenen Winkel drehen.

Fahrfunktion driveDist()

Parameter: float dist (zu fahrende Entfernung in [mm], positiv oder negativ), float vSoll (Sollgeschwindigkeit in [mm/s], positiv)

Die Funktion regelt die Geschwindigkeit des Fahrzeugs mit einem PI-Regler. Die Verstärkungsfaktoren wurden so empirisch ermittelt, dass das Fahrzeug nicht schwingt, da bei schnellen Schwingungen der Maussensor die werte nicht korrekt einliest. Gegen Ende der angegebenen Strecke wird die vorgegebene Geschwindigkeit automatisch reduziert um ein (unkontrolliertes) Rutschen des Fahrzeugs bei zu starker Bremsung zu verhindern.
Zusätzlich regelt die Funktion mit einem P-Regler die rotatorische Position des Fahrzeugs gegen 0, sodass das Fahrzeug geradeaus fährt.

Fahrfunktion rotateAngle()

Parameter: float angle (zu drehender Winkel in [rad], positiv oder negativ), float vSoll (Sollgeschwindigkeit in [rad/s], positiv)

Die Regelung der Drehung ist sehr ähnlich zu Fahrregelung in "driveDist()". Auch hier kommt ein PI-Regler mit empirisch ermittelten Parametern zum Einsatz. Ein Unterschied ist, dass die Räder entgegengesetzt angesteuert werden, sodass sich das Fahrzeug auf der Stelle drehen kann.
Zusätzlich wird hier die gefahrene Distanz gegen 0 geregelt, sodass sich das Fahrzeug wirklich auf der Stelle dreht.


  Mit Unterstützung von Prof. J. Walter Sommersemester 2019