PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : NiboBiene und Sharp IR



Rabenauge
25.05.2010, 22:41
Hallöle.
Wieder mal so ein Thread von mir...Bienenerweiterung für Dummys. ;)
Mein Sharp ist da.
Im Bienen-Erweiterungen-Thread habe ich gelesen, wie "einfach" es ist, diesen Sensor (es ist der Sharp GP2Y0A21YK0F) an den XBee-Port 3 mitsamt nem Servo anzuschliessen.
Servo habe ich schon ein paar Tage dran, das funktioniert bestens, im Moment kann ich es mit den Fühlern frei nach rechts und links schwenken.

Nun soll der IR-Sensor mitarbeiten, angeschlossen (am gleichen Port, halt die vierte Leitung als Signalleitung) ist er, auch die IR-LED funktioniert prächtig.
Jetzt die Preisfrage: UND NU?

Ich habe den Artikel im RN-Wissen (über den ADC) zwar um Hilfe bemüht, aber vergebens, 2/3 des dort beschriebenen Codes kapiere ich wieder nich. :(


Diese vielen ASDC, ASRaels und andere, meist mit A anfangenden Wörter sind einfach böhmische Dörfer für mich, trotz einiger Kommentare im Text.
Alleine diese Zeile:

// Den ADC aktivieren und Teilungsfaktor auf 64 stellen
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);

sagt mir praktisch gar nix. 8-[
Immerhin glaube ich, herausgefunden zu haben, dass ich den Teilungsfaktor so lassen kann, obwohl meine Biene ja doppelt so schnell tickt.
Aber: wie kriege ich es beispielsweise hin, dass am Port PC3 gemessen wird?

Das müsste, meinem lausigen Verständnis nach, ja hier drinnen dann passieren:

// Kanal des Multiplexers waehlen
// Interne Referenzspannung verwenden (also 2,56 V)
ADMUX = channel | (1<<REFS1) | (1<<REFS0);
ODER?

syn_error
25.05.2010, 23:01
Alleine diese Zeile:

// Den ADC aktivieren und Teilungsfaktor auf 64 stellen
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);

sagt mir praktisch gar nix.
das ist schlecht, aber genau dafür gibt es datenblätter.

ADCSRA = analog digital converter status register a.
ADEN = analog digital enable
usw.

könnte man fast selbst drauf kommen.


wie kriege ich es beispielsweise hin, dass am Port PC3 gemessen wird?
...
// Kanal des Multiplexers waehlen
// Interne Referenzspannung verwenden (also 2,56 V)
ADMUX = channel | (1<<REFS1) | (1<<REFS0);
ODER?
dazu muss man den passenden adc kanal wählen.
welcher das ist und was man dazu einstellen muss, kann man im Analog-to-Digital Converter abschitt im datenblatt nachlesen.

Rabenauge
26.05.2010, 16:55
das ist schlecht, aber genau dafür gibt es datenblätter.

Danke, das hilft mir _nicht_ weiter, ich werd schlichtweg aus dem Kauderwelsch im Datenblatt _nicht_ schlau.
Ein paar Dinge begreife ich zwar, aber (ich sitze nun mehr oder weniger zwei Stunden vor dem Ding) ich habe noch immer _keine_ Ahnung, wie zum Teufel ich einfach nur den Wert von PC3 einlesen soll.
Mehr will ich doch erst mal gar nicht, vermutlich istd as das enzige, woran ich überhaupt hänge (wenn nicht, sag ichs dann schon).

Aus dem ADC-Tutorial von RN-Wissen:

Je nach Einsatzart muss das Beispielprogramm entsprechend abgeändert werden (also diese Zeile mit ADMUX |= (1<<REFS1) |

Soso. Schön. Wenn mir einer erklärt, WAS und WIE ichs ändern muss, tu ich das sofort. [-(

Ich lese überall, dass man einen bestimmten Kanal wählen muss (denk ich mir, das leuchtet irgendwie ein) aber WELCHEN und WIE erkläre ich das dem Prozessor?
Wie finde ich heraus, welcher Kanal mein PC3 ist?

oberallgeier
26.05.2010, 17:33
Hi Rabenauge,

ich gehe jetzt mal davon aus, dass Du einen mega16 im Bee drin hast. Leider habe ich für den grad nur ein altes Datenblatt hier von 2007: 2466P–A VR–08/07, das dürfte aber in anderen ähnlich drinstehen.


... Beispielprogramm entsprechend abgeändert werden ... ADMUX |= (1<<REFS1)
Wie finde ich heraus, welcher Kanal mein PC3 ist?Suche mal im Datenblatt nach "Input Channel and Gain Selections" - da müsste es eine Tabelle geben. Extra um Dich (und mich und andere) zu verwirren, hat At mel nämlich die Ports mit den verschiedenen Namen versehen *ggg*). Der PC3 des m16 ist als ADC-Port GARNICHT zu benutzen :( . In den docs steht, meist auf Seite 2, eine Zeichnung mit den verschiedenen Portnamen. Bei meinem Doc steht da der ADC3 am PORT A - also als PA3 - nun würde ich gern wissen, wie Du auf PC3 kommst. Wie gesagt, der lässt sich NICHT als ADC-Eingang benutzen.

Rabenauge
26.05.2010, 18:39
Aus dem Schaltplan der Biene: laut _dem_ liegen am Port X3 (das ist der rechte, vorne, neben der Nase) an:

AN3
PC3
VCC
GND

Die letzten beiden sind eh klar, an AN3 habe ich das Servo liegen.
Funktioniert auch wunderbar.
Im Thread: Nibobee-Erweiterungen las ich, dass an den Port _ausserdem_ ein Sharp (eben zusätzlich zum Servo) angeschlossen werden kann, haben wohl einige auch erfolgreich gemacht.
Daher denke ich mir, dass das geht. Ich vertrau da den Profis einfach.
Sollte es wirklich nicht gehen, müsste ich den Sharp halt wo anders anschliessen, die Frage wäre nur, wo...

Tante Edit ergänzt:
Oook, die Blödheit (oder mein Kabelgewusele?) hat mich wieder voll erwischt: Die Signalleitung des Servos hängt natürlich an PC3!
](*,)
Ab und an hilft es doch, sich seine Programme mal wieder im Ganzen anzusehen.... :-b
Damit ist nun (nein, eigentlich die ganze Zeit schon) AN3 der analoge Eingang, und es sollte funktionieren.

Der Einfachheit halber würde ich gerne den gleichen Code, den ich für die Spannungsmessung benutze, wiederverwerten, von dem weiss ich, er funktioniert. Ok, ein paar Variablen müssen geändert werden, aber das ist nix.
Genügt es dafür, die Zeile:

while ((ADMUX & 0x07) != 0x04);
umzustellen und wie?
_So_ liefert mir der Code brav den ADC-Wert des Spannungsteilers vom Akku..

oberallgeier
27.05.2010, 08:18
Ahhhh - JETZT macht es Sinn. Gestern abends hatte ich nur gelesen, dass Du den Ser vo an AN3 hättest - und hatte spätabendsfastmitternacht gegrübelt, wie über den PC3 ein analoges Signal ausgewertet wird (schon klar, man könnte das Sharp-Ergebnis auch einfach dann auswerten, wenn es einen Pegel - low oder high - über-/unterschreitet). Und dazu bin ich dann "alles" durchgegangen. Deinen Kommentar von Tante Edit hatte ich erst heute morgen gelesen. Klar, AN3 ist der Controllereingang PA3/ADC3 (denn - warum sollte man diesen Eingang so benennen, wie es At mel macht - etwas Abwechslung törnt ja die Gehirntätigkeit an) und damit kannst Du analoge Signale messen. Wenn Du das sowieso schon machst mit Deinem Spannungsteiler am AN3 dann muss der gleiche Code ja auch zur Spannungsmessung für den Sharp funktionieren.

Ich hatte bei meiner Anwendung mit dem Sharp, da aber mit dem GP2D120, mir nach Hardware- und Softwareinstallation erstmal eine Eichkurve für den Sharp erstellt. Danach hatte ich aus dieser Messkurve >>in MEINEM System gemessen!!<< den Umrechnungsfaktor abgeleitet und eine entsprechende Softwareanpassung mit Angabe der Entfernung in üblichen Einheiten (ich rechne NICHT in russischen Werst oder althannoveraner Ellen!) geschrieben. Aufpassen: der Sharp, zumindest mein GP2D120, ist bei seinen Ergebnissen ziemlich abhängig von der Eingangsspannung - und der GP2D120 spuckt Störungen zurück. (https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=37624&highlight=) Die Eichkurve sieht so aus, der Thread dazu ist im Bild verlinkt.

. . . . . . . . http://oberallgeier.ob.funpic.de/GP2D120-ADC.jpg (https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=384168&sid=f6a4399a12636483f22ef17352dac544#384168)

Viel Erfolg.

Rabenauge
28.05.2010, 11:20
Sodele. Der Sharp läuft, und zeigt auch _brauchbare_ Werte an, aber:
Es gibt ein neues Problem.
Offenbar verträgt sich _diese_ ADC-Routine nicht mit der, die mir die Akkuspannung ermittelt. :-k

Konkret sieht es so aus, dass beim "hochfahren" so ziemlich als erstes mal die Akkuspannung ermittelt wird, wenn der einen bestimmten Wert unterschreitet, machts keinen Sinn, weiterzumachen.
So weit klappt das auch, dann wird das Schwenkservo mal kurz getestet und dann der Sharp abgefragt.
Keinerlei Probleme, bis dahin.
Nun will ich aber, Timergesteuert, von Zeit zu Zeit den Akku immer wieder mal prüfen, und _dann_ hängt _diese_ Routine sich auf.



//----------------------------------------- Sharp-IR-Sensor ---------------------------------------

int Sharp(void)
{

ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);// Frequenzvorteiler: setzen auf 128 (16 MHz / 128kHz = 125 kHz) und ADC aktivieren
ADMUX = 0x03; // Kanal waehlen (ADC3)
ADMUX |= (1<<REFS1); // interne Referenzspannung nutzen
ADCSRA |= (1<<ADSC); // eine ADC-Wandlung
while ( ADCSRA & (1<<ADSC) ); // auf Abschluss der Konvertierung warten
sharpResult = ADCW; // Wert abholen
lcd_setCursor(0,1); // Ausgabe ADC-Wert
printf("Sharp: %4d",sharpResult);

return 0;
}
//----------------------------------------- Spannungsüberwachung ----------------------------------
int Batterie(void)
{
led_set(LED_L_RD,1); // blitzt bei jeder Messung kurz auf
unsigned char temp1,temp2;
while ((ADMUX & 0x07) != 0x04);
cli();
while (!(ADCSRA & (1 << ADIF))); // wait for conversion complete
ADCSRA |= (1 << ADIF); // clear ADCIF
temp1 = ADCSRA; // Registerinhalte retten
temp2 = ADMUX;

ADMUX = (1 << REFS0) | (1 << REFS1) | ANALOG_VOLT; //interne Referenzspannung m. externem Kondensator

// ADCSRA löschen und neu setzen
ADCSRA = 0;
ADCSRA |= ((1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0)); // ADC clock = Clock/128
ADCSRA |= (1 << ADEN); // Enable ADC (das aktiviert die 2.56V Referenz)

// Warten bis Kondensator an ARef = 2.56V hat
// Messung an ARef ergab 1,2msec
delay(6); // = ca. 5*Tau

// 1. ADC Wandlung starten und Ergebnis ignorieren
ADCSRA |= (1 << ADSC); // Start conversion
while (!(ADCSRA & (1 << ADIF))); // wait for conversion complete
ADCSRA |= (1 << ADIF); // clear ADCIF

// 2. ADC Wandlung starten und Ergebnis übernehmen
ADCSRA |= (1 << ADSC); // Start conversion
while (!(ADCSRA & (1 << ADIF))); // wait for conversion complete
ADCSRA |= (1 << ADIF); // clear ADCIF

akkuResult = ADCL + (ADCH << 8);

// Registerinhalte wiederherstellen
ADMUX = temp2;
ADCSRA = temp1 & ~(1 << ADSC);

// Warten bis Kondensator an ARef = AVcc hat
// Messung ergab sehr steile Flanke
delay(2); // nicht notwendig, nur zur Sicherheit

ADCSRA |= (1 << ADSC); // Start conversion

sei();
.....hier kommt noch ein bisschen Rechnerei und die Displayausgabe

Lasse ich den periodischen Aufruf von Batterie() weg, klappt alles hervorragend.
Die Routine Sharp() wird im Hauptprogramm in einer Endlosschleife praktisch ununterbrochen aufgerufen, nebenbei zählt ein Timer hoch und löst, bei einem bestimmten Wert (so alle 15s) die Routine Batterie() auf.
Die LED (sie wird später, während der Umrechnung des ADC-Wertes in brauchbares wieder ausgeschalten) bleibt an, daher _muss_ der Punkt, an dem sich das Programm aufhängt, irgendwo in Batterie() liegen.
Alleine (wenn ich Sharp() _nicht_ aufrufe) funktioniert Batterie() einwandfrei.

Weiss jemand Rat?

Skroete
28.05.2010, 18:59
Hallo Rabenauge,

um klarzustellen was da passiert, muss man etwas in den Eingeweiden der Nibobee wühlen.
Wird mit analog_init() der ADWandler gestartet, so arbeitet er "interrupt getrieben" vor sich hin. D.h. sobald die Wandlung abgeschlossen ist, löst er einen Interrupt aus. In der ADC-Interruptroutine (siehe unten den Teil des listing von analog.c) wird das Resultat abgeholt (analog_storeResult(analog_pos)), ein neuer Kanal eingestellt (++analog_pos...) und die nächste Wandlung gestartet (analog_setupNext(analog_pos)).

ISR(ADC_vect) {
analog_storeResult(analog_pos);
if (++analog_pos>10) {
analog_pos=0;
}
analog_setupNext(analog_pos);
}

Das macht er für 11 analoge Kanäle (Warum 11 kommt später).

Mit deiner neu Konfiguration von ADCSRA in deiner Sharp(void) bringst du diesen Automatismus zum Stehen. Dadurch funktioniert nun die Abfrage der Liniensensoren nicht mehr und "meine" Batteriespannungsmessung hängt sich auf, denn sie setzt auf die laufenden Interrupts.
Eigentlich brauchst du deine Sharp(void) nicht, denn das Resultat von ADC3 findest du auch im Array analog_samples[..] (siehe analog.c) als 3. Eintrag, denn ANALOG_EXT3 = 3 (siehe analog.h).

Also mit

sharpResult = analog_samples[3];

erhälst du den Wert von ADC3 ganz automatisch.
Das funktioniert für alle AD-Werte bis auf die Batteriespannung, für die ich dieses "workaround" geschrieben habe (detaillierte Erklärung siehe nibobee: Akkuspannung mit ADC messen).

Nun noch ein Wort zu den 11 analogen Kanälen. Eigentlich hat der ATMega16 nur 8 Kanäle. Die Liniensensoren (3 analoge Werte) werden jedoch 2 mal gelesen. Nämlich einmal mit eingeschalteten IR-LED und einmal mit ausgeschalteten IR-LED.
Die Werte bei abgeschalteten IR-LED sind hier zu finden
case 4: analog_samples[ANALOG_L0]=value; break;
case 5: analog_samples[ANALOG_C0]=value; break;
case 6: analog_samples[ANALOG_R0]=value; clear_output_bit(IO_LINE_EN); break;

Die Werte bei eingeschalteten IR-LED sind hier zu finden
case 8: analog_samples[ANALOG_L1]=value; break;
case 9: analog_samples[ANALOG_C1]=value; break;
case 10: analog_samples[ANALOG_R1]=value; set_output_bit(IO_LINE_EN); break;

Es gibt nun 2 Möglichkeiten.
1. Du benutzt sharpResult = analog_samples[3]; und lässt das ADCSRA in der Sharp(void) in Ruhe.
2. Du schreibst ebenfalls ein "workaround" das ungefähr so aussieht (nicht getestet, nur ein Beipiel)

int Sharp(void)
{
unsigned char temp1,temp2;
uint16_t sharpResult;

while ((ADMUX & 0x07) != 0x03); //warten bis ADC3 dran ist

cli();

while (!(ADCSRA & (1 << ADIF))); // wait for conversion complete
ADCSRA |= (1 << ADIF); // clear ADCIF

//Registerinhalte retten
temp1 = ADCSRA;
temp2 = ADMUX;

//hier müssen die Teile deiner Sharp(void) rein

sharpResult = ADCL + (ADCH << 0x08);

//Registerinhalte wiederherstellen
ADMUX = temp2;
ADCSRA = temp1 & ~(1 << ADSC);


ADCSRA |= (1 << ADSC); // Start conversion

sei();

return sharpResult;
}


Viel Erfolg
Skroete

Rabenauge
28.05.2010, 21:17
Also mit

sharpResult = analog_samples[3];

erhälst du den Wert von ADC3 ganz automatisch.

Nee, oder? Ich sitze vier Tage an dem Problem, und da kommst du daher, grienst dir einen und sagst dass es mit zwei Zeilen abgeht?
Erstmal ein grosses Danke, das löst gleich ein halbes dutzend Probleme, die später noch gekommen wären.

Ja, ich habs probiert, und es funktioniert auch, aber...nicht ganz richtig.
Von weitem klappt das gut, ab ca. nem Meter misst der Sharp brauchbar (ich glaub, ab 80cm _sollte_ er erst), aber im Nahbereich....mit meiner Methode war ich bei ca. 10cm Entfernung dann bei 1023, jetzt bin ich, wenn das Hindernis 10cm weg ist, gerade mal bei ungefähr 600, und wenn ich näher als 5cm rangehe, sinkt der Wert wieder.
Oook, man _könnte_ damit durchaus leben, das messen an sich funktioniert ja, aber der volle Wertebereich hätte auch was- dann nämlich kann man im Nahbereich superfein messen, wie ich festgestellt habe.
Geht das noch besser?
Oder hab ich wiedermal Mist gebaut?

Da der Compiler mit mir schimpfte, als ich _nur_ :

sharpResult = analog_samples[3];

schrieb, habe ich im Kopf des Programmes noch hinzugefügt:

int analog_samples[10];

Schon gibts keine Fehler-oder Warnmeldungen mehr.
Hab ich da auch was vertrottelt oder...

[/i]

Skroete
29.05.2010, 11:58
Hallo Rabenauge,

du hast Recht. So
sharpResult = analog_samples[3];
funktioniert es nicht. Ich habe leider versäumt es zu testen.

Richtig müsste es heissen
sharpResult=analog_getValue(ANALOG_EXT3);

Im Kopf deines Programms muss natürlich
#include <nibobee/analog.h>
rein und zu Beginn des Hauptprogramm die Zeile
analog_init();

Damit gibt es keine Fehlermeldungen mehr und deine Zeile
int analog_samples[10];
kann raus.
Ob damit das Problem mit der Auflösung des Abstands ebenso erledigt ist,
musst du ausprobieren.
Es gibt nämlich ein Problem bei dieser Methode. Wie ich schon schrieb wurschtelt der AD-Wandler fröhlich in seinem Interrupt vor sich hin. Wenn deine Abstandsroutine nun eine gewisse Synchronität erwartet (z.B. die LED wird eingeschaltet und dann soll gemessen werden), dann musst du dem AD-Wandler genügend Zeit geben, denn er könnte ja gerade dabei sein, alle anderen Kanäle abzuklappern.
Wenn es nicht funktioniert, dann hilft nur, die AD-Wandlung von ADC3 zu Fuss zu machen und deine Routine so einzubinden, wie ich es in der letzten Antwort beschrieben habe.

Eine weitere Möglichkeit wäre dann auch ganz auf die Nibobee AD-Wandlung zu verzichten.
Denn dann hast du die Batteriemessung und die ADC3-Messung schon zu Fuss gemacht und die Nibobee AD-Wandlung macht eigentlich nur noch die Wandlung der Liniensensoren. Die kann man dann auch noch zu Fuss machen.
Also kein
analog_init();
mehr und alles zu Fuss.


Viel Erfolg
Skroete

Rabenauge
29.05.2010, 14:06
Nein, du hast mich falsch verstanden, es funktioniert schon.

Nur wird offenbar nicht die maximale Auflösung des Sharp genutzt- es müssten ja Werte zwischen Null und 1023 möglich sein.
Ich komme aber selbst bei maximaler Annäherung an ein Hindernis nicht wirklich über den Wert 650 hinaus. Gehe ich noch näher ran (das ist dann allerdings unter 5cm und somit eh ausserhalb des Arbeitsbereiches dieses Sharp), sinkt der Wert wieder.
Aber deine vorgeschlagene Lösung


sharpResult=analog_getValue(ANALOG_EXT3);

probiere ich trotzdem mal aus, auch wenn ich denke, sie wird das selbe Ergebnis liefern.

Tante Edit merkt an: Es macht keinen Unterschied, beide Methoden funktionieren gleichermassen.
Auch das Ergebnis (inzwischen habe ich die Messung in einer Schleife laufen, um den Durchschnittswert zu ermitteln) ist in beiden Fällen das selbe.

Skroete
29.05.2010, 16:00
Hallo Rabenauge,

eine letzte Idee habe ich noch.
Mit dem
analog_init();
startest du die analogen Kanäle.
Wie ich schrieb werden dabei auch die Liniensensoren bedient und 2mal abgefragt, 1mal mit eingeschalteter IR-LED und 1mal mit ausgeschalteter IR_LED.
D.h. die IR-LED des Liniensensors blinken fröhlich vor sich hin.
Als du in deiner Sharp() Routine mit dem Beschreiben von ADCSRA die ganze AD-Wandler Geschichte abgewürgt hast, konnten somit auch die IR-LED des Liniensensors nicht mehr vor sich hin blinken.
Ich vermute nun, dass die IR-LED des Liniensensors über Streuungen das Ergebnis des Sharp beeinflussen.
Ich glaube, du musst dir doch deine eigene ADC3-Routine schreiben, in der du dafür sorgst, dass die IR-LED des Liniensensors aus sind, während du den Sharp abfragst.
Die IR-LED kannst du übrigens mit einer Handy Kamera prüfen, ob sie leuchten oder nicht. Aber das dürfte sich ja mittlerweile schon rumgesprochen haben.

Die IR-LED sollten zum Zeitpunkt der ADC3-Wandlung eigentlich aus sein, wenn du dir den Code von analog.c und analog.h anschaust. Vielleicht besitzt der Sharp aber auch eine AGC (automatische Verstärkungsregelung) wie der IR-Remote-Receicer SFH5110-36 und wird von den IR-LED des Liniensensors zugestopft.

Viel Erfolg
Skroete

Rabenauge
29.05.2010, 17:17
Im aktuellen Programm ist der Liniensensor gar nicht in Aktion (bisher nicht, hab ich aber vor).
Dass da eine Beeinflussung stattfindet kann ich mir auch nicht vorstellen: erstens habe ich eine Maske vor dem Liniensensor (da _kann_ kein Licht nach vorne raus) und zweitens sitzt der Sharp mechanisch noch 3-4 cm weiter vorne und schaut auch nach vorne.
Also dass das Licht der Liniensensoren irgendwie den Sharp erreicht, ist praktisch eigentlich unmöglich.
Eben versucht:
Selbst, wenn ich die Liniensensoren komplett abdecke, ändert sich daran (an den gelieferten Werten) rein gar nichts.
Eine Idee dazu habe ich allerdings (werde sie aber jetzt wirklich nicht weiter verfolgen): ist es möglich, dass das "Problem" damit zusammenhängt, dass in der Bienenbibliothek mit der externen Referenzspannung gemessen wird?
Die habe ich nämlich, in den letzten Tagen, probehalber auch mal benutzt und da ergeben sich natürlich andere Resultate.

Also gut: um alles neu zu machen, fehlen mir (noch) die Kenntnisse, ich denke, ich kann mit der jetztigen Lösung leben. Wenn ichs recht bedenke, kann ich bereits auf ca. 20cm Entfernung milimetergenau messen, das reicht eigentlich. ;)
Damit erkläre ich das Ziel "Hinderniserkennung mit dem Sharp-Sensor) für erreicht und überlege mir jetzt, wie man damit optimal navigieren kann, ich hab da etwas mehr vor als nur einfach Hindernisse zu umgehen, wenns soweit ist.