_HP_
22.04.2007, 23:44
Hi all,
der in der Zip-Datei vorliegende Code gestattet es, alle 6 AD-Wandler-Kanäle des ASURO interruptgesteuert auszulesen. Bisher (in Lib2.7) war das nur für die Odometriesensoren möglich. Die einzelnen Kanäle (besser, die einzelnen Funktionen) des ASURO lassen sich dabei getrennt ein- und ausschalten. Zugriff auf die Daten hat man über globale Variablen. Mein Wunsch wäre es, wenn diese Funktionalität Einzug in die nächste Version der ASURO-Lib finden würde.
Bei der Entwicklung und den nötigen Tests sind mir darüber hinaus in einigen alten Funktionen ein paar Merkwürdigkeiten aufgefallen. Diese werde ich hier ebenfalls beschreiben.
Als Grundlage habe ich die Software genommen, wie sie sich im Auslieferungszustand auf der CD befindet. Funktionen aus der Lib2.7, die ich benötigt habe, habe ich per Copy&Paste hinzugefügt. Originalfunktionen aus der asuro.c, die ich verändert habe, habe ich kopiert und mit einem Unterstrich versehen.
Soviel zur Einleitung und nun zum Code:
Das Hauptprogramm liest zunächst die einzelnen ADC-Kanäle mit den bekannten Funktionen aus der asuro.c und gibt sie aus. Danach werden die Kanäle dann per ADC-Interrupt gelesen und ebenfalls ausgegeben. Schließlich werden die einzelnen Funktionen wieder ausgeschaltet.
Die Ausgabe habe ich mit den Funktionen SerPrint) und PrintInt() aus der Lib2.7 gemacht. Mit meinem Compiler „avr-gcc.exe (GCC) 4.1.1 (WinAVR 20070122)“, bekomme ich bei der SerPrint()-Funktion jedes Mal eine Vorzeichenwarnung. Darum habe ich den Parameter von „unsigned char“ auf „char“ geändert.
OdometrieData() schaltet die IR-Dioden ein. Wenn diese nicht schon eingeschaltet waren, sind – zumindest bei meinem ASURO – die danach gelesenen Werte (besonders für den linken Sensor) zu niedrig. Das merkt man aber nur, wenn man - bei stehenden Motoren - die Sensoren mehrfach ausliest. Ein Sleep(5) nach dem Einschalten der LEDs war nötig, um die richtigen Werte zu messen. Natürlich könnte man die LEDs auch einschalten, bevor man OdometrieData() aufruft – dann würde die Verzögerung nicht jedes Mal wirksam.
LineData() hat bei mir übrigens den gleichen Effekt hervorgerufen. Werden die Werte direkt nach dem Ein- oder Ausschalten der Front-LED gemessen, sind sie verschieden von den nachfolgenden Messungen. Offensichtlich haben LEDs also auch eine nicht zu vernachlässigenden Trägheit.
Besonders verblüfft hat mich die Batterie-Funktion aus der Lib2.7 (hier GetVoltage()). Der gemessene Wert hatte einfach keine Beziehung zur angelegten Spannung. Das Rätsel ließ sich auch hier über eine Verzögerung (Sleep(5)) lösen! Ursache ist das Umschalten der Referenzspannung von VCC auf die internen 2,56V, die ein wenig Zeit benötigt.
Es würde mich interessieren, ob bei Euren ASUROs die gleichen Effekte auftreten, oder ob es an meinem Exemplar liegt.
Jetzt aber zum interruptgesteuterten Auslesen der Daten:
Die erste Idee, dies im Freeflow-Modus des AD-Wandlers zu machen habe ich fallengelassen, nachdem ich die Dokumentation dazu gelesen hatte. Das Problem ist nämlich, dass man dann nicht mit 100%iger Sicherheit sagen kann, zu welchem Kanal die gerade anstehenden Daten gehören. Bei zwei Kanälen (wie bei dem schon realisierten Auslesen der Odometriesensoren) kann man das noch einigermaßen „erraten“ (obwohl ich erst jetzt verstanden habe, warum die Zuordnung der Daten in der ISR scheinbar „falschrum“ vorgenommen wurde). Bei mehreren Känälen, die auch noch wahlfrei ein- oder ausgeschaltet werden können, war es einfacher, die Messung jedes Mal gezielt zu starten, wenn der neue Kanal gesetzt ist.
Das Ein- bzw. Ausschalten von Funktionsgruppen bzw. Kanälen erfolgt über „Start-„ bzw. „Stop“-Routinen. Diese sind im Wesentlichen nach dem gleichen Prinzip aufgebaut, so dass es einfach ist, solche Routinen auch für Erweiterungsplatinen zu schreiben.
Der Zugriff auf die Daten erfolgt über globale Variablen.
Im Unterschied zur Lösung in der Lib2.7 werden alle Daten (also auch die der Odometer) als 10bit-Werte im Integerformat abgespeichert. Dies hat einmal den Vorteil, dass die Daten aller Kanäle im gleichen Format gespeichert werden und zum anderen kann man die gleichen Schwellwerte sowohl im Polling-Betrieb als auch im Interruptbetrieb nutzen. Man erhält Zugriff auf diese Daten über die globale Variable ADC_DATA[]. Hier werden allerdings tatsächlich nur die „Rohdaten“ des AD-Wandlers abgelegt – also NICHT die Schwarz/Weiß- bzw. Weiß/Schwarz-Übergänge (Ticks) der Odometerscheiben. Diese findet man in der globalen Variable ENCODER[].
Ob neue Daten für einen Kanal vorhanden sind, kann man in der globalen Variablen NEW_ADC_DATA testen. Jedes Mal, wenn neue Daten für einen Kanal in ADC_DATA[] abgelegt werden, wird das entsprechende Bit in NEW_ADC_DATA gesetzt.
Ob sich die Anzahl der Ticks in ENCODER[] geändert hat, kann man in NEW_ENCODER_DATA testen.
Eine Besonderheit Betrifft das Auslesen des ADC-Kanals für die Schalter. Mit ‚StartSwitches()’ kann man diesen Kanal analog zu den anderen einschalten. Wird viermal der gleiche Wert gelesen, kann man über SWITCHES_PRESSED direkt auf das Bitmuster der gedrückten Tasten zugreifen. Ob diese Variable neu geschrieben wurde kann man mit NEW_SWITCH_DATA testen.
Nun macht es natürlich Sinn, das Auslesen der Schalter erst dann zu starten, wenn überhaupt ein Schalter gedrückt wurde. Dies kann ebenfalls interruptgesteuert geschehen. Dazu gibt es in asuro.c die Funktion ‚StartSwitch()’. Erstaunlich fand ich jedoch, dass bei meinem ASURO der Interrupt immer ausgelöst wurde, wenn die Funktion aufgerufen wurde – also unabhängig davon, ob ein Schalter betätigt wurde oder nicht. Ich habe das – in der Funktion ‚WatchSwitches()’ jetzt so geändert, dass der Interrupt nicht mehr bei Low-Potential sonder bei der negativen Flanke ausgelöst wird. Das funktionierte bei meinen Tests zuverlässig. Die Information, dass eine Taste gedrückt wurde, findet man dann in SWITCHED. Um auch zu ermitteln, WELCHE Taste gedrückt wurde, wird in der ISR ‚StartSwitches()’ aufgerufen, so dass man die Information über die gedrückte(n) Taste(n) dann in SWITCHES_PRESSED finden kann, wie oben beschrieben.
Da es sein kann, dass man – egal welche Taste gedrückt wurde – auf jeden Fall erst mal die Motoren stoppen möchte, habe ich diese Möglichkeit mit in die ISR eingebaut. Um sie „scharf zu schalten“ muss die globale Variable ‚STOP_ON_SWITCH’ vorher auf TRUE gesetzt haben.
Bitte schaut Euch die Funktionen an und Testet Sie mit Euren ASUROs aus. Ich freue mich über jedes Feedback – auch über Bugs, die mir entgangen sind. Eine Diskussion darüber, ob und was wir davon in die offizielle Lib übernehmen wollen/sollen, wäre auch willkommen.
Gruß
_HP_
der in der Zip-Datei vorliegende Code gestattet es, alle 6 AD-Wandler-Kanäle des ASURO interruptgesteuert auszulesen. Bisher (in Lib2.7) war das nur für die Odometriesensoren möglich. Die einzelnen Kanäle (besser, die einzelnen Funktionen) des ASURO lassen sich dabei getrennt ein- und ausschalten. Zugriff auf die Daten hat man über globale Variablen. Mein Wunsch wäre es, wenn diese Funktionalität Einzug in die nächste Version der ASURO-Lib finden würde.
Bei der Entwicklung und den nötigen Tests sind mir darüber hinaus in einigen alten Funktionen ein paar Merkwürdigkeiten aufgefallen. Diese werde ich hier ebenfalls beschreiben.
Als Grundlage habe ich die Software genommen, wie sie sich im Auslieferungszustand auf der CD befindet. Funktionen aus der Lib2.7, die ich benötigt habe, habe ich per Copy&Paste hinzugefügt. Originalfunktionen aus der asuro.c, die ich verändert habe, habe ich kopiert und mit einem Unterstrich versehen.
Soviel zur Einleitung und nun zum Code:
Das Hauptprogramm liest zunächst die einzelnen ADC-Kanäle mit den bekannten Funktionen aus der asuro.c und gibt sie aus. Danach werden die Kanäle dann per ADC-Interrupt gelesen und ebenfalls ausgegeben. Schließlich werden die einzelnen Funktionen wieder ausgeschaltet.
Die Ausgabe habe ich mit den Funktionen SerPrint) und PrintInt() aus der Lib2.7 gemacht. Mit meinem Compiler „avr-gcc.exe (GCC) 4.1.1 (WinAVR 20070122)“, bekomme ich bei der SerPrint()-Funktion jedes Mal eine Vorzeichenwarnung. Darum habe ich den Parameter von „unsigned char“ auf „char“ geändert.
OdometrieData() schaltet die IR-Dioden ein. Wenn diese nicht schon eingeschaltet waren, sind – zumindest bei meinem ASURO – die danach gelesenen Werte (besonders für den linken Sensor) zu niedrig. Das merkt man aber nur, wenn man - bei stehenden Motoren - die Sensoren mehrfach ausliest. Ein Sleep(5) nach dem Einschalten der LEDs war nötig, um die richtigen Werte zu messen. Natürlich könnte man die LEDs auch einschalten, bevor man OdometrieData() aufruft – dann würde die Verzögerung nicht jedes Mal wirksam.
LineData() hat bei mir übrigens den gleichen Effekt hervorgerufen. Werden die Werte direkt nach dem Ein- oder Ausschalten der Front-LED gemessen, sind sie verschieden von den nachfolgenden Messungen. Offensichtlich haben LEDs also auch eine nicht zu vernachlässigenden Trägheit.
Besonders verblüfft hat mich die Batterie-Funktion aus der Lib2.7 (hier GetVoltage()). Der gemessene Wert hatte einfach keine Beziehung zur angelegten Spannung. Das Rätsel ließ sich auch hier über eine Verzögerung (Sleep(5)) lösen! Ursache ist das Umschalten der Referenzspannung von VCC auf die internen 2,56V, die ein wenig Zeit benötigt.
Es würde mich interessieren, ob bei Euren ASUROs die gleichen Effekte auftreten, oder ob es an meinem Exemplar liegt.
Jetzt aber zum interruptgesteuterten Auslesen der Daten:
Die erste Idee, dies im Freeflow-Modus des AD-Wandlers zu machen habe ich fallengelassen, nachdem ich die Dokumentation dazu gelesen hatte. Das Problem ist nämlich, dass man dann nicht mit 100%iger Sicherheit sagen kann, zu welchem Kanal die gerade anstehenden Daten gehören. Bei zwei Kanälen (wie bei dem schon realisierten Auslesen der Odometriesensoren) kann man das noch einigermaßen „erraten“ (obwohl ich erst jetzt verstanden habe, warum die Zuordnung der Daten in der ISR scheinbar „falschrum“ vorgenommen wurde). Bei mehreren Känälen, die auch noch wahlfrei ein- oder ausgeschaltet werden können, war es einfacher, die Messung jedes Mal gezielt zu starten, wenn der neue Kanal gesetzt ist.
Das Ein- bzw. Ausschalten von Funktionsgruppen bzw. Kanälen erfolgt über „Start-„ bzw. „Stop“-Routinen. Diese sind im Wesentlichen nach dem gleichen Prinzip aufgebaut, so dass es einfach ist, solche Routinen auch für Erweiterungsplatinen zu schreiben.
Der Zugriff auf die Daten erfolgt über globale Variablen.
Im Unterschied zur Lösung in der Lib2.7 werden alle Daten (also auch die der Odometer) als 10bit-Werte im Integerformat abgespeichert. Dies hat einmal den Vorteil, dass die Daten aller Kanäle im gleichen Format gespeichert werden und zum anderen kann man die gleichen Schwellwerte sowohl im Polling-Betrieb als auch im Interruptbetrieb nutzen. Man erhält Zugriff auf diese Daten über die globale Variable ADC_DATA[]. Hier werden allerdings tatsächlich nur die „Rohdaten“ des AD-Wandlers abgelegt – also NICHT die Schwarz/Weiß- bzw. Weiß/Schwarz-Übergänge (Ticks) der Odometerscheiben. Diese findet man in der globalen Variable ENCODER[].
Ob neue Daten für einen Kanal vorhanden sind, kann man in der globalen Variablen NEW_ADC_DATA testen. Jedes Mal, wenn neue Daten für einen Kanal in ADC_DATA[] abgelegt werden, wird das entsprechende Bit in NEW_ADC_DATA gesetzt.
Ob sich die Anzahl der Ticks in ENCODER[] geändert hat, kann man in NEW_ENCODER_DATA testen.
Eine Besonderheit Betrifft das Auslesen des ADC-Kanals für die Schalter. Mit ‚StartSwitches()’ kann man diesen Kanal analog zu den anderen einschalten. Wird viermal der gleiche Wert gelesen, kann man über SWITCHES_PRESSED direkt auf das Bitmuster der gedrückten Tasten zugreifen. Ob diese Variable neu geschrieben wurde kann man mit NEW_SWITCH_DATA testen.
Nun macht es natürlich Sinn, das Auslesen der Schalter erst dann zu starten, wenn überhaupt ein Schalter gedrückt wurde. Dies kann ebenfalls interruptgesteuert geschehen. Dazu gibt es in asuro.c die Funktion ‚StartSwitch()’. Erstaunlich fand ich jedoch, dass bei meinem ASURO der Interrupt immer ausgelöst wurde, wenn die Funktion aufgerufen wurde – also unabhängig davon, ob ein Schalter betätigt wurde oder nicht. Ich habe das – in der Funktion ‚WatchSwitches()’ jetzt so geändert, dass der Interrupt nicht mehr bei Low-Potential sonder bei der negativen Flanke ausgelöst wird. Das funktionierte bei meinen Tests zuverlässig. Die Information, dass eine Taste gedrückt wurde, findet man dann in SWITCHED. Um auch zu ermitteln, WELCHE Taste gedrückt wurde, wird in der ISR ‚StartSwitches()’ aufgerufen, so dass man die Information über die gedrückte(n) Taste(n) dann in SWITCHES_PRESSED finden kann, wie oben beschrieben.
Da es sein kann, dass man – egal welche Taste gedrückt wurde – auf jeden Fall erst mal die Motoren stoppen möchte, habe ich diese Möglichkeit mit in die ISR eingebaut. Um sie „scharf zu schalten“ muss die globale Variable ‚STOP_ON_SWITCH’ vorher auf TRUE gesetzt haben.
Bitte schaut Euch die Funktionen an und Testet Sie mit Euren ASUROs aus. Ich freue mich über jedes Feedback – auch über Bugs, die mir entgangen sind. Eine Diskussion darüber, ob und was wir davon in die offizielle Lib übernehmen wollen/sollen, wäre auch willkommen.
Gruß
_HP_