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()](images/0403%20Ablauf%20writeData.png)
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()](images/0403%20Ablauf%20readData.png)
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](images/0403%20Programmablauf%20MouseVehicle4.png)
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.
|