PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : nibobee IR-Abstandsmessung



pinsel120866
10.01.2010, 15:59
Hallo,

ich möchte euch meine neue Erweiterung zeigen. Es soll eine IR-Abstandmessung werden.

Ich verwende hierzu 4 Ports:

- 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

Der Code für das Testprogramm sieht so aus:

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

#define FALSE 0
#define TRUE 1

uint8_t objekt_sichtbar_rechts(uint8_t distance_r)
{
uint16_t j,z;

PORTA |= (1 << PA0); // PA0 auf HIGH (LED ausschalten)
DDRA |= (1 << DDA1); // PA1 als Ausgang
PORTA &= ~(1 << PA1); // PA1 auf LOW

OCR2 = 254-distance_r; // wenn OCR2=0xFE dann Objekt sehr nahe
z=0;
for(j=0;j<30;j++) // loop time: 5ms
{
if (PINA & (1 << PA3))z++;
delay(6); // 6*Sleep(6)=1ms
}
if (z>=29) return FALSE; // Objekt nicht gefunden
else return TRUE;
}

uint8_t objekt_sichtbar_links(uint8_t distance_l)
{
uint16_t i,y;

PORTA |= (1 << PA1); // PA1 auf HIGH (LED ausschalten)
DDRA |= (1 << DDA0); // PA0 als Ausgang
PORTA &= ~(1 << PA0); // PA0 auf LOW

OCR2 = 254-distance_l; // wenn OCR2=0xFE dann Objekt sehr nahe
y=0;
for(i=0;i<30;i++) // loop time: 5ms
{
if (PINA & (1 << PA3))y++;
delay(6); // 6*Sleep(6)=1ms
}
if (y>=29) return FALSE; // Objekt nicht gefunden
else return TRUE;
}



uint8_t abstand_rechts()
{
uint8_t k,n;

k=255;
for(n=0;n<8;n++)
{
if (!objekt_sichtbar_rechts(n)) k=n; // solange kein Objekt, Distanz erhoehen
}
return k;
}

uint8_t abstand_links()
{
uint8_t l,m;

l=255;
for(m=0;m<8;m++)
{
if (!objekt_sichtbar_links(m)) l=m; // solange kein Objekt, Distanz erhoehen
}
return l;
}



/************************************************** ***********************

Hauptprogramm

************************************************** ***********************/
int main(void)
{
led_init();
uint8_t n,m;

while(1)
{
n=abstand_rechts();
m=abstand_links();

if ((n!=255) && (m!=255))
{
if (n<2) led_set(LED_R_RD, 1);
if (m<2) led_set(LED_L_RD, 1);


delay(10);
}
}

}


Das Problem ist nur - die IRLEDs leuchten nicht...

Kann mir jemand helfen?

radbruch
10.01.2010, 17:19
Hallo

Ich habe mich noch nicht richtig in dein Programm eingelesen, aber ein paar Punkte stechen mir sofort ins Auge:


PORTA |= (1 << PA0); // PA0 auf HIGH (LED ausschalten)
DDRA |= (1 << DDA1); // PA1 als Ausgang
PORTA &= ~(1 << PA1); // PA1 auf LOW

Wenn die Anoden an PA0/1 angeschlossen sind und die Kathoden an PA3, dann schaltet ein High an PA0 die Led ein. Hier passt dein Kommentar nicht? Wo wird denn PA3 auf Ausgang und Low geschaltet? Wie groß ist der berechnete Strom für PA0/1 und der für PA3? Der SFH5110 benötigt eine Trägerfrequenz für das IR-Signal, wo wird diese erzeugt und auf die IR-LEDs geschaltet?


for(j=0;j<30;j++) // loop time: 5ms
{
if (PINA & (1 << PA3))z++;
delay(6); // 6*Sleep(6)=1ms

Hoffentlich optimiert der Kompiler die for-Schleife nicht weg, was wird hier an PA3 abgefragt, warum werden aus 1ms plötzlich 6?

Du solltest den Code mal nur auf eine Seite und das Wesentliche reduzieren und besser kommentieren.

Ein kleine Beschreibung der Funktion und der Anschlüse im Programmkopf würde das lästige switchen zwischen Editor und Forumbeitrag überflüssig machen...

Gruß

mic

pinsel120866
10.01.2010, 17:40
Hallo,

danke dass du dir die Mühe gemacht hast, den Code anzusehen.

Ich habe versucht Anleihen von meiner ASURO-Erweiterung zu nehmenm wo die Anwendung einwandfrei funktioniert hat:https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=40427&highlight=

Als Programmierpflaume ist es mir offensichtlich wieder einmal nicht möglich die Erweiterung richtig zu programmieren.

Wie erzeuge ich die Trägerfrequenz und lege sie auf die IR-LEDs? Kannst du mir Verbesserungsvorschläge machen?

radbruch
10.01.2010, 19:08
Hallo

Bisher war es noch keine Mühe *kicher*

Hier habe ich gezeigt, wie man die 36kHz (ich nehme mal an, dies ist auch die Frequenz deines SFH) auf die LineLEDs bringt:
https://www.roboternetz.de/phpBB2/viewtopic.php?p=470683#470683

Davon brauchst du eigentlich nur das Timersetup:

TCCR2 = (1 << WGM21) | (1 << CS20); // CTC-Mode, no prescaling, no OC2-PIN!
OCR2 = 208; // 36kHz @15MHz
TIMSK |= (1 << OCIE2);
Zwei Halbwellen mit 72kHz, (15000000/72000)-1 sind ca. 208

Und eine angepasste ISR bei der PA3 mit 72kHz getoggelt wird:

ISR (TIMER2_COMP_vect)
{
static uint8_t status=0;
if(status)
{
led_set(LED_R_YE, 1);
PORTA |= (1<<PA3);
status=0;
}
else
{
led_set(LED_R_YE, 0);
PORTA &= ~(1<<PA3);
status=1;
}
}


Zusammen sollte das dann ungefähr so funktionieren:

// nibobee IR-LEDs an PA0 und PA1 im Wechsel mit 36kHz ansteuern 10.1.2010 mic

// https://www.roboternetz.de/phpBB2/viewtopic.php?p=479870#479870

// 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

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

#define FALSE (1==0)
#define TRUE (1==1)

int main(void)
{
led_init();
delay(2000); // wait4programmer

// Timer2-Setup für 72kHz
TCCR2 = (1 << WGM21) | (1 << CS20); // CTC-Mode, no prescaling, no OC2-PIN!
OCR2 = 208; // 36kHz @15MHz

DDRA |= (1<<PA1) | (1<<PA0); // Anoden der LEDs mit Vorwiderstand (??? Ohm) an PA0/1
PORTA &= ~((1<<PA1) | (1<<PA0)); // low = LEDs aus
DDRA &= ~(1<<PA3); // keine 72kHz-Ansteuerung
PORTA &= ~(1<<PA3); // deshalb Kathoden-Pin auf Ausgang ohne PullUp

if(TRUE) led_set(0,1); // true-Test ;)
sei(); // und los

while(1)
{
DDRA |= (1<<PA3); // Trägerfrequenz an Kathoden einschalten
TIMSK |= (1 << OCIE2); // ISR aktivieren

led_set(2,1);
PORTA |= (1<<PA0); // rechte IR-LEDs einschalten
delay(1000); // zwei Sekunden mit 36kHz blinken
PORTA &= ~(1<<PA0); // IR-Leds wieder ausschalten
led_set(2,0);

led_set(1,1);
PORTA |= (1<<PA1); // dito für linke IR-LEDs
delay(1000);
PORTA &= ~(1<<PA1);
led_set(1,0);

TIMSK &= ~(1 << OCIE2); // ISR ausklinken
DDRA &= ~(1<<PA3); // keine Trägerfrequenz an Kathoden
PORTA &= ~(1<<PA3); // bedeutet alle IR-LEDS aus
led_set(LED_R_YE, 0); // Kontroll-LED aus
delay(2000); // zwei Sekunden dunkel
}
}

ISR (TIMER2_COMP_vect)
{
static uint8_t status=0;
if(status)
{
led_set(LED_R_YE, 1); // Kontroll-LED
PORTA |= (1<<PA3);
status=0;
}
else
{
led_set(LED_R_YE, 0);
PORTA &= ~(1<<PA3);
status=1;
}
}
Kontrolle mit Digicam, ich kann es natürlich nicht selbst testen. Ein asuro oder RP6 müßte das Signal auch erkennen.

Das war jetzt etwas Mühe ;)

btw. ist delay() echter Schrott! Mit dieser ISR könnte man auch die Sleep()-Funktion ala asuro nachbilden.

Gruß

mic

pinsel120866
11.01.2010, 18:00
Super erklärt Radbruch,

es funktioniert, die LEDs leuchten. \:D/

Danke für deine Mühe!

pinsel120866
11.01.2010, 19:56
Nochmals hallo,

ich habe nun versucht mic's Code in mein Programm einzubauen:


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

#define FALSE (1==0)
#define TRUE (1==1)

uint8_t objekt_sichtbar_rechts(uint8_t distance_r)
{
uint16_t j,z;

PORTA |= (1 << PA0); // PA0 auf HIGH (LED einschalten)
DDRA |= (1 << DDA1); // PA1 als Ausgang
PORTA &= ~(1 << PA1); // PA1 auf LOW (andere LED ausschalten)

OCR2 = 254-distance_r; // wenn OCR2=0xFE dann Objekt sehr nahe
z=0;
for(j=0;j<30;j++) // loop time: 5ms
{
if (PINA & (1 << PA3))z++;
delay(1);
}
if (z>=29) return FALSE; // Objekt nicht gefunden
else return TRUE;
}

uint8_t objekt_sichtbar_links(uint8_t distance_l)
{
uint16_t i,y;

PORTA |= (1 << PA1); // PA1 auf HIGH (LED einschalten)
DDRA |= (1 << DDA0); // PA0 als Ausgang
PORTA &= ~(1 << PA0); // PA0 auf LOW (andere LED ausschalten)

OCR2 = 254-distance_l; // wenn OCR2=0xFE dann Objekt sehr nahe
y=0;
for(i=0;i<30;i++) // loop time: 5ms
{
if (PINA & (1 << PA3))y++;
delay(1);
}
if (y>=29) return FALSE; // Objekt nicht gefunden
else return TRUE;
}



uint8_t abstand_rechts()
{
uint8_t k,n;

k=255;
for(n=0;n<8;n++)
{
if (!objekt_sichtbar_rechts(n)) k=n; // solange kein Objekt, Distanz erhoehen
}
return k;
}

uint8_t abstand_links()
{
uint8_t l,m;

l=255;
for(m=0;m<8;m++)
{
if (!objekt_sichtbar_links(m)) l=m; // solange kein Objekt, Distanz erhoehen
}
return l;
}

/************************************************** ***********************

Hauptprogramm

************************************************** ***********************/
int main(void)
{
led_init();
uint8_t n,m;

delay(2000); // wait4programmer

if(TRUE) led_set(0,1); // true-Test ;)
sei(); // und los

while(1)
{
// Timer2-Setup für 72kHz
TCCR2 = (1 << WGM21) | (1 << CS20); // CTC-Mode, no prescaling, no OC2-PIN!
OCR2 = 208; // 36kHz @15MHz

DDRA |= (1<<PA1) | (1<<PA0); // Anoden der LEDs mit Vorwiderstand (??? Ohm) an PA0/1
PORTA &= ~((1<<PA1) | (1<<PA0)); // low = LEDs aus
DDRA &= ~(1<<PA3); // keine 72kHz-Ansteuerung
PORTA &= ~(1<<PA3); // deshalb Kathoden-Pin auf Ausgang ohne PullUp

n=abstand_rechts();
m=abstand_links();

if ((n!=255) && (m!=255))
{
if (n<2) led_set(LED_R_RD, 1);
if (m<2) led_set(LED_L_RD, 1);


delay(10);
}
}
}

ISR (TIMER2_COMP_vect)
{
static uint8_t status=0;
if(status)
{
led_set(LED_R_YE, 1); // Kontroll-LED
PORTA |= (1<<PA3);
status=0;
}
else
{
led_set(LED_R_YE, 0);
PORTA &= ~(1<<PA3);
status=1;
}
}

Wenn sich von links oder rechts ein Gegenstand nähert, soll die linke oder rechte rote LED leuchten.
Leider funktioniert der Code wieder nicht, was ist nun jetzt wieder falsch?

radbruch
11.01.2010, 21:54
Hallo

Das wird so nichts, weil die IR-Abstandsmessung nach Waste beim asuro mit variablen Halbwellen der Trägerfrequenz arbeitet. Das ist letzlich die Meisterleistung seiner Modifikation der asuro-Lib bei der IR-Abstandsmessung.

Wir erzeugen im Moment ein symetrisches Signal bei dem die Impuls- und Pausenzeiten des IR-Signals 50:50 sind. Waste hat den Timer im PWM-Mode betrieben und die Dauer der Halbwellen mit dem OCR2-Register gesteuert. Das kann meine Ansteuerung nicht, weil der OC2-Pin bei der bee zur Motordrehrichtungsansteuerung (PD7, DIR_R) verwendet wird und deshalb nicht zur freien Verfügung steht. (Das ist meiner Meinung nach einer der wenigen Kritikpunkte bei der bee) Um das nachzubilden müßten wir in der ISR zusätzlich eine Software-PWM einbauen. (was natürlich nicht ausschliest, dass wir das noch machen werden;)

Weil ich deine Erweiterungen (noch) nicht an meiner bee angebaut habe, kann ich weiterhin nur "theoretisch" programmieren. Testen mußt du selbst. Das folgende Programm sendet jeweils auf einer Seite ein IR-Signal, prüft, ob der SFH ein IR-Echo empfängt und zeigt das Ergebniss an den roten Leds an:

// nibobee Einfache Abstandsmessung mit Pinselbee-Umbau 11.1.2010 mic

// Es werden im Wechsel die rechten und linken IR-Leds mit 36kHz angesteuert
// und jeweils das Echo ausgewertet und an den roten Leds angezeigt.
// LED3 leuchtet, wenn die ISR aktiv ist.

// https://www.roboternetz.de/phpBB2/viewtopic.php?p=480142#480142

// 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

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

#define FALSE (1==0)
#define TRUE (1==1)

uint8_t detect_right(void)
{
uint8_t echo;

DDRA |= (1<<PA3); // Trägerfrequenz an Kathoden einschalten
TIMSK |= (1 << OCIE2); // ISR aktivieren

PORTA |= (1<<PA0); // rechte IR-LEDs einschalten
delay(1); // 36 IR-Impulse senden (10 sollten dem SFH genügen) ???
//delay(10); // delay(1) ist nicht immer 1/1000 Sek!!!

// SFH Ausgang wird low, wenn er Trägersignal erkennt => echo ist true ;)
echo=((PINA & (1<<PA2)) == 0); // SFH hängt an PA2

PORTA &= ~(1<<PA0); // IR-Leds wieder ausschalten
TIMSK &= ~(1 << OCIE2); // ISR ausklinken
DDRA &= ~(1<<PA3); // keine Trägerfrequenz an Kathoden
PORTA &= ~(1<<PA3); // bedeutet alle IR-LEDS aus
led_set(LED_R_YE, 0); // ISR-Kontroll-LED aus
return(echo);
}

uint8_t detect_left(void)
{
uint8_t echo;

DDRA |= (1<<PA3); // 36kHz-Trägersignal senden
TIMSK |= (1 << OCIE2);

PORTA |= (1<<PA1);
delay(1);
//delay(10);

echo=((PINA & (1<<PA2)) == 0); // Echo empfangen?

PORTA &= ~(1<<PA1); // Trägersignal ausschalten
TIMSK &= ~(1 << OCIE2);
DDRA &= ~(1<<PA3);
PORTA &= ~(1<<PA3);
led_set(LED_R_YE, 0);
return(echo);
}

int main(void)
{
led_init();
PORTB |= 15; // alle LEDs an
delay(2000); // wait4programmer
PORTB &= ~15; // alle LEDs aus

// Timer2-Setup für 72kHz
TCCR2 = (1 << WGM21) | (1 << CS20); // CTC-Mode, no prescaling, no OC2-PIN!
OCR2 = 208; // 36kHz @15MHz

DDRA |= (1<<PA1) | (1<<PA0); // Anoden der LEDs mit Vorwiderstand an PA0/1
PORTA &= ~((1<<PA1) | (1<<PA0)); // low = LEDs aus
DDRA &= ~(1<<PA3); // keine 72kHz-Ansteuerung
PORTA &= ~(1<<PA3); // deshalb Kathoden-Pin auf Ausgang ohne PullUp

if(TRUE) led_set(0,1); // true-Test ;)
sei(); // und los

while(1)
{
if(detect_right()) led_set(2,1); else led_set(2,0);
if(detect_left()) led_set(1,1); else led_set(1,0);
delay(1000);
}
}

ISR (TIMER2_COMP_vect)
{
static uint8_t status=0;
if(status)
{
led_set(LED_R_YE, 1); // Kontroll-LED
PORTA |= (1<<PA3);
status=0;
}
else
{
led_set(LED_R_YE, 0);
PORTA &= ~(1<<PA3);
status=1;
}
}
Der SFH reagiert nach ca. 10 Signalwellen auf eine IR-Trägerfrequenz. Wir senden jeweils ca. 36 Wellen (mit dem schrottigen delay() gemessen) und prüfen dann, ob der SFH etwas erkannt hat. Das funktioniert vermutlich nur zufriedenstellend, wenn die IR-Leds eine deutliche Richtungswirkung haben. Das kann man z.B. mit Schrumpfschläuchen (wie beim Probot) über den Leds erreichen.

Ob das Programm wie erwartet fumktioniert mußt du testen. Bei meiner bee ohne deine Erweiterung ist der X3-Slot nicht belegt und die PA2/3-Pins hängen frei in der Luft. Deshalb kann ich mit dem Daumen eine Brücke zwischen GND und PA2 machen und so einen SFH-Empfang simulieren ;)

Wie groß sind eigentlich die Vorwiderstände der IR-LEDs? Vorsichtshalber sollten nie beide Seiten eingeschaltet sein um eine Überlastung von PA3 zu vermeiden.

Gruß

mic

radbruch
12.01.2010, 15:46
Hallo

Weil ich ja schon immer mal etwas mit einer PWM-Ansteuerung spielen wollte, habe ich mal Grundlagenforschung betrieben. Ich denke, obwohl das folgende noch Betastatus ist, hat es sich gelohnt. Meine bee kann jetzt ein pulsweitenmoduliertes 36kHz-IR-Signal erzeugen und es mit den LineLEDs abstrahlen.

Basis ist der Timer2 der im Phase-Correct-PWM-Mode betrieben wird. Das hört sich zwar übelst kompliziert an, ist aber eigentlich ganz einfach;) Anders als in den "normalen" Modi zählt hier der Counter von null bis 255 und dann wieder zurück nach null. Wenn er beim Runterzählen bei null angekommen ist, wird der Overflow-Interrupt ausgelöst und der Zyklus beginnt erneut. Deshalb dauert in dieser Betriebsart ein Zyklus 512 Countertakte (Das genaue Timing muss ich noch überprüfen)

Um nun auf die 36kHz zu kommen, benötigt man bei der bee 15MHz/36kHz=416,irgendwas Takte. Deshalb verwende ich den selben Trick wie Waste beim asuro und lade das Zählregister in der Overflow-ISR mit einem neuen Startwert ab dem der Counter dann weiter bis 255 und wieder zurück bis null zählt. Den Wert errechnet man so: 512-416=96. Zusätzlich benötigt man noch 3 Takte für das Laden des Wertes in das Zählregister, damit sind es 99 die in der Overflow-ISR in das Zählregister geladen werden um den 36kHz-Takt zu erzeugen.

Um nun die IR-LEDs mit dieser Frequenz anzusteuern verwendet man das OCR-Register. Jedesmal, wenn der Zählwert so groß wie der Wert im OCR ist, wird ein CompareMatch-Interrupt ausgelöst. Und zwar jedesmal, wenn die Werte gleich sind, also auch beim Runterzählen. Wenn beim Start des Timers die LED aus ist, wird sie bei OCR eingeschaltet, bleibt an während der Zähler 255 erreicht und brennt dann weiter, bis der Zähler beim Runterzählen wieder OCR erreicht. Dann wird die LED wieder getoggelt, also ausgeschaltet und bleibt solange aus, bis der Zähler nach Erreichen von null und Laden von Startwert in der OVF-ISR den OCR-Wert erneut erreicht. An diesem Punkt ist dann eine Periode beendet.

Der Wert fürs OCR-Register bestimmt die Hell- und Dunkelanteile einer Periode des Signals. Um 50:50 zu erhalten, muss die LED ca. 208 Takte eingeschaltet und 208 Takte ausgeschaltet sein, also 104 Takte vor 255 einschalten beim Hochzählen und beim Runterzählen auch nach 104 Takten. Das ergibt als Wert für das OCR2-Register 255-104=151. Alle Werte die größer sind, verringern den Leuchtanteil der IR-LEDs, Werte kleiner 100 sind nicht erlaubt. Ebenso funktioniert es bei Werten über 253 nicht mehr.

Dieses kleine Demo zeigt wie es gemacht wird und wie es funktioniert:

// Nibobee 36kHz mit Timer2 in PhaseCorrect-PWM-Mode. 12.1.2010 mic

#define LineLEDs_on PORTB &=~(1<<PB4)
#define LineLEDs_off PORTB |= (1<<PB4)

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

volatile uint8_t count36kHz;

void Sleep(uint8_t pause);
void Msleep(uint16_t pause);

int main(void)
{
led_init();
sens_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 Bottom!
enable_interrupts();

Msleep(2000); // wait4programmer
led_set(0,1);
DDRB |= (1<<PB4); // LineLED ist ein Ausgang
LineLEDs_on; // LineLED schaltet gegen GND!

while(!sens_getLeft() & !sens_getRight()) // solange keine Taste gedrückt wird
{Msleep(200); PORTB ^= (1<<PB0);} // hektisches Blinken mit LED0

while(1)
{
if(sens_getLeft() == -1) {OCR2=200; PORTB &= ~15; led_set(0,1);}
if(sens_getLeft() == 1) {OCR2=230; PORTB &= ~15; led_set(1,1);}
if(sens_getRight() == 1) {OCR2=250; PORTB &= ~15; led_set(2,1);}
if(sens_getRight() == -1){OCR2=253; PORTB &= ~15; led_set(3,1);} // minwert!
Msleep(100);
}
return(0);
}
ISR (TIMER2_COMP_vect)
{
PORTB ^= (1<<PB4); // LineLEDs togglen
}
// Frequenzkorrektur 512-416 plus 3 Takte fürs Laden von TCNT2?
ISR (TIMER2_OVF_vect)
{
TCNT2 = 99;
if(count36kHz) count36kHz--;
}

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);
}
Zusätzlich ist noch die Sleep()-Funktion eingebaut für die asuro-Umsteiger;) Sie ersetzt hier die delay()-Funktion der nibobee-Lib. Als IR-Detektor dient mein RP6:

// Status des TSOP an LEDs anzeigen 12.1.2010 mic
// Funktion kann einfach mit einer Fernbedienung getestet werden

#include "RP6RobotBaseLib.h"

int main(void)
{
initRobotBase();
powerON();

while(1)
{
if(PINB & ACS) setLEDs(1); else setLEDs(63);
}
return(0); // wird nie erreicht
}

Das Video ist dazu ist äzend, ich zeige es nur weil es eben existiert:

http://i1.ytimg.com/vi/Lrj1Xzp791c/1.jpg (http://www.youtube.com/watch?v=Lrj1Xzp791c)
http://www.youtube.com/watch?v=Lrj1Xzp791c

Möglicherweise stimmt das Timing nicht 100%ig. Da ich kein Oszi habe, kann ich das Signal nicht genau ausmessen. Vermutlich passt es nicht Taktgenau (512, 104, 151, 99?), aber es ist offensichtlich innerhalb der TSOP-Toleranz. Grundsätzlich funktioniert das wohl auch mit dem SFH.

Gruß

mic

pinsel120866
12.01.2010, 18:28
Hi Radbruch,

du bist ein echter Tüftler - toll wie da an die Sache rangehst.

Ich habe dein Programm von gestern nun geflasht und beide roten LEDs leuchten durchgehend, sollten die nicht kurz aufleuchten?

radbruch
12.01.2010, 18:50
Hallo

Der Status der roten LEDs wird einmal in der Sekunde aktuallisiert. Wenn dann ein IR-Echo erkannt wird, leuchten sie. Getestet habe ich es nicht, weil ich noch keinen IR-Empfänger an meine bee gebaut habe. Wenn du aber den Stecker an X1 (Anoden der Leds) abziehst wird kein Signal gesendet und die roten Led sollten ausgehen. Wenn das funktioniert könntest du deinen SFH (immer noch ohne eingesteckten X1) mit einer Fernbedienung anblinken, die roten Leds sollten dann reagieren (eventuell den delay(1000)-Befehl dazu auskommentieren)

Gruß

mic

pinsel120866
12.01.2010, 18:59
Hi,

funktioniert - ich habe die Sekunde herausgenommen und mit einer Fernbedienung den SFH angeblinkt. Die roten LEDs flackern dann.

Kannst du die "pulsweitenmoduliertes 36kHz-IR-Signal - Geschichte" noch in das gestrige Programm einbauen, oder macht dies im Moment noch keinen Sinn?

radbruch
12.01.2010, 20:28
Hallo

Naja, Sinn würde das schon machen:

// Nibobee ACS mit Pinselbee 12.1.2010 mic

#include <nibobee/iodefs.h>
#include <nibobee/led.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)
{
OCR2=253;
PORTA |= (1<<PA1); // ACS LED left on
while((PINA & (1<<PA2)) && (OCR2 > 151))
{
acs=15; //15 Impulse senden, acs wird in OVF-ISR runtergezählt
while(acs);
OCR2--;
}
PORTA &= ~(1<<PA1); // ACS LED left off
data[0]=OCR2;
while(!(PINA & (1<<PA2))); // warten bis keine Echo mehr

OCR2=253;
PORTA|= (1<<PA0); // ACS LED right on
while((PINA & (1<<PA2)) && (OCR2 > 151))
{
acs=15;
while(acs);
OCR2--;
}
PORTA &= ~(1<<PA0); // ACS LED right off 6
data[1]=OCR2;
while(!(PINA & (1<<PA2)));
}

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

led_init();
sens_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();

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

DDRA |= (1<<PA3)|(1<<PA1)|(1<<PA0);
PORTA &= ~((1<<PA3)|(1<<PA1)|(1<<PA0));
//DDRB |= (1<<PB4); // Test mit LineLEDs
//PORTB &= ~(1<<PB4);

while(!sens_getLeft() & !sens_getRight()) // solange keine Taste gedrückt wird
{Msleep(200); PORTB ^= (1<<PB0);} // hektisches Blinken mit LED0

while(1)
{
ACSData(data);
PORTB &= ~15; // alle Leds aus
if(data[0] > 250) led_set(1,1);
else if(data[0] > 230) led_set(0,1);
if(data[1] > 250) led_set(2,1);
else if(data[1] > 230) led_set(3,1);
Msleep(100);
}
return(0);
}
ISR (TIMER2_COMP_vect)
{
PORTA ^= (1<<PA3); // IR-LEDs togglen
//PORTB ^= (1<<PB4); // Test mit LineLEDs
}
// Frequenzkorrektur 512-416 plus 3 Takte fürs Laden von TCNT2?
ISR (TIMER2_OVF_vect)
{
TCNT2 = 99;
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);
}

Der ACS-Code stammt von hier:
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=459661#459661

ACSData() liefert Werte von 151(kein Echo) bis 253(vor der Nase) und funktioniert wie LineData() oder Odometriedata() beim asuro. Ich hab's mit einer Brücke zwischen X2:1 (PA2) und X2:3 (GND) getestet....

Gruß

mic

[Edit]
Tilde entfernt s.u.

pinsel120866
13.01.2010, 19:20
Hi mic,

ich habe deinen Code getestet, mit dem Ergebnis dass die IR-LEDs auf der rechten Seite nicht leuchten. Links wird der Abstand mit der gelben und roten LED angezeigt.

Übrigens, dieIR-LED-Widerstände haben 150 Ohm.

radbruch
13.01.2010, 19:27
Hallo

Im Moment habe ich das auch bemerkt (weil ich grad deine Erweiterung nachbaue). Hier ist die Tilde zuviel:

PORTA|= ~(1<<PA0); // ACS LED right on
_______^__________________________

Gruß

mic

pinsel120866
13.01.2010, 20:09
Ist OK, habe ich auch gerade beim Vergleichen festgestellt.

Kleine Verbesserungfür deinen Nachbau: Löte das SFH von unten ein - siehe Foto.

radbruch
13.01.2010, 21:18
Hallo

Danke für den Tipp, er kommt leider zu spät ;)

http://radbruch.bplaced.net/robot/nibobee/acs/nibobee-acs1_klein.jpg (http://radbruch.bplaced.net/robot/nibobee/acs/nibobee-acs1.jpg) http://radbruch.bplaced.net/robot/nibobee/acs/nibobee-acs2_klein.jpg (http://radbruch.bplaced.net/robot/nibobee/acs/nibobee-acs2.jpg) http://radbruch.bplaced.net/robot/nibobee/acs/nibobee-acs3_klein.jpg (http://radbruch.bplaced.net/robot/nibobee/acs/nibobee-acs3.jpg) http://radbruch.bplaced.net/robot/nibobee/acs/nibobee-acs4_klein.jpg (http://radbruch.bplaced.net/robot/nibobee/acs/nibobee-acs4.jpg)
(Wegen der Qualität meiner Lötstellen sind die Pics mit Absicht unscharf ;)

Ich verwende nur einen 120 Ohm Widerstand für beide LEDs an der Kathode, der erscheint mir allerdings zu groß. Links habe ich noch keinen Empfang mit der IR-LED, mit der Fernbedienung funzt es aber. Ich verwende einen IRM 2636A (http://conrad.de/goto.php?artikel=156405) mit 3mm-IR-LEDS aus der Kruschtelkiste (von meiner Cam= 100%-Recycling;). Vielleicht passen die Wellenlängen nicht optimal. Mein Code mit LCD:

// Nibobee ACS mit radbruchbee 13.1.2010 mic

#define acs_led_l (1<<PA2) // Anoden der IR-LEDs
#define acs_led_r (1<<PA3)
#define acs_36kHz (1<<PC2) // Kathoden der IR-LEDS mit 36kHz getaktet und 120R
#define acs_tsop (1<<PC3) // Ausgang IR-Empfänger

#include <nibobee/iodefs.h>
#include <nibobee/delay.h>
#include <nibobee/led.h>
#include <stdlib.h>
#include "lcd_lib.c"

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();
lcd_init(); // lcd initialisieren

// 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();

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

DDRA |= acs_led_l|acs_led_r; // die Anoden der IR-LEDs
PORTA &= ~(acs_led_l|acs_led_r); // muss angepasst werden!!!
DDRC |= acs_36kHz; // die Kathoden der IR-LEDs
DDRC &= ~acs_36kHz; // muss angepasst werden!!!

lcd_cls(); // lcd Inhalt löschen
lcd_locate(2,0);
lcd_writeString("NIBOBee mit ACS");
lcd_locate(8,2);
lcd_writeString("13.1.10 mic");
lcd_locate(0,3);
lcd_writeString("Bitte Taste druecken");
while(!lcd_getkeys())
{Msleep(100); PORTB ^= (1<<PB0);} // hektisches Blinken mit LED0

lcd_cls(); // lcd Inhalt löschen
lcd_locate(2,0);
lcd_writeString("NIBOBee mit ACS");
lcd_locate(0,2);
lcd_writeString("ACS links:");
lcd_locate(0,3);
lcd_writeString("ACS rechts:");

while(1)
{
ACSData(data);
lcd_locate(13,2);
lcd_writeInteger(data[0], 10);
lcd_writeString(" ");
lcd_locate(13,3);
lcd_writeInteger(data[1], 10);
lcd_writeString(" ");
Msleep(100);
}
return(0);
}
ISR (TIMER2_COMP_vect)
{
PORTC ^= 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;
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;
PORTA |= acs_led_l; // ACS LED left on
while((PINC & acs_tsop) && (OCR2 > 151))
{
acs=30; //15 Impulse senden, acs wird in OVF-ISR runtergezählt
while(acs);
OCR2--;
}
PORTA &= ~acs_led_l; // ACS LED left off
data[0]=OCR2;
while(!(PINC & acs_tsop)); // warten bis keine Echo mehr

OCR2=253;
PORTA|= acs_led_r; // ACS LED right on
while((PINC & acs_tsop) && (OCR2 > 151))
{
acs=30;
while(acs);
OCR2--;
}
PORTA &= ~acs_led_r; // ACS LED right off
data[1]=OCR2;
while(!(PINC & acs_tsop));
}

Gruß

mic

radbruch
14.01.2010, 17:44
Noch ein kleines Update:

ISR (TIMER2_OVF_vect)
{
TCNT2 = 114; // oje??? (mit 114 werden gemessene 36kHz erzeugt!
PORTC &= ~acs_36kHz; // bei Nulldurchgangg soll die IR-LED aus sein!
if(count36kHz) count36kHz--;
if(acs) acs--;
}

Ich habe nun die Frequenz mit einem Multimeter gemessen und nachjustiert. Warum man 114 ins TCNT2 laden muss ist mir noch völlig schleierhaft. Mit 99 habe ich 34,7kHz gemessen.

Die zweite Änderung hatte ich schon zu Beginn angedacht: Wie erkenne ich in welche Richtung der Timer zählt um sicherzustellen, dass beim höchsten Timerwert die IR-LEDs auch an sind? Mit dieser Änderung ist es klar: Beim Nulldurchgang sind sie aus und damit syncron zum Timer.

Meine linke Led war noch verpolt.

Gruß

mic

pinsel120866
14.01.2010, 19:30
Hi mic,

ich habe dein Programm an meine Belegung angepasst, mit Ausgabe über die roten und gelben LEDs:

// 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 acs_led_l (1<<PA1) // Anoden der IR-LEDs
#define acs_led_r (1<<PA0)
#define acs_36kHz (1<<PA3) // Kathoden der IR-LEDS mit 36kHz getaktet und 120R
#define acs_tsop (1<<PA2) // Ausgang IR-Empfänger

#include <nibobee/iodefs.h>
#include <nibobee/delay.h>
#include <nibobee/led.h>
#include <stdlib.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();

// 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();

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

DDRA |= acs_led_l|acs_led_r; // die Anoden der IR-LEDs
PORTA &= ~(acs_led_l|acs_led_r); // muss angepasst werden!!!
DDRA |= acs_36kHz; // die Kathoden der IR-LEDs
DDRA &= ~acs_36kHz; // muss angepasst werden!!!

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)
{
PORTA ^= acs_36kHz; // IR-LEDs togglen
}
// Frequenzkorrektur für 36kHz (512-416 plus 3 Takte fürs Laden von TCNT2?)
ISR (TIMER2_OVF_vect)
{
TCNT2 = 114; // oje??? (mit 114 werden gemessene 36kHz erzeugt!
PORTA &= ~acs_36kHz; // bei Nulldurchgangg soll die IR-LED aus sein!
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;
PORTA |= acs_led_l; // ACS LED left on
while((PINA & acs_tsop) && (OCR2 > 151))
{
acs=30; //15 Impulse senden, acs wird in OVF-ISR runtergezählt
while(acs);
OCR2--;
}
PORTA &= ~acs_led_l; // ACS LED left off
data[0]=OCR2;
while(!(PINC & acs_tsop)); // warten bis keine Echo mehr

OCR2=253;
PORTA|= acs_led_r; // ACS LED right on
while((PINC & acs_tsop) && (OCR2 > 151))
{
acs=30;
while(acs);
OCR2--;
}
PORTA &= ~acs_led_r; // ACS LED right off
data[1]=OCR2;
while(!(PINC & acs_tsop));
}


Damit leuchtet aber gar keine LED mehr. Kannst du dir bitte das mal ansehen?

radbruch
14.01.2010, 19:59
Da bin ich auch reingetappt:
DDRA &= ~acs_36kHz; // muss angepasst werden!!!

Es sollte eigentlich so aussehen:
//PORTC &= ~acs_36kHz;

...und sollte dazu dienen, die IR-LEDs auf einen definierten Startzustand zu bringen. Es wird nun durch die Änderung in der ISR ersetzt und kann gelöscht werden.

radbruch
14.01.2010, 22:22
Ha, 99 passt doch, wenn man die Takte für den Aufruf der ISR beachtet:
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=473142#473142

Ein kleiner Nebensatz der viel bewirkt:
Das += bewirkt, dass inzwischen schon erfolgte Zähltakte nicht ignoriert werden.

Das Schicksal meint es mal wieder gut mit mir und winkt als Sir William (https://www.roboternetz.de/phpBB2/viewtopic.php?p=480644#480644), der mich dazu anregte, die alten Threads nochmals durchzulesen ;)

Wie Waste das alles rausbekommen hat ist mir echt schleierhaft. Das ist meiner Meinung nach echt genial!

Die Overflow-ISR sieht deshalb nun so aus:

ISR (TIMER2_OVF_vect)
{
TCNT2 += 99; // += bewirkt, dass schon erfolgte Zähltakte nicht ignoriert werden!
PORTC &= ~acs_36kHz; // bei Nulldurchgang soll die IR-LED aus sein!
if(count36kHz) count36kHz--;
if(acs) acs--;
}
Gemessene Frequenz an den IR-LEDs: 36,22kHz

Gruß

mic

P.S. Ich habe die Abstandsmessung als eigenständigen Thread angelegt. Ich hoffe, das ist o.K. so

pinsel120866
15.01.2010, 17:33
Hi mic,

ich habe nochmals deine Änderungen in meinen Code eingepflegt, die IR_LEDs leuchten aber immer noch nicht.

Kannst du bitte nochmals drübersehen?


// 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 acs_led_l (1<<PA1) // Anoden der IR-LEDs
#define acs_led_r (1<<PA0)
#define acs_36kHz (1<<PA3) // Kathoden der IR-LEDS mit 36kHz getaktet und 150R
#define acs_tsop (1<<PA2) // Ausgang IR-Empfänger

#include <nibobee/iodefs.h>
#include <nibobee/delay.h>
#include <nibobee/led.h>
#include <stdlib.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();

// 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();

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

DDRA |= acs_led_l|acs_led_r; // die Anoden der IR-LEDs
PORTA &= ~(acs_led_l|acs_led_r); // muss angepasst werden!!!
DDRA |= acs_36kHz; // die Kathoden der IR-LEDs

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)
{
PORTA ^= 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!
PORTA &= ~acs_36kHz; // bei Nulldurchgang soll die IR-LED aus sein!
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;
PORTA |= acs_led_l; // ACS LED left on
while((PINA & acs_tsop) && (OCR2 > 151))
{
acs=30; //15 Impulse senden, acs wird in OVF-ISR runtergezählt
while(acs);
OCR2--;
}
PORTA &= ~acs_led_l; // ACS LED left off
data[0]=OCR2;
while(!(PINA & acs_tsop)); // warten bis keine Echo mehr

OCR2=253;
PORTA|= acs_led_r; // ACS LED right on
while((PINA & acs_tsop) && (OCR2 > 151))
{
acs=30;
while(acs);
OCR2--;
}
PORTA &= ~acs_led_r; // ACS LED right off
data[1]=OCR2;
while(!(PINA & acs_tsop));
}

pinsel120866
15.01.2010, 19:16
Nochmals Hallo,

ich habe den Fehler gefunden, die LEDs leuchten wieder. Ausserdem habe ich die Wertausgabe auf mein I2C LCD eingebaut:

// 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 acs_led_l (1<<PA1) // Anoden der IR-LEDs
#define acs_led_r (1<<PA0)
#define acs_36kHz (1<<PA3) // Kathoden der IR-LEDS mit 36kHz getaktet und 150R
#define acs_tsop (1<<PA2) // Ausgang IR-Empfänger

#include <nibobee/iodefs.h>
#include <nibobee/delay.h>
#include <nibobee/led.h>
#include <stdlib.h>
#include <nibobee/i2cmaster.h>
#include <nibobee/lcdi2c.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();
i2c_init();
lcd_init(0x27);
lcd_setBacklight(0);
printf("Pinsel's NIBObee");

// 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();

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

DDRA |= acs_led_l|acs_led_r; // die Anoden der IR-LEDs
PORTA &= ~(acs_led_l|acs_led_r); // muss angepasst werden!!!
DDRA |= acs_36kHz; // die Kathoden der IR-LEDs

while(1)
{
ACSData(data);
// PORTB &= ~15; // alle Leds aus

lcd_setCursor(0,1);
printf("WERT LI: %4d", data[0]);
lcd_setCursor(0,2);
printf("WERT RE: %4d", data[1]);
Msleep(100);
}
return(0);
}

ISR (TIMER2_COMP_vect)
{
PORTA ^= 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!
PORTA &= ~acs_36kHz; // bei Nulldurchgang soll die IR-LED aus sein!
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;
PORTA |= acs_led_l; // ACS LED left on
while((PINA & acs_tsop) && (OCR2 > 151))
{
acs=30; //15 Impulse senden, acs wird in OVF-ISR runtergezählt
while(acs);
OCR2--;
}
PORTA &= ~acs_led_l; // ACS LED left off
data[0]=OCR2;
while(!(PINA & acs_tsop)); // warten bis keine Echo mehr

OCR2=253;
PORTA|= acs_led_r; // ACS LED right on
while((PINA & acs_tsop) && (OCR2 > 151))
{
acs=30;
while(acs);
OCR2--;
}
PORTA &= ~acs_led_r; // ACS LED right off
data[1]=OCR2;
while(!(PINA & acs_tsop));
}

Nun wird für Links und Rechts jeweils der Wert von 252 angezeigt. Sollte der Wert sich nicht ändern, wenn die Bee bewegt wird?

radbruch
15.01.2010, 19:54
Hallo

Was war denn der Fehler? Ich habe nichts gefunden. Weil mein LCD deine ACS-Pins belegt, mußte ich mein ACS an X2/3 anschließen. Diese Variante funktioniert (mit meiner Pinbelegung) an meiner 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 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)

#include <nibobee/iodefs.h>
#include <nibobee/led.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();

// 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=8; // 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=8;
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));
}

http://i2.ytimg.com/vi/ivMShzrRRC8/default.jpg (http://www.youtube.com/watch?v=ivMShzrRRC8)
http://www.youtube.com/watch?v=ivMShzrRRC8

Das ACS ist superempfindlich, beachte meine Abschirmung über den LEDs. In ACSData() kann man die Anzahl der Impulse pro Burst einstellen. Im Video verwende ich nur 8 Impulse und der Empfänger reagiert immer noch enorm empfindlich. Vielleicht sollte man die Trägerfrequenz absichlich etwas "verbiegen" um weniger empfindlich zu sein.

Gruß

mic

pinsel120866
15.01.2010, 21:22
Hi,

mit diesem Code habe ich bis jetzt das beste Ergebnis erzielen können:

// nibobee IR-LEDs an PA0 und PA1 im Wechsel mit 36kHz ansteuern

// https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=479870#479870

// 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


#include <nibobee/iodefs.h>
#include <nibobee/led.h>
#include <nibobee/sens.h>
#include <nibobee/i2cmaster.h>
#include <nibobee/lcdi2c.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)
{
OCR2=253;
PORTA |= (1<<PA1); // ACS LED left on
while((PINA & (1<<PA2)) && (OCR2 > 151))
{
acs=6; //6 Impulse senden, acs wird in OVF-ISR runtergezählt
while(acs);
OCR2--;
}
PORTA &= ~(1<<PA1); // ACS LED left off
data[0]=OCR2;
while(!(PINA & (1<<PA2))); // warten bis keine Echo mehr

OCR2=253;
PORTA|= (1<<PA0); // ACS LED right on
while((PINA & (1<<PA2)) && (OCR2 > 151))
{
acs=6;
while(acs);
OCR2--;
}
PORTA &= ~(1<<PA0); // ACS LED right off 6
data[1]=OCR2;
while(!(PINA & (1<<PA2)));
}

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

led_init();
sens_init();
i2c_init();
lcd_init(0x27);
lcd_setBacklight(0);
printf("Pinsel's NIBObee");

// 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();

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

DDRA |= (1<<PA3)|(1<<PA1)|(1<<PA0);
PORTA &= ~((1<<PA3)|(1<<PA1)|(1<<PA0));
//DDRB |= (1<<PB4); // Test mit LineLEDs
//PORTB &= ~(1<<PB4);

// while(!sens_getLeft() & !sens_getRight()) // solange keine Taste gedrückt wird
// {Msleep(200); PORTB ^= (1<<PB0);} // hektisches Blinken mit LED0

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);
lcd_setCursor(0,1);
printf("Links: %4d", data[0]);

if(data[1] > 251) led_set(2,1);
else if(data[1] > 230) led_set(3,1);
lcd_setCursor(0,2);
printf("Rechts: %4d", data[1]);
Msleep(500);
}
return(0);
}
ISR (TIMER2_COMP_vect)
{
PORTA ^= (1<<PA3); // IR-LEDs togglen
}
// Frequenzkorrektur 512-416 plus 3 Takte fürs Laden von TCNT2?
ISR (TIMER2_OVF_vect)
{
TCNT2 = 99;
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);
}


Du hast recht, ich musste ebenfalls die Impulse auf 6 reduziert um den besten Kompromiss zu erhalten. Die Schrumpschläuche habe ich ebenfalls um 1 cm verlängert, um nicht zu viele Störungen zu empfangen. Weiters musste ich die Bee vorne höher legen um die Reflexionen des Fussbodens zu reduzieren. Die Empfindlichkeit ist immer noch sehr hoch, besonders die Differenz der Werte links und rechts beträgt bei mir etwa 20 Einheiten.

Aber im Grossen und Ganzen bin ich sehr zufrieden mit dem Ergebnis, ich werde versuchen ein Programm zu schreiben bei dem sich die Bee autonom im Raum bewegt.

Nochmals vielen Dank für deine Unterstützung, vielleicht hast du noch Lust die Geschichte in BASCOM zu übersetzen?

To be continued...

radbruch
16.01.2010, 14:20
Hallo

Hier die Umsetzung der ACS-Funktion in Bascom:
https://www.roboternetz.de/phpBB2/viewtopic.php?p=480966#480966

Die IR-Trägerfrequenz habe ich allerdings noch nicht gemessen.

Gruß

mic

Schreibtisch
05.01.2011, 16:38
Hey ich würde mir gerne die IR- Abstandsmessung nachbauen. Hab mir das alles auch schön durchgelesen. Nur verstehe ich nicht wieso ihr beiden noch zwei Kondensatoren auf der Platine verbaut habt. Ich vermute die sind zur Rauschunterdrückung. Könnt ihr mir mal den Schaltplan für die Patine geben oder sagen wie groß, für was, und wo die Kondensatoren angeschlossen werden?

Ich hab mir die Arbeit und den Schaltplan von raid_ox am Asuro auch durchgelesen (https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=252907#252907)
nur er verwendet keine Kondensatoren.

Danke schon ma im vorraus :)

radbruch
05.01.2011, 17:18
Hallo

Ja, die Kondensatoren dienen der "Rauchunterdrückung" und werden in den Datenblättern der IR-Empfänger empfohlen. Hier verwende ich z.B. den IRM 2636A (http://conrad.de/goto.php?artikel=156405), der asuro hat nen SFH5110 (http://www.reichelt.de/?ACTION=3;ARTICLE=37920;PROVID=2402) und der probot einen TSOP1736 (http://www.conrad.de/ce/de/product/171069/). Link zu den Datenblättern sind jeweils auf den Katalogseiten zu finden ;)

Der kleine Kondensator stammt vom asuro (C2/100nF). Dessen Wert für den Vorwiderstand seines SFH5110 scheint mir allerdings nicht optimal:
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=424521#424521

Viel Erfolg beim Nachbau.

mic

Schreibtisch
05.01.2011, 17:21
das war mal eine schnelle Antwort, in die Datenblätter hätte ich natürlich schauen können :D dankeschön

Schreibtisch
12.01.2011, 17:53
Yo ich muss nochmal eine Frage stellen
Ich habe IR- Transistoren nur mit 2 Pins Zuhause rumliegen und musste mir selbst eine Schaltung überlegen. Nun reagiert der IR Transistor nur auf direkte Lichteinstrahlung und nicht auf Reflexionen. Außerdem funktionieren die IR-LEDs nicht mehr wenn ich den R1 größer 120k Ohm wähle. Was mache ich falsch, einer ne Idee?

hardware.bas
12.01.2011, 18:14
Wenn Du R1 zu gross machst, reicht der Basisstrom nicht mehr, den
rechten Transistor durchzusteuern, so dass Dieser nicht mehr den
PEGEL nach GND ziehen kann. Empfohlene Abhilfe:
der Rc muss sicherlich nicht ein 1k sein, ich denke 4,7k oder 10k
machens auch, dann werden auch die Verhältnisse im Basiskreis
des rechten Transistors günstiger und R1 kann wesentlich
erhöht werden. VG Micha

Schreibtisch
12.01.2011, 19:13
hmm, geht schon besser danke.
Aber der IR Transistor reagiert immer noch nur auf direktes IR-Licht aus der Fernbedienung. Die blinkenden, angeschlossenen IR- LED's interessieren den nicht.

workwind
12.01.2011, 19:52
Im Schaltplan zur NIBObee (http://download.nicai-systems.com/nibo/nibobee_schematic_1_07.pdf) ist auf Seite 1 die Anordnung für die Fototransistoren des Liniensensors gezeigt.
Diese Schaltung kann man auch zur Hindernisdetektion benutzen, dabei ist kein zusätzlicher Transistor zur Verstärkung notwendig.
Die Empfindlichkeit kann man mit der Wahl des Widerstandes beeinflussen: je höher der Wert desto Empfindlicher....

knapsen
05.10.2011, 10:59
kannst du was über die Genauigkeit (mm) und Geschwindigkeit (Hz) deiner Methode sagen?

elektrolutz
05.10.2011, 12:40
Hallo knapsen,

neue IR-Abstandsensoren für den NiboBee siehe hier: http://www.nicai-systems.com/de/zubehoer/bkit-xs.html

Die Messgenauigkeit und Messweite hängt sicherlich von den Lichtverhältnissen ab (z.B. störendes UV-Licht) und von Form und Farbe des gemessenen Objektes ab.

Die Messgeschwindigkeit hängt von der Taktfrequenz des verwendeten Proz ab (für den NiboBee gibt es ja ein Tuning-Kit) und natürlich vom Programm.

knapsen
06.10.2011, 10:33
Ich würde gerne die Wellenbewegung einer eingefärbten Wasseroberfläche in einem untransparenten Tank messen.
Es wäre doch möglich, die Wellenhöhe durch den Abstand vom Sensor zur Oberfläche zum Zeitpunkt x an einem Punkt zu detektieren. Dabei muss das es aber auf ca. 1mm genau sein und mir ca. 50 Daten pro Sekunde (50Hz) liefern.

elektrolutz
06.10.2011, 12:34
Hallo knapsen,

oops, da wirst Du aber mit den "Trivial-Sensoren" des NiboBee nicht weit kommen.

Schau mal hier: http://www.de.endress.com/#products/fuellstand Hier findest Du einiges an Informationen zu Möglichkeiten und Tücken der Füllstandmessung.

Die Sende-Diode strahlt einen Lichtkegel aus, je weiter der Abstand des Messpunktes, desto breiter ist also auch der Lichtkegel. Der Empfänger reagiert dann auf alle Reflexionen aus diesem Lichtkegel.
Begrenzt man den Lichtkegel zu stark, reduziert sich auch die brauchbar abgestrahlte Lichtmenge und somit die Empfindlichkeit der Senorik.
Bündelt man das Licht (wie bei einen Laserstrahl) stellt sich die Frage, ob der reflektierte Lichtstahl auch noch den Empfänger trifft.

Speziell bei Flüssigkeiten ist eine optische Messung etwas problematisch, denn Flüssigkeiten verdunsten und der Nebel setzte sich auf den Optiken der Sensoren ab und stört so die Messgenauigkeit.

Welche Mittel/Gerätschaften stehen Dir für die Lösung dieser Messaufgabe zur Verfügung?

Recht einfach wäre eine Messung durch einen Schwimmer in einem offenen Röhrchen, hier muss man dan aber berücksichtigen, dass dabei die Wellenspitzen der Wasseroberfläche durch die Trägheit des Schwimmers und die Kapillarbildung im Röhrchen bedämpft werden.
(Ich gehe davon aus, dass Du die Wellenbewegung nur an einer Stelle betrachten möchtest und nicht die Wellenverteilung auf der Wasseroberfläche.)

knapsen
06.10.2011, 14:46
Um das "vernebeln" der Sensor-Optik zu verhindern könnte man diese ja ausreichend genug weit weg von der Wasseroberfläche anbringen. Dies sollte mit einer LASER-Messung eigentlich nicht so das Problem darstellen.

Die Idee mit dem Schwimmer hatte ich auch schon. Hier würde ich einfach ein Styropor Plättchen auf einem gespannten Faden durch den Tank führen und dessen Höhe ermitteln. Aber genau aus dem von dir genannten Punkt bin ich vorerst auf der Suche nach einer, am Besten, berührungslosen Möglichkeit.

Ich denke, an Mitteln und Gerätschaften sollte es nicht scheitern. Ich mache gleich einen Versuch mit einem Leica T-Scan Lasertracker und gefärbtem Wasser.

Genau, die Wellenverteilung ist vorerst unrelevant, ich möchte nur die Höhe an einem bestimmten Punkt detektieren.

elektrolutz
06.10.2011, 17:12
Hallo knapsen,

was verstehst Du unter "ausreichend genug von der Wasseroberfläche weg"?
Ich habe Dich so verstanden, dass Du in einem Tank messen möchtest; für mich ist ein Tank ein geschlossenes System, da bildet sich Schwitzwasser ohne Ende und an allen Stellen.

Ein teures Gerät, um es in einen Tank zu stecken und ich bin mir nicht sicher, ob ein Lasertacker die Wasseroberfläche erkennen kann.
Ich meine mal gelesen zu haben, dass bei Lasermessungen auf stark glänzenden Flächen, die Fläche eingestaubt werden muss.
Aber berichte bitte von Deinen Erkenntnissen.

knapsen
07.10.2011, 10:42
Hallo knapsen,

was verstehst Du unter "ausreichend genug von der Wasseroberfläche weg"?
Ich habe Dich so verstanden, dass Du in einem Tank messen möchtest; für mich ist ein Tank ein geschlossenes System, da bildet sich Schwitzwasser ohne Ende und an allen Stellen.

Ein teures Gerät, um es in einen Tank zu stecken und ich bin mir nicht sicher, ob ein Lasertacker die Wasseroberfläche erkennen kann.
Ich meine mal gelesen zu haben, dass bei Lasermessungen auf stark glänzenden Flächen, die Fläche eingestaubt werden muss.
Aber berichte bitte von Deinen Erkenntnissen.

Der Tank ist glücklicherweise oben offen, also nennen wir ihn Behälter. :)

Erkannt hat der Tracker die Oberfläche, weil ich sie mit weißer Wandfarbe eingefärbt habe, die Ergebnisse sind nur schwer zu interpretieren, da der Tracker keinen Zeitstempel hat, ergo Interpolation über Y-Achse und Zeit. Bin ich aber noch nicht zu gekommen.

Ich würde es auch lieber rudimentärer mit einem Laser-Abstandsscanner, ähnlich wie die aus dem Bamarkt machen wollen. Die sind erstmal punktuell, was vollkommen ausreichend für mich ist und außerdem könnte man deswegen dort eher die Zeit mitloggen.
Den richtigen für meine Anforderungen habe ich aber noch nicht gefunden.

elektrolutz
10.10.2011, 10:19
Hallo knapsen,

habe mal ein wenig herumgesucht.
Gute Geräte haben ihren Preis, aber schau mal hier: http://www.waycon.de/lasersensor.html