PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : ACS an NIBObee



pacman
05.06.2011, 09:44
Hallo,

ich möchte gerne ein ACS an meine NIBObee bauen.
Was ist dafür besser geeignet, Ultraschall oder Infrarot? Oder hat einer vielleicht noch eine andere, bessere Lösung?
Die zweite Frage wär, ob man diese über die freien Analoganschlüsse der NIBObee verbinden kann. (bin neu auf dem Gebiet, könnt sein dass die Frage dumm ist :))

mfG pacman

radbruch
05.06.2011, 09:53
Infrarot funktioniert gut:

https://www.roboternetz.de/community/showthread.php?46233-nibobee-IR-Abstandsmessung

pacman
05.06.2011, 10:09
mh.. also wenn ich mir den thread so durchlese, wird es wohl eine kompliziertere Angelegenheit als ich gedacht hatte ;D..
ich möchte jetzt keine richtige, genaue Abstandsmessung, sondern nur, dass der Bot erkennt, dass zB halb links ein Gegenstand ist. Da müssten doch ein paar Infrarot-LEDs und ein TSOP reichen oder?

radbruch
05.06.2011, 10:22
So arg kompliziert ist es auch wieder nicht. Der Aufbau ist Minimal und die Software ist quasi fertig. TSOP und IR-Leds (jeweils mit Vorwiderständen) reichen aus, aber viel mehr ist die vorgestellte Lösung ja auch nicht.

pacman
05.06.2011, 11:39
stimmt, ist im Prinzip ja eigentlich fast das selbe:) .. jedenfalls, vielen Dank für deine Antworten, ich schau dann mal was ich nehme.

pacman
05.06.2011, 13:26
radbruch, könntest du mir vielleicht noch einmal helfen :) ... da ich eben einen TSOP bei mir gefunden habe, habe ich mich entschlossen mein ACS damit zu bauen. Ich habe leider keine Ahnung wie ich den in Verbindung mit den IR-LEDs anschließen soll.:( (wie gesagt, ich bin neu auf dem Gebiet)
Ich bin um jede Hilfe dankbar!

radbruch
06.06.2011, 08:32
Es sind zwei IR-LED jeweils mit Vorwiderstand und ein 36kHz-IR-Empfänger mit Vorwiderstand und Glättungskondensatoren nach Datenblatt (z.B. der SFH5110 (http://www.w-r-e.de/robotik/data/sfh5110.pdf)). Der Anschluß ist im Kopf der Programme beschrieben:


// Die Anoden der rechten IR-LEDs hängen mit Vorwiderstand an PA0 (X1-AN0)
// Die Anoden der linken IR-LEDs hängen mit Vorwiderstand an PA1 (X1-AN1)
// Die Kathoden beider IR-LEDs sind mit PA3 (X3-AN3) verbunden
// Der Empfänger SFH5110 ist mit PA2 (X2-AN2) verbunden

#define ddr_acs_led_l DDRA // Anoden der IR-LEDs links
#define port_acs_led_l PORTA
#define pin_acs_led_l (1<<PA1)

#define ddr_acs_led_r DDRA // rechts
#define port_acs_led_r PORTA
#define pin_acs_led_r (1<<PA0)

#define ddr_acs_36kHz DDRA // Kathoden der IR-LEDS mit 36kHz getaktet
#define port_acs_36kHz PORTA
#define pin_acs_36kHz (1<<PA3)

#define ddr_acs_tsop DDRA // Eingang IR-Empfänger
#define port_acs_tsop PINA // Achtung, das ist ein Eingang!
#define pin_acs_tsop (1<<PA2)



Das ist aber eher als Vorschlag zu betrachten und kann natürlich an die eigene bee angepasst werden.

Zum Thema IR-Abstandsmessung wurde auch von oberallgeier etwas Grundlagenforschung betrieben:

https://www.roboternetz.de/community/showthread.php?33984-Abstandsmessung-%E4hnlich-wie-IR-Schnittstelle-des-asuro

http://www.mikrocontroller.net/topic/125704

Aufbau beim asuro:
https://www.roboternetz.de/community/showthread.php?50714-Lichtschranke-funktioniert-nicht-korrekt&p=489253&viewfull=1#post489253

pacman
08.06.2011, 14:39
Hallo,
ich habe mein NIBObee mit 4 IR-LEDs erweitert. Das Problem ist jetzt das Programm zu schreiben. Ich bin Programmieranfänger und habe versucht ein Programm zu schreiben, wo zumindest mal die LEDs leuchten. Mit dem Code hier, klappt es allerdings nicht (was für eine Überraschung :)):

#include <nibobee/iodefs.h>
#include <nibobee/led.h>
#include <nibobee/delay.h>

#define ddr_acs_led_l DDRA
#define port_acs_led_l PORTA
#define pin_acs_led_l (1<<PA1)

#define ddr_acs_led_r DDRA
#define port_acs_led_r PORTA
#define pin_acs_led_r (1<<PA0)

#define ddr_acs_36kHz DDRA
#define port_acs_36kHz PORTA
#define pin_acs_36kHz (1<<PA3)

#define ddr_acs_tsop DDRA
#define port_acs_tsop PINA
#define pin_acs_tsop (1<<PA2)

int main()
{led_init();
while(1==1)
{ led_set(ddr_acs_led_l, 1);
led_set(ddr_acs_led_r, 1);
}
return 0;
}

Ich weiß, wenn ihr den Code seht, werdet ihr mich wahrscheinlich auslachen :) ..aber könnt ihr mir vielleicht trotzdem weiterhelfen?

radbruch
08.06.2011, 16:51
Hallo

Wenn man an den freien Erweiterungssteckern der bee etwas anschliesen möchte, kann man die Befehle der Library nicht so ohne weiteres verwenden. Man muss vielmehr die entsprechenden IO-Pins "von Hand" ansteuern. Dazu muss man allerdings ein paar Grundlagen kennen:

Die Ein- bzw. Ausgänge des Kontrollers sind zu Ports mit (meist) 8 einzelnen Pins zusammengefasst und von 0 bis 7 durchnummeriert. Jeder Pin kann ein Eingang oder ein Ausgang sein, die jeweilige Funktion kann für jeden Pin einzeln festgelegt werden. Als Ausgang kann ein Pin entweder ein low (0V) oder ein high (5V) ausgeben. Als Eingang können alle Pins digital eingelesen werden, einige besondere (meist ein kompletter Port) können auch analoge Signale verarbeiten. Bei allen als Eingang verwendeten Pins kann man Bedarf einen zusätzlichen internen PullUp-Widerstand (OpenKollektor) aktivieren.

Ohne PullUp ist ein Eingang sehr hochohmig, ein Pin kann deshalb drei Zustände annehmen: High, Low und Hochohmig (oder Z-State). Das ist wichtig für die gebräuchlichen Kommunikationsprotokolle wie SPI oder TWI(I2C) und sprengt den Rahmen des Beitrags ;)

Jeder Port besitzt drei Register, das Eingangsregister, das Ausgangsregister und das Datenrichtungsregister. Sie ermöglichen den Zugriff auf die Daten und die Funktion des Ports. Diese Register sind 8 Bit (= ein Byte) breit, jedes Bit entspricht einem Pin im jeweiligen Port. Die Nummer des Pins bezeichnet deshalb die Stelle des Pins als Zweierpotenz im Registerbyte, 2^0 (mit Wert 1!) ist Pin0, 2^1 (Wert 2) ist Pin1 bis 2^7 (Wert 128) für Pin7.

Mit dem Datenrichtungsregister (DDR + Portname A-D) entscheidet man, ob die Pins eines Ports jeweils als Ein- oder Ausgang geschaltet sind. Jeder Pin, dessen Bit im Registerbyte 1 ("gesetzt") ist, ist ein Ausgang, jeder Pin mit einer 0 in seinem Bit ("nicht gesetzt") ist ein Eingang. Ist ein Pin als Ausgang geschaltet, kann man mit dem entsprechenden Bit im Ausgangsregister (PORT + Portname) mit einem gesetzten Bit ein High und einem gelöschten Bit ein Low ausgeben. Wenn der Pin als Eingang geschaltet ist, wird das entsprechende Bit im Eingangsregister (PIN -Portname) in Abhängigkeit vom angelegten Spannungspegel gesetzt oder gelöscht. Bei Eingängen schaltet das Ausgaberegister die internen PullUp-Widerstände. Ein gesetztes Bit schaltet den PullUp für den Pin ein, ein gelöschtes Bit schalten ihn aus.

Verwirrt? Keine Panik, das kann man alles auch in den bekannten AVR-Tutorials nachlesen. ;) Nach so vielen Grundlagen kommen wir nun endlich zur Praxis.

Bei Start nach dem Einschalten oder einem Reset sind alle Pis auf Eingang ohne PullUp geschaltet. Das ist eine Vorsichtsmassnahme um zu verhindern, dass etwas abraucht. Die Init-Funktionen der Library setzt dann in den diversen Registern die zur externen Hardware passenden Bits. Bei einer eigenen Erweiterung muss man diese Bits selbst richtig setzen.

Als Beispiel schliesen wir eine LED mit ihrem Vorwiderstand zwischen Pin0 von PortA (PA0) und GND (0V) an, die Kathode (der Strich im Schaltplan) zeigt dabei nach GND. Diese LED würde leuchten, wenn am PA0 5V (ein High) ausgegeben wird. Also müssen wir das Bit im Datenrichtungsregister setzen um einen Ausgang zu erhalten und das Bit im Ausgangsregister muss gesetzt sein für ein High:

DDRA = 1;
PORTA = 1;
while(1);

Jetzt sollte die LED leuchten :)

Blöderweise haben wir so aber alle anderen Pins vom Port A auf Eingang geschaltet, das stört natürlich alle anderen Funktionen an diesem Port. Viel besser wäre es, wenn wir nur das benötigte Bit manipulieren würden und alle anderen Bits so lassen würden, wie sie sind. Das erreichen wir mit einer bitweisen Verknüpfung (nicht mit einer logischen Verknüpfung verwechseln!) des alten Registerwertes mit dem Bitwert für den zu ändernden Pin. Dazu muss man zuallererst den Registerwert auslesen, dann die gewünschte Verknüpfung ausführen und dann erst das Ergebniss wieder ins Register zurückschreiben. Für unser Beispiel mit der LED an PA0 würde das so aussehen:

DDRA = DDRA | 1;
PORTA = PORTA | 1;

Oder die Kurzform:

DDRA |= 1;
PORTA |= 1;

(Beide Schreibweisen erzeugen den selben Programmcode!)

Ächz. Mehr zum Thema Bitmanipulationen findet man auch wieder in den Tutorials. Ich hatte das hier schonmal angeschnitten:
https://www.roboternetz.de/community/showthread.php?28448-Probleme-mit-if-Bedingungen&p=271532&viewfull=1#post271532

Es wäre natürlich schlau alle Fragen zu deiner IR-Erweiterung in einem Thread zusammenzufassen. ;)


ich habe mein NIBObee mit 4 IR-LEDs erweitert.Desweiteren wäre ebenfalls schlau mitzuteilen, wo und wie die LEDs angeschlossen wurden. ;)

Gruß

mic

pacman
08.06.2011, 19:58
Hallo radbruch,
ich finde es echt super, dass du dir die Mühe machst und die Zeit nimmst mir alles so genau zu erklären!

Desweiteren wäre ebenfalls schlau mitzuteilen, wo und wie die LEDs angeschlossen wurden. ;)
Ich habe deinen Ratschlag befolgt und die Anoden der rechten IR-LEDs an Signal 1 von X1 angeschlossen, die Anoden der linken an Signal 2 von X1, die Kathoden beider Seiten an Signal 1 von X3 und den Empfänger an Signal 1 von X2.
Fragen: 1. Also muss ich, wenn ich es richtig verstanden habe, die Kathoden der LEDs als Ausgang als low definieren?
2. Wie kann ich die eingehenden Daten, die von meinem Empfänger ausgehen, sehen? (sprich die Werte)
3. Muss ich die +/- Anschlüsse von z.B. X1 auch aktivieren? (für den Empfänger)
4. Wie kann ich in einem Code schreiben, dass die LEDs z.B. jede Sekunde leuchten? Kann ich da auf die delay.h zurückgreifen?
5. Muss ich an den Projekteinstellungen im AVR-Studio etwas ändern?

radbruch
08.06.2011, 20:44
Hallo

Da ich selbst viele Anregungen und Infos aus dem Forum ziehe ist es nur fair, wenn ich auch etwas einbringe.

Es ist gut, wenn man die Anschlußbelegung im Programmkopf als Kommentar dokumentiert. Ich füge auch immer den Link zum entsprechenden Thread mit ein.

Zu 1: Ja, wenn die Kathoden auch auf einen Pin gehen muss dieser ein Low ausgeben. Aber Achtung! Ein Ausgang kann nur begrenzt viel Strom vertragen, egal ob High oder Low. 20mA sollten kein Problem sein, aber je nach Bemessung der Vorwiderstände für die IR-Leds muss man bei der Parallelschaltung beider Seiten diese Stromgrenze beachten. Vor allem bei der Dauerschaltung könnten gefährliche Werte auftreten, später im Pulsbetrieb ist es nicht mehr so kritisch. Vorsichtshalber erwähne ich es nochmals: Vorwiderstände nicht vergessen!

Zu 2: Eingehende Daten oder gar Werte gibt es nicht wirklich. Es wird nur der Pegel des TSOP-Ausgang geprüft. Wenn er ein 36kHz-IR-Signal erkennt schaltet dieser Ausgang von High nach Low.

Zu 3: Die +/- Anschlüsse der Erweiterungssteckplätze sind dauerhaft mit Vcc und GND verbunden und brauchen nicht extra aktiviert werden.

Zu 4: Ja, du kannst dazu vorerst auch die Funktionen aus delay.h verwenden, diese sind allerdings unter gewissen Umständen nicht sehr genau und die Wartezeit sollte unbedingt vor dem Kopilieren bekannt sein. Infos dazu stehen in Programmkopf von delay.h. Später kannst du den 36kHz-Takt auch als Zeitbasis für eine eigene Zeitfunktion nutzen.

Zu 5 kann ich nichts sagen, weil ich das AVR-Studio nicht verwende. Möglicherweise muss man delay.c einbinden.

Ich werde versuchen, diesen Thread an den ACS-Thread anzuhängen. Der befindet sich allerdings in einer Rubrik in der ich dann keine Mod-Recht mehr habe...

Gruß

mic

pacman
08.06.2011, 21:09
Vielen Dank!... ohne dich jetzt irgendwie nerven zu wollen, habe ich noch eine Frage bezüglich dem Programm :)
Ich habe also meine Anode an PA0 und meine Kathode an PA3. Also erst einmal PA0 als high definieren: DDRA = DDRA | 1; PORTA = PORTA | 1;
Wie definiere ich PA3 als low? Und wie bringe ich das ganze in ein Programm unter, das die LEDs zum leuchten bringt?

radbruch
08.06.2011, 22:17
Der Einstieg ist immer schwierig. Also machen wir mal weiter.

PA3 ist das dritte Bit von rechts in den Portregistern mit der Wertigkeit 2^3 oder 8 bzw. binär 0b00001000. Anhand der binären schreibweise (erkennbar an der vorangestellten 0b) versuche ich mal zu erklären, wie man in einem Byte genau dieses Bit3 löschen kann. Dazu verwenden wir die bitweise Und-Verknüpfung & (UMSCHALT und 6). Diese setzt im Ergebniss nur dann das Bit, wenn in beiden Operanten dieses Bit auch gesetzt war. Operanten sind in diesem Fall der Inhalt des Ausgaberegisters und ein Bitmuster, gewöhnlich nennt man es Maske, das nur dieses eine Bit beeinflusst. Als Programmcode sieht das dann so aus:

PORTA = PORTA & 0b11110111

Alle im Ausgangsregister schon gesetzten Bits bleiben gesetzt außer Bit3 (Zählung beginnt rechts mit 0!).

Um beide Pins "in einem Rutsch" zu ändern kann man die Masken auch kombinieren:

DDRA |= 0b00001001; // PA0 und PA3 als Ausgang betreiben
PORTA |= 1; // High an PA0 ausgeben
PORTA &= ~8 // Low an PA3 ausgeben

- Das Datenrichtungsregister muss man natürlich nur ändern wenn sich auch was geändert hat.
- Die Tilde ~ (ALTGR und +) bewirkt, dass alle Bits im nachfolgenden Wert invertiert werden, aus 1 wird 0, aus 0 wird 1 und aus der Maske 0b00001000 (oder 8) wird 0b11110111 ;)
- Die Kommentare sind für Anfänger, bei einem echten Programm würde ich dich dafür erschlagen! Besser wäre später als Fortgeschrittener z.B.:

// Pins für IR-LEDs auf Ausgang setzen
// Anoden mit 5V versorgen
// Kathoden an 36kHz

oder so ähnlich... ;)

Um das Thema abzurunden vielleicht noch ein paar Zeilen zur Erzeugung der Maske. Die binäre Darstellung ist recht unübersichtlich und fehleranfällig. Deshalb verwendet man häufig die Bitschiebereien mit dem << (oder eher selten >>) -Operator dazu. Erster Operant ist das Bitmuster welches rechtsbündig in die Maske geschrieben wird. Der zweite Operand gibt an, wie oft dieses Muster, entsprechend dem Operator, geschoben wird. Mit

Maske = 1 << 0

schreibt man in Bit 0 der Maske eine 1 und schiebt diese 0 mal nach links, Maske sieht dann so aus: 0b00000001. Ein paar weitere Beispiele:

Maske = 1 << 3 ergibt 0b00001000
Maske = ~(1<<3) ergibt 0b11110111
Maske = (1<<3) | (1<<0) entspricht 0b00001000 | 0b00000001 und ergibt 0b00001001

Wenn man nun noch weiß, wie #define funktioniert und dass in io.h die Portpins PA0 bis PA7 als 0 bis 7 definiert werden, kann man schnell recht übersichtliche Masken erzeugen:

DDRA |= (1<<PA3) | (1<<PA0);
PORTA |= (1<<PA0);
PORTB &= ~(1<<PA3);

So, Ende für Heute ;)

Viel Spass beim Ausprobieren.

mic

pacman
08.06.2011, 22:33
So viel Imput auf einmal...:) ..aber trotzdem vielen, vielen Dank für diese ausführliche Erklärung!
Ich denke ich werde mir das morgen alles noch einmal in Ruhe durchlesen, für Heute ist es zu spät :D

radbruch
09.06.2011, 11:22
Hallo

Die Threads sind jetzt zusammengeführt. Beim Durchlesen ist mir noch etwas aufgefallen:

"die Anoden der rechten IR-LEDs an Signal 1 von X1 angeschlossen, die Anoden der linken an Signal 2 von X1"

Das bedeutet, die LEDs sind parallel angeschlossen und jede LED hat einen eigenen Vorwiderstand? Einfacher wäre es, wenn die LEDs in Reihe geschaltet werden, so sieht das z.B. beim probot aus:

https://www.roboternetz.de/community/attachment.php?attachmentid=19010
(Bildausschnitt aus der probot-Beschreibung Seite 37)

Das sind zwar 3mm-LEDs und die Polung stimmt nicht, aber es zeigt das Prinzip. (Mit geänderter Ansteuerung würde das auch bei der bee funktionieren, aber es wäre unnötig komplizierter;)

Gruß

mic

pacman
09.06.2011, 12:33
Hallo,

ich habe die LEDs so angeschlossen wie in der angehängten Grafik.

Allerdings habe ich noch eine Frage zu dem Programm, das die LEDs erst einmal leuchten lassen soll. Am Anfang werden normal die header-dateien "included" und dann die Ports definiert? ..Und wie sieht die Endlos-Schleife dann aus?

radbruch
09.06.2011, 15:55
Hallo

Prima, allerdings scheint mir der 470er etwas groß. Aber vielleicht ist das auch ein Vorteil weil das ACS dann nicht so empfindlich ist.

Ich habe meine bee jetzt reaktiviert und ein kleines Demo zusammengebastelt. Da mir die Library der bee noch nie gefallen hat, und als kleine Herausforderung für mich, verzichte ich vorerst auf den Einsatz der Lib. Ich hoffe, die Funktionen sind trotzdem nachvollziehbar.

Da meine ACS-Erweiterung andere Pins verwendet habe ich die #defines auch für meine bee eingefügt und auskommentiert. Diese Version sollte deshalb auf deiner bee funktionieren:


// Übungen für ACS an Nibobee 9.6.11 mic

// https://www.roboternetz.de/community/showthread.php?53494-ACS-an-NIBObee

// Die Anoden der rechten IR-LEDs hängen mit Vorwiderstand an PA0 (X1-AN0)
// Die Anoden der linken IR-LEDs hängen mit Vorwiderstand an PA1 (X1-AN1)
// Die Kathoden beider IR-LEDs sind mit PA3 (X3-AN3) verbunden
// Der IR-Empfänger ist mit PA2 (X2-AN2) verbunden

// pacman-bee
#define ddr_acs_led_l DDRA // Anoden der IR-LEDs links
#define port_acs_led_l PORTA
#define pin_acs_led_l (1<<PA1)

#define ddr_acs_led_r DDRA // rechts
#define port_acs_led_r PORTA
#define pin_acs_led_r (1<<PA0)

#define ddr_acs_36kHz DDRA // Kathoden der IR-LEDS mit 36kHz getaktet
#define port_acs_36kHz PORTA
#define pin_acs_36kHz (1<<PA3)

#define ddr_acs_tsop DDRA // Eingang IR-Empfänger
#define port_acs_tsop PINA // Achtung, das ist ein Eingang!
#define pin_acs_tsop (1<<PA2)

/*
// radbruch-bee
#define ddr_acs_led_l DDRA // Anoden der IR-LEDs links
#define port_acs_led_l PORTA
#define pin_acs_led_l (1<<PA2)

#define ddr_acs_led_r DDRA // rechts
#define port_acs_led_r PORTA
#define pin_acs_led_r (1<<PA3)

#define ddr_acs_36kHz DDRC // Kathoden der IR-LEDS mit 36kHz getaktet
#define port_acs_36kHz PORTC
#define pin_acs_36kHz (1<<PC2)

#define ddr_acs_tsop DDRC // Eingang IR-Empfänger
#define port_acs_tsop PINC // Achtung, das ist ein Eingang!
#define pin_acs_tsop (1<<PC3)
*/

#include <avr/io.h>
#include <util/delay.h>

void leds(uint8_t status) // status bit0 bis 3 entspricht den LED0 bis 3
{
PORTB &= 0b11110000; // alle LED ausschalten
PORTB |= 0b1111 & status; // gewünschte LED einschalten
}

int main(void)
{
DDRB |= 0b1111; // PB0 bis PB3 sind die LED 0 bis 3
leds(0b1001); // LED 0 und 3 einschalten
_delay_ms(1000); // eine Sekunde warten
leds(0); // alle LED ausschalten

ddr_acs_led_l |= pin_acs_led_l; // linke ACS-LED Anode
ddr_acs_36kHz |= pin_acs_36kHz; // Kathoden beide LED
port_acs_36kHz &= ~pin_acs_36kHz; // Dauerlicht

while(1)
{
port_acs_led_l |= pin_acs_led_l; // linke IR-LED anschalten
leds(0b0010); // LED1 zur Kontrolle einschalten
_delay_ms(500);

port_acs_led_l &= ~pin_acs_led_l; // linke IR-LED ausschalten
leds(0); // LED1 zur Kontrolle ausschalten
_delay_ms(300);
}
return(0);
}


Gruß

mic

pacman
09.06.2011, 20:56
Ach, ich weiß gar nicht wie oft und viel ich dir noch danken soll :) ich habe das Programm mal geflasht. Die LEDs leuchten auch, aber wie stelle ich fest, ob die IR-LEDs auch leuchten? :) habe mal gehört man kann das mit einer Kamera irgendwie sehen? :)

radbruch
09.06.2011, 21:21
Mit einer Digicam/Handycam kann man das IR-Licht erkennen. Gegenprobe wäre eine Fernbedienung die auch von der Kamera gesehen werden kann. Meine IR-LEDs (und auch der lötempfindliche IR-Empfänger) sind gesteckt, zum Testen kann ich sie gegen normale LEDs austauschen.

pacman
11.06.2011, 09:58
Hallo!
Ich habe mal deinen Code geflasht:

// Die Anoden der rechten IR-LEDs hängen mit Vorwiderstand an PA0 (X1-AN0)
// Die Anoden der linken IR-LEDs hängen mit Vorwiderstand an PA1 (X1-AN1)
// Die Kathoden beider IR-LEDs sind mit PA3 (X3-AN3) verbunden
// Der IR-Empfänger ist mit PA2 (X2-AN2) verbunden

// pacman-bee
#define ddr_acs_led_l DDRA // Anoden der IR-LEDs links
#define port_acs_led_l PORTA
#define pin_acs_led_l (1<<PA1)

#define ddr_acs_led_r DDRA // rechts
#define port_acs_led_r PORTA
#define pin_acs_led_r (1<<PA0)

#define ddr_acs_36kHz DDRA // Kathoden der IR-LEDS mit 36kHz getaktet
#define port_acs_36kHz PORTA
#define pin_acs_36kHz (1<<PA3)

#define ddr_acs_tsop DDRA // Eingang IR-Empfänger
#define port_acs_tsop PINA // Achtung, das ist ein Eingang!
#define pin_acs_tsop (1<<PA2)


#include <avr/io.h>
#include <util/delay.h>

void leds(uint8_t status) // status bit0 bis 3 entspricht den LED0 bis 3
{
PORTB &= 0b11110000; // alle LED ausschalten
PORTB |= 0b1111 & status; // gewünschte LED einschalten
}

int main(void)
{
DDRB |= 0b1111; // PB0 bis PB3 sind die LED 0 bis 3
leds(0b1001); // LED 0 und 3 einschalten
_delay_ms(10000); // eine Sekunde warten
leds(0); // alle LED ausschalten

ddr_acs_led_l |= pin_acs_led_l; // linke ACS-LED Anode
ddr_acs_36kHz |= pin_acs_36kHz; // Kathoden beide LED
port_acs_36kHz &= ~pin_acs_36kHz; // Dauerlicht

while(1)
{
port_acs_led_l |= pin_acs_led_l; // linke IR-LED anschalten
leds(0b0010); // LED1 zur Kontrolle einschalten
_delay_ms(5000);

port_acs_led_l &= ~pin_acs_led_l; // linke IR-LED ausschalten
leds(0); // LED1 zur Kontrolle ausschalten
_delay_ms(3000);
}
return(0);
}

Bei diesem Programm leuchten zwar die LEDs wie vorgeschrieben, die IR-LEDs allerdings nicht. Die IR-LEDs sind alle richtig rum eingebaut und es gibt auch keine kalten Lötstellen. Danach habe ich mit dem Multimeter an den Anschlüssen von X1, X2 und X3 gemessen und festgestellt, dass dort überhaupt keine Spannung anliegt (bzw. 0,09 V).
Wie kann das sein?

radbruch
11.06.2011, 10:32
Zumindest zwischen + und - sollte an allen Erweiterungssteckern die Bordspannung messbar sein. Testweise kann man anstelle der Pausen einen Programmstop einfügen, dann hat man mehr Zeit zum Messen. Aus _delay_ms(5000); wird ein schlichtes while(1); (mit Semikolon!)

pacman
11.06.2011, 13:01
So, Problem gefunden, die Akkus waren fast leer. Jetzt klappt alles.
Mittlerweile habe ich auch ein Programm geschrieben was beide IR-LEDs leuchten lässt.
Könntest du mir jetzt vielleicht abschließend erklären, wie ich den TSOP einbinden kann? Sprich wie ich ihn definiere und wie ich ihn in if-Bedingungen einbinden kann.

radbruch
11.06.2011, 13:25
// Übungen für ACS an Nibobee 11.6.11 mic

// https://www.roboternetz.de/community/showthread.php?53494-ACS-an-NIBObee

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "acs_defines.h" // Definitionen als Headerdatei einbinden

void set_leds(uint8_t status)
{
PORTB &= 0xf0;
PORTB |= (0x0f & status);
}

int main(void)
{
ddr_acs_led_l |= pin_acs_led_l;
//port_acs_led_l |= pin_acs_led_l;
port_acs_led_l &= ~pin_acs_led_l;

ddr_acs_led_r |= pin_acs_led_r;
//port_acs_led_r |= pin_acs_led_r;
port_acs_led_r &= ~pin_acs_led_r;

ddr_acs_36kHz |= pin_acs_36kHz;
port_acs_36kHz &= ~pin_acs_36kHz;

DDRB |= 0x0f; // LEDs
set_leds(0b1001);
_delay_ms(1000);
set_leds(0);

while(1)
{
if(port_acs_tsop & pin_acs_tsop)
set_leds(0b0001);
else
set_leds(0b1000);
}
return(0);
}

Deine Datei acs_defines.h:

// pacman-bee

// Die Anoden der rechten IR-LEDs hängen mit Vorwiderstand an PA0 (X1-AN0)
// Die Anoden der linken IR-LEDs hängen mit Vorwiderstand an PA1 (X1-AN1)
// Die Kathoden beider IR-LEDs sind mit PA3 (X3-AN3) verbunden
// Der IR-Empfänger ist mit PA2 (X2-AN2) verbunden

#define ddr_acs_led_l DDRA // Anoden der IR-LEDs links
#define port_acs_led_l PORTA
#define pin_acs_led_l (1<<PA1)

#define ddr_acs_led_r DDRA // rechts
#define port_acs_led_r PORTA
#define pin_acs_led_r (1<<PA0)

#define ddr_acs_36kHz DDRA // Kathoden der IR-LEDS mit 36kHz getaktet
#define port_acs_36kHz PORTA
#define pin_acs_36kHz (1<<PA3)

#define ddr_acs_tsop DDRA // Eingang IR-Empfänger
#define port_acs_tsop PINA // Achtung, das ist ein Eingang!
#define pin_acs_tsop (1<<PA2)


[Edit]

Mittlerweile habe ich auch ein Programm geschrieben was beide IR-LEDs leuchten lässt.#define ddr_acs_36kHz DDRA // Kathoden der IR-LEDS mit 36kHz getaktet

Das ist dir aber sicher auch schon aufgefallen ;)

Du bist nun da wo Pinsel gestartet ist:
https://www.roboternetz.de/community/showthread.php?46233-nibobee-IR-Abstandsmessung

Um es nun etwas abzukürzen:


// ACS Testprogramm für die bee 11.6.2011 mic

// Infrarot-ACS für die bee im zweiten Anlauf mit pacman ;)

// https://www.roboternetz.de/community/showthread.php?53494-ACS-an-NIBObee
// https://www.roboternetz.de/community/showthread.php?46233-nibobee-IR-Abstandsmessung
// &p=444151&viewfull=1#post444151

#include <nibobee/iodefs.h>
#include <nibobee/led.h>
#include "acs_defines.h" // Definitionen der ACS-Hardware als Headerdatei einbinden

volatile uint8_t count36kHz;
volatile uint8_t acs=0;

void Sleep(uint8_t pause);
void Msleep(uint16_t pause);
void ACSData(uint16_t *data);

int main(void)
{
uint16_t data[2]; // Speicher für ACS-Werte

led_init();

// Setup Timer2
TCCR2 = (1 << WGM20)|(1 << CS20); // PhaseCorrect-PWM, no prescaling, no OC2-Pin!
TCNT2 = 96; // (512-416) 36kHz @15MHz
OCR2 = 151; // (255-(208/2)) 151 ist 50:50 Compare Match für symetrische Halbwellen
TIMSK |= (1 << OCIE2)|(1 << TOIE2); // Comp und OVF-ISR enable, Overflow bei Bottum!
enable_interrupts();

ddr_acs_led_l |= pin_acs_led_l; // die Anoden der IR-LEDs
port_acs_led_l &= ~pin_acs_led_l;

ddr_acs_led_r |= pin_acs_led_r;
port_acs_led_r &= ~pin_acs_led_r;

ddr_acs_36kHz |= pin_acs_36kHz; // die Kathoden der IR-LEDs

led_set(0,1);
Msleep(2000); // wait4programmer
led_set(0,0);

while(1)
{
ACSData(data);
PORTB &= ~15; // alle Leds aus
if(data[0] > 251) led_set(1,1);
else if(data[0] > 230) led_set(0,1);
if(data[1] > 251) led_set(2,1);
else if(data[1] > 230) led_set(3,1);
Msleep(100);
}
return(0);
}
ISR (TIMER2_COMP_vect)
{
port_acs_36kHz ^= pin_acs_36kHz; // IR-LEDs togglen
}
// Frequenzkorrektur für 36kHz (512-416 plus 3 Takte fürs Laden von TCNT2?)
ISR (TIMER2_OVF_vect)
{
TCNT2 += 99; // += bewirkt, dass schon erfolgte Zähltakte nicht ignoriert werden!
port_acs_36kHz &= ~pin_acs_36kHz; // bei Nulldurchgang soll die IR-LED aus sein!
//port_acs_36kHz |= pin_acs_36kHz; // seltamerweise funktioniert das auch?
if(count36kHz) count36kHz--;
if(acs) acs--;
}

void Sleep(uint8_t pause) // 1/36000 Pause blockierend
{
count36kHz=pause;
while(count36kHz);
}
void Msleep(uint16_t pause) // 1/1000 Pause blockierend
{
while(pause--) Sleep(36);
}
void ACSData(uint16_t *data)
{
OCR2=253;
port_acs_led_l |= pin_acs_led_l; // ACS LED left on
while((port_acs_tsop & pin_acs_tsop) && (OCR2 > 151))
{
acs=10; // Impulse senden, acs wird in OVF-ISR runtergezählt
while(acs);
OCR2--;
}
port_acs_led_l &= ~pin_acs_led_l; // ACS LED left off
data[0]=OCR2;
while(!(port_acs_tsop & pin_acs_tsop)); // warten bis keine Echo mehr

OCR2=253;
port_acs_led_r |= pin_acs_led_r; // ACS LED right on
while((port_acs_tsop & pin_acs_tsop) && (OCR2 > 151))
{
acs=10;
while(acs);
OCR2--;
}
port_acs_led_r &= ~pin_acs_led_r; // ACS LED right off
data[1]=OCR2;
while(!(port_acs_tsop & pin_acs_tsop));
}Das Programm reagiert auf die Annäherung eines Hindernisses und auf eine Fernbedienung. Es wird nun doch die nibobee-Library verwendet. Erklärung folgt...

pacman
13.06.2011, 12:21
vielen Dank, radbruch! Du hast mir sehr geholfen und mir viel beigebracht. Nachdem ich ein Programm geschrieben hab, wobei der NIBObee fährt und an Hindernisse vorbeifährt, melde ich mich wieder, damit du "dein" Werk begutachten kannst :)

achso und:
#define ddr_acs_36kHz DDRA // Kathoden der IR-LEDS mit 36kHz getaktet

Das ist dir aber sicher auch schon aufgefallen ;)
ja, das ist mir aufgefallen.....nur halt ein bischen später :D

pacman
13.06.2011, 21:23
Hallo! Ich bräuchte doch noch einmal eure Hilfe :D ... und zwar versuche ich meinen vorhandenen Code der Hindernisdetektion durch Fühler mit dem Code der Hindernisdetektion durch das ACS zu kombinieren. Alleine kann ich die Codes compilieren, doch sobald ich nur ein "include" oder "integer" kopiere, folgen haufenweise Errors. Von: "conflicting types for 'led_set' " bis hin zu "expected '(' before 'while' ".
Ich sitze jetzt schon fast den ganzen Tag dran und bin langsam am verzweifeln ](*,):MistPC

pacman
15.06.2011, 12:58
Hallo, ich habe folgenden
// Die Anoden der rechten IR-LEDs hängen mit Vorwiderstand an PA0 (X1-AN0)
// Die Anoden der linken IR-LEDs hängen mit Vorwiderstand an PA1 (X1-AN1)
// Die Kathoden beider IR-LEDs sind mit PA3 (X3-AN3) verbunden
// Der IR-Empfänger ist mit PA2 (X2-AN2) verbunden

#define ddr_acs_led_l DDRA // Anoden der IR-LEDs links
#define port_acs_led_l PORTA
#define pin_acs_led_l (1<<PA1)

#define ddr_acs_led_r DDRA // rechts
#define port_acs_led_r PORTA
#define pin_acs_led_r (1<<PA0)

#define ddr_acs_36kHz DDRA // Kathoden der IR-LEDS mit 36kHz getaktet
#define port_acs_36kHz PORTA
#define pin_acs_36kHz (1<<PA3)

#define ddr_acs_tsop DDRA // Eingang IR-Empfänger
#define port_acs_tsop PINA // Achtung, das ist ein Eingang!
#define pin_acs_tsop (1<<PA2)

#include <nibobee/iodefs.h>
#include <nibobee/led.h>
#include <nibobee/motpwm.h>
#include <nibobee/sens.h>

volatile uint8_t count36kHz;
volatile uint8_t acs=0;

void Sleep(uint8_t pause);
void Msleep(uint16_t pause);
void ACSData(uint16_t *data);

int main(void)
{
uint16_t data[2]; // Speicher für ACS-Werte

led_init();
motpwm_init();
sens_init();
while(1==1) {

// Setup Timer2
TCCR2 = (1 << WGM20)|(1 << CS20); // PhaseCorrect-PWM, no prescaling, no OC2-Pin!
TCNT2 = 96; // (512-416) 36kHz @15MHz
OCR2 = 151; // (255-(208/2)) 151 ist 50:50 Compare Match für symetrische Halbwellen
TIMSK |= (1 << OCIE2)|(1 << TOIE2); // Comp und OVF-ISR enable, Overflow bei Bottum!
enable_interrupts();

ddr_acs_led_l |= pin_acs_led_l; // die Anoden der IR-LEDs
port_acs_led_l &= ~pin_acs_led_l;

ddr_acs_led_r |= pin_acs_led_r;
port_acs_led_r &= ~pin_acs_led_r;

ddr_acs_36kHz |= pin_acs_36kHz; // die Kathoden der IR-LEDs

led_set(0,1);
Msleep(2000); // wait4programmer
led_set(0,0);

while(1)
{enable_interrupts();
int16_t speed_l=0;
int16_t speed_r=0;
motpwm_setLeft(speed_l);
motpwm_setRight(speed_r);

ACSData(data);
PORTB &= ~15; // alle Leds aus
if(data[0] > 251) speed_l = 500;
else if(data[0] > 230) speed_l = 800;
if(data[1] > 251) speed_r = 500;
else if(data[1] > 230) speed_r = 800;
Msleep(100);
}
return(0);
}
ISR (TIMER2_COMP_vect)
{
port_acs_36kHz ^= pin_acs_36kHz; // IR-LEDs togglen
}
// Frequenzkorrektur für 36kHz (512-416 plus 3 Takte fürs Laden von TCNT2?)
ISR (TIMER2_OVF_vect)
{
TCNT2 += 99; // += bewirkt, dass schon erfolgte Zähltakte nicht ignoriert werden!
port_acs_36kHz &= ~pin_acs_36kHz; // bei Nulldurchgang soll die IR-LED aus sein!
//port_acs_36kHz |= pin_acs_36kHz; // seltamerweise funktioniert das auch?
if(count36kHz) count36kHz--;
if(acs) acs--;
}

void Sleep(uint8_t pause) // 1/36000 Pause blockierend
{
count36kHz=pause;
while(count36kHz);
}
void Msleep(uint16_t pause) // 1/1000 Pause blockierend
{
while(pause--) Sleep(36);
}
void ACSData(uint16_t *data)
{
OCR2=253;
port_acs_led_l |= pin_acs_led_l; // ACS LED left on
while((port_acs_tsop & pin_acs_tsop) && (OCR2 > 151))
{
acs=10; // Impulse senden, acs wird in OVF-ISR runtergezählt
while(acs);
OCR2--;
}
port_acs_led_l &= ~pin_acs_led_l; // ACS LED left off
data[0]=OCR2;
while(!(port_acs_tsop & pin_acs_tsop)); // warten bis keine Echo mehr

OCR2=253;
port_acs_led_r |= pin_acs_led_r; // ACS LED right on
while((port_acs_tsop & pin_acs_tsop) && (OCR2 > 151))
{
acs=10;
while(acs);
OCR2--;
}
port_acs_led_r &= ~pin_acs_led_r; // ACS LED right off
data[1]=OCR2;
while(!(port_acs_tsop & pin_acs_tsop));
}
Beim Compilieren gibt er mir folgende Errors:
-static declaration of '__vector_3' follows non-static declaration
-previous declaration of '__vector_3' was here
-static declaration of '__vector_4' follows non-static declaration
-previous declaration of '__vector_4' was here
-expected declaration or statement at end of input

das ganze bezieht sich auf diesen Teil:
ISR (TIMER2_COMP_vect)
{
port_acs_36kHz ^= pin_acs_36kHz; // IR-LEDs togglen
}
// Frequenzkorrektur für 36kHz (512-416 plus 3 Takte fürs Laden von TCNT2?)
ISR (TIMER2_OVF_vect)
{TCNT2 += 99; // += bewirkt, dass schon erfolgte Zähltakte nicht ignoriert werden!
port_acs_36kHz &= ~pin_acs_36kHz; // bei Nulldurchgang soll die IR-LED aus sein!
//port_acs_36kHz |= pin_acs_36kHz; // seltamerweise funktioniert das auch?
if(count36kHz) count36kHz--;
if(acs) acs--;}

radbruch
15.06.2011, 13:44
Hallo

Vor return fehlt eine }

Aber das löst noch nicht alle Probleme. In allen Funktionsaufrufen der Library fehlt das void wenn kein Parameter übergeben wird, atomic kann mein Kompiler auch nicht und base.h muss eingebunden werden wegen nibobee_initialization:

In motpwm.c:

void motpwm_init(void) {
nibobee_initialization |= NIBOBEE_MOTPWM_INITIALIZED;
set_output_group(IO_MOTOR);
activate_output_group(IO_MOTOR);
PWM_TIMER_CCRA = PWM_TIMER_CCRA_INIT;
PWM_TIMER_CCRB = PWM_TIMER_CCRB_INIT;
PWM_TIMER_IMSK |= PWM_TIMER_IMSK_INIT;
}


void motpwm_stop(void) {
//ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
cli();
motpwm_motor_l = 0;
motpwm_motor_r = 0;
PWM_TIMER_OCRA = 0;
PWM_TIMER_OCRB = 0;
sei();
//}
}


sens.c:

void sens_init(void) {
set_output_group(IO_SENS);
}


leds.c:

void led_init(void) {
activate_output_group(IO_LEDS); // LED bits als Output
}


base.h einbinden:

...
#define pin_acs_tsop (1<<PA2)

#include <nibobee/base.h>
#include <nibobee/iodefs.h>
...


Ich habe den Beitrag mal hier eingeordend, das passt hier besser.

Gruß

mic

pacman
15.06.2011, 16:44
Also, wenn ich das dazuschreibe, kommen noch mehr Fehlermeldungen: zB 'PWM_TIMER_CCRB' undeclared (first use in this function) ?

pacman
19.06.2011, 16:21
Hallo, da bin ich wieder. Ich bin kurz vor dem Ziel. Mein letztes Problem ist noch, dass ich, wenn ich die sens.c Datei und die anderen ändern will, den Zugriff verweigert bekomme. Weiß einer wie ich das umgehen kann, oder muss ich die gesamte Datei in mein Programm kopieren?

radbruch
19.06.2011, 17:15
Wie lautet denn der genaue Wortlaut der Meldung? Screenshot?

pacman
19.06.2011, 18:19
"Zugriff auf C:/Program Files (x86)/NIBObeeLib/src/nibobee/motpwm.c wurde verweigert." zum Beispiel

radbruch
19.06.2011, 18:26
Sollten sich die Dateien der Library nicht im AVR-Pfad befinden?

C:\WinAVR\avr\include\nibobee

pacman
19.06.2011, 20:13
So, es klappt jetzt, nachdem ich den include-Ordner rüberkopiert habe. Allerdings funktioniert mein Programm immer noch nicht, wie es funktionieren sollte, könntest du noch einmal drübersehen?:

#define ddr_acs_led_l DDRA // Anoden der IR-LEDs links
#define port_acs_led_l PORTA
#define pin_acs_led_l (1<<PA1)

#define ddr_acs_led_r DDRA // rechts
#define port_acs_led_r PORTA
#define pin_acs_led_r (1<<PA0)

#define ddr_acs_36kHz DDRA // Kathoden der IR-LEDS mit 36kHz getaktet
#define port_acs_36kHz PORTA
#define pin_acs_36kHz (1<<PA3)

#define ddr_acs_tsop DDRA // Eingang IR-Empfänger
#define port_acs_tsop PINA // Achtung, das ist ein Eingang!
#define pin_acs_tsop (1<<PA2)

#include <nibobee/iodefs.h>
#include <nibobee/led.h>
#include <nibobee/motpwm.h>
#include <nibobee/sens.h>
#include <nibobee/base.h>

volatile uint8_t count36kHz;
volatile uint8_t acs=0;

void Sleep(uint8_t pause);
void Msleep(uint16_t pause);
void ACSData(uint16_t *data);

int main(void)
{
uint16_t data[2]; // Speicher für ACS-Werte

led_init();
motpwm_init();
sens_init();
while(1==1) {

// Setup Timer2
TCCR2 = (1 << WGM20)|(1 << CS20); // PhaseCorrect-PWM, no prescaling, no OC2-Pin!
TCNT2 = 96; // (512-416) 36kHz @15MHz
OCR2 = 151; // (255-(208/2)) 151 ist 50:50 Compare Match für symetrische Halbwellen
TIMSK |= (1 << OCIE2)|(1 << TOIE2); // Comp und OVF-ISR enable, Overflow bei Bottum!

enable_interrupts();
int16_t speed_l=0;
int16_t speed_r=0;
motpwm_setLeft(speed_l);
motpwm_setRight(speed_r);

ddr_acs_led_l |= pin_acs_led_l; // die Anoden der IR-LEDs
port_acs_led_l &= ~pin_acs_led_l;

ddr_acs_led_r |= pin_acs_led_r;
port_acs_led_r &= ~pin_acs_led_r;

ddr_acs_36kHz |= pin_acs_36kHz; // die Kathoden der IR-LEDs

led_set(0,1);
Msleep(2000); // wait4programmer
led_set(0,0);

while(1)
ACSData(data);
PORTB &= ~15; // alle Leds aus
if(data[0] > 251) speed_l = 500;
else if(data[0] > 230) speed_l = 800;
if(data[1] > 251) speed_r = 500;
else if(data[1] > 230) speed_r = 800;
Msleep(100);


}
return(0);
}
ISR (TIMER2_COMP_vect)
{
port_acs_36kHz ^= pin_acs_36kHz; // IR-LEDs togglen
}
// Frequenzkorrektur für 36kHz (512-416 plus 3 Takte fürs Laden von TCNT2?)
ISR (TIMER2_OVF_vect)
{
TCNT2 += 99; // += bewirkt, dass schon erfolgte Zähltakte nicht ignoriert werden!
port_acs_36kHz &= ~pin_acs_36kHz; // bei Nulldurchgang soll die IR-LED aus sein!
//port_acs_36kHz |= pin_acs_36kHz; // seltamerweise funktioniert das auch?
if(count36kHz) count36kHz--;
if(acs) acs--;
}

void Sleep(uint8_t pause) // 1/36000 Pause blockierend
{
count36kHz=pause;
while(count36kHz);
}
void Msleep(uint16_t pause) // 1/1000 Pause blockierend
{
while(pause--) Sleep(36);
}
void ACSData(uint16_t *data)
{
OCR2=253;
port_acs_led_l |= pin_acs_led_l; // ACS LED left on
while((port_acs_tsop & pin_acs_tsop) && (OCR2 > 151))
{
acs=10; // Impulse senden, acs wird in OVF-ISR runtergezählt
while(acs);
OCR2--;
}
port_acs_led_l &= ~pin_acs_led_l; // ACS LED left off
data[0]=OCR2;
while(!(port_acs_tsop & pin_acs_tsop)); // warten bis keine Echo mehr

OCR2=253;
port_acs_led_r |= pin_acs_led_r; // ACS LED right on
while((port_acs_tsop & pin_acs_tsop) && (OCR2 > 151))
{
acs=10;
while(acs);
OCR2--;
}
port_acs_led_r &= ~pin_acs_led_r; // ACS LED right off
data[1]=OCR2;
while(!(port_acs_tsop & pin_acs_tsop));
}

radbruch
19.06.2011, 20:47
while(1)
ACSData(data);
PORTB &= ~15; // alle Leds aus
if(data[0] > 251) speed_l = 500;
else if(data[0] > 230) speed_l = 800;
if(data[1] > 251) speed_r = 500;
else if(data[1] > 230) speed_r = 800;
Msleep(100);
}


Wo werden die speed_x-Werte an die Antriebe übergeben?

Hier stimmt es auch noch nicht:

while(1==1) {

und

while(1)
ACSData(data);

Lese endlos die ACS-Daten ein...

pacman
19.06.2011, 21:05
Ok, die beiden unteren Fehler habe ich gefunden, aber das mit den speed_x-Werte an die Antriebe übergeben, versteh ich nicht...

radbruch
19.06.2011, 21:52
In der Hauptschleife fehlt etwas in der Art:

motpwm_setLeft(speed_l);
motpwm_setRight(speed_r);

pacman
19.06.2011, 22:36
vielen Dank, jetzt klappt alles wunderbar!

radbruch
19.06.2011, 23:05
Prima, Glückwunsch. Großes Lob für dein Durchhaltevermögen ;)