PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Helligkeitsmessung mit LEDs



SprinterSB
21.02.2006, 14:38
Zu Anzeigezwecken verwende ich in meinem aktuellen Projekt eine 3·9 Matrix aus LEDs an einem AVR. Die Anzeige soll einerseits bei heller Umgebung/Sonne gut ablesbar sein, andererseits aber bei Dunkelheit/Dämmerlicht nicht unangenehm hell sein.

Also muss die Umgebungshelligkeit gemessen werden. Was liegt da näher, als die LEDs selbst als Fotodioden zu nutzen?!

Die Anzeige besteht aus 3 Zeilen zu je 9 LEDs. Die LEDs in einer Spalte sind baugleich und können daher den gleichen Vorwiderstand R4 nutzen. Jede Spalte (Kathoden) braucht einen µC-Port, abenso jede Zeile (Anoden). Insgesamt brauchen die 27 LEDs also 12 Ports.

Die Zeilen werden im Zeit-Multiplex angesteuert. Unterschiedliche Helligkeiten werden realisiert, indem über dieses Zeit-Multiplex eine Software-PWM gelegt wird. Grundtakt (Interrupt-Last) ist 20kHz, selbst 30kHz sind problemlos realisierbar (mit avr-gcc und 16MHz Systemtakt).

Zur Helligkeits-Messung werden Anoden und Kathoden kurzzeitig (1µs) auf HIGH gelegt und danch der Kathoden-Port (AIN1) auf INPUT geschaltet. Der zum Input-Capture verdrahtete Analog-Komparator vergleicht die Spannung an den 3 LED-Kathoden mit der Spannung am Spannungsteiler R5-R6, welche dicht unter VCC liegt. Beim Unterschreiten der Schwelle wird ein Input-Capute-Ereignis ausgelöst und die zwischen "Loslassen" der Kathode und Unterschreiten der Schwellspannung verstrichene Zeit gemerkt. Diese Zeit (bzw. Zählerstand) ist ein Maß für die Umgebungshelligkeit.

Zum Einsatz kommen High-Efficiency LEDs: rote vom GaAlAs-Typ, gelbe vom InGaAlP-Typ, grüne und blaue vom GaInN-Typ. Gemessen wird Rot. Glau und Grün sind ungeeignet zur Messung.

Zusäzlich hängen in der LED-Matrix 3 Taster als Eingabe-Medien. Um einen Taster auszuwerten, wird die angeschlossene Anode als INPUT mit aktiviertem Pullup betrieben. Bei gedrücktem Taster liest man LOW, bei ungedrücktem HIGH. Die 3 Taster verbrauchen also keine extra-Ports! Die Dioden sichern die Anoden beim Drücken von mehr als einem Taster.

Die maximal 9 aktiven LEDs pro Zeile werden ohne externen Zeilentreiber direkt durch den AVR angesteuert. Der Maximalstrom von 20mA/Port reicht locker, auch in heller Umgebung alle LEDs mit ausreichen Saft zu versorgen – trotz Helligkeitsverlust durch Multiplex.

Das ganze geht natürlich auch, wenn man nur eine LED zum Messen nimmt und diese nicht in einer Matrix hängt...

AlexAtRobo
21.02.2006, 15:35
Funktioniert das schon? Wenn ja, halte ich das Extrem geniale Lösung. Gratulation.

lg

Alex

ogni42
21.02.2006, 15:50
Super Idee! Halte uns unbedingt auf dem Laufenden.

Könnte man die LED nicht auch als Fotodioden (sprich: Stromquelle) nutzen? Die Anoden werden dann auf Low gelegt und der Fotostrom lässt am Eingangswiderstand des Komparators eine Spannung gegen GND abfallen (ggfs. einen 1MOhm Widerstand nehmen).

Hab's gerade mal mit einer weißen 10000mcd LED ausprobiert: Unter einer voll aufgedrehten 25W Halogenlampe messe ich über 850mV und 144kOhm (beides in Sperrrichtung).

SprinterSB
21.02.2006, 16:26
Ja es funktioniert. Das ganze geht auch andersrum, indem ich die Anoden auf LOW lege und die Kathoden auf HIGH, die Dioden also vorher auflade (in Sperrichtung als C betrachtet). Von den Messwerten sieht das genauso aus.

Diese Ansteuerung hatte ich versucht, um zur Helligkeitsmessung die Kapazitätsänderung der LEDs zu messen. Durch die Bestrahlung ändert sich die Dicke der Sperrschicht. Allerdings kann ich damit nix messen. Zum Entladen der Dioden hatte ich einen 10MOhm parallel geschaltet, aber der war viel zu klein. Warum diese Messung nicht funktioniert, ist mir unklar. Die Kapazität einer LED schätze ich auf 20pF, insgesamt wären das bei meiner Anordnung 60pF ohne Parasiten. Mit nem AT90S2313 hatte ich mal nen C-Messer gebaut, daher der Schätzwert von 20pF.

@ogni42: Wenn man die Anoden auf LOW legt, pumpen die LEDs Elektronen zur Kathode und ziehen das Potential unter GND. Geht nicht, oder?

Meine momentane Lösung leidet an einer recht größen Schwankung der Messwerte, von der ich nicht ganz weiß, woher sie kommt. Die Zeit-Werte schwanken locker mit +/-3dB, was aber für meine Zwecke gut ausreicht. Zudem glätte ich per Software. Messbereich habe ich von 0 bis 0x1ff vorgesehen, danach gibt's nen Timeout und die Messung beendet (passiert bei Dunkelheit).

Schwieig (und für mich interessanter) ist nicht die Messung im Hellen, sondern wenn's dunkelt.

Die Software ist übrigens recht kompliziert. Nicht nur die Helligkeitsmessung will den Timer1 benutzen, sondern auch noch 2 andere Teile der Software mit anderer Initialisierung von Timer1! Dafür musste ich ein Ressourcen-Management für Timer1 einführen. Zudem muss die Messung in einer extra geschaffenen Multiplex-Lücke stattfinden und dann Timer1 auch verfügbar sein...also nicht trivial.

Erschwerden kommt hinzu, daß sich Tasterdruck-Messung und Helligkeitsmessung eigentlich ausschliessen. Dennoch brauch ich im 10ms-Abstand Poll-Werte von den Tastern zum Entprellen, lange/kurze Tasterdruck zu unterscheiden oder Auto-Repeate zu realisieren. Nichtsdestotrotz funktionieren auch die Taster problemlos... *grinz*

HannoHupmann
22.02.2006, 06:52
Jo ist echt nicht schlecht, das sind diese Lösungen die wirklich was hermachen. Ich hab zwar keine Anwendung bei mir dafür, obwohl ich könnte meine beiden Leuchtdioden am Roboter auch das Umgebungslicht scannen lassen und dann je nach dem einschalten.

Was muss ich den genau machen wenn ich nur 2 Dioden reinhäng und braucht man Spezielle Leuchtdioden?

SprinterSB
22.02.2006, 08:59
Messen kann ich an 7 Eingängen: Direkt am AIN1 wie gezeigt, oder an den 6 ADC-EIngängen. Ich verwende den ADC nicht (wenn, ginge das nur im Zeitmultiplex). Bei nicht aktiviertem ADC kann man den Ausgang des Analog-Multiplexers zum Analog-Comparator verdrahten. Qualitativ sehe ich keinen Unterschied an den Werten, ob ich das Signal durch den Multiplexer schleife oder nicht.

Welche LEDs geeignet sind musst mal ausprobieren. Ich hatte ürsprünglich angedacht, grüne LEDs zu nehemen, weil das Empfindlichkeitsmaximum des Auges im Grünen liegt und die erzeugten Elektronen energiereicher sein sollten. Aber die besten Ergebnisse hab ich mit roten LEDs, evtl gehen auch gelbe. Die Substrate stehen oben, aber bei Rot geht vermutlich auch ne "normale" oder eine low-Current. Ich verwende ultrahelle (Kingbright vom Reichelt), weil ich dann 9 LEDs parrallel an einen µC port hängen kann.

@ogni42: Die Fotospannung messe ich in Durchlassrichtung, also + an der Anode und - an der Kathode. Ich hatte es auch schon mal mit einem Transimpedanzwandler zur Strommessung versucht und mit der Spannung den ADC gefüttert. Aber das braucht viel externe Hardware (OpAmps, Widerstände...) und man muss sehr nahe an der Versorgungsspannung messen. Ich hatte das recht schnell wieder aufgegeben, weil zu kompliziert. Gleichzeitig noch die LED leuchten zu lassen hatte ich erst garnicht versucht, weil die Konvertierungszeiten im ADC viel zu lange sind.

Was bisher nicht klappt ist wie gesagt, die Kapazität auszumessen. Das solle eigentlich problemlos gehen :-k Die Kapazität ist spannungsabhängig, grob vermute ich mal, daß C linear mit der Dicke der Sperrschickt geht. Ne "normale" C-Messung schlägt aber fehl :-(
Ne C-Messung wäre natürlich viel schneller! Bei 10MHz erinnere ich mich an 18 Takte pro pF (hängt natürlich vom R ab...)

ogni42
23.02.2006, 19:07
Ich halte die Widerstandsmessung auch für praktikabler, eben wegen des hohen Innenwiderstandes der LED. Beide Messverfahren funktioneren.

Du hast recht. Positive Spannung ergibt sich in Durchlassrichtung.

SprinterSB
23.02.2006, 19:17
Nö, ich messe nicht über einen Widerstand. Der R4 ist nur der Strombegrenzer, wenn eine der LEDs in der Spalte leuchten soll und AIN1 als OUT geschaltet ist.

Beim Messen stört der R4 nicht, weil AIN1 als Eingang natürlich sehr hochohmig ist.

Bei einer Strommessung ist ein Elektron ja "weg", nachdem es durch den Widerstand gehuscht ist. Wenn man die Elektronen ein Potential aufbauen lässt, trägt es mit zum Potentialaufbau bei.

Noch was: Wenn man keine Taster an der Matrix hängen hat, kann man die ganze Chose auch "rumdrehen", also überall Plus/Minus und Kathoden/Anoden tauschen. Dann kann man den AIN0 einsparen, wenn man als Referenz die interne Bandgap von 1.2 V nimmt :-)

Ich hab die Logik aber so rum, weil ich die PullUps für die Taster brauche und AVR keine PullDowns hat. Ausserdem ist meine Spannung näher an Vcc als 1.2 V (paar 100mV oder so), das spart Messzeit.

SprinterSB
02.03.2006, 11:07
Zwischenzeitlich hab ich die Schaltung etwas abgeändert:
Die Schalter hängen nicht mehr an den Anoden, sondern in Reihe mit je einem Widerstand zwischen GND und je einem Kathoden-Port (ausser dem Port, an dem die Helligkeit gemessen wird).

Vorteile:
-- Es können 8 Taster angeschlossen werde, ohne extra-Ports zu verbrauchen
-- Dioden werden überflüssig (waren sie vorher eigentlich auch schon), und die Taster müssen sich nicht einen Widerstand teilen
-- Bei gedrücktem Taster schlägt die Strombelastung nicht zu den Anoden-Ports, sondern zu den weniger beanspruchten Kathoden-Ports.

Um ein Signal als LOW zu erkennen, muss die Spannung am Port kleiner sein als 0.2*VCC. Da die internen Pollups minimal 20kΩ haben, ergibt sich als Obergrenze für die Taster-Widerstände 5kΩ. Mit ausreichend Sicherheitsabstand tut es also R=3.9kΩ.

Das einzige, was jetzt noch fehlt, ist die Helligkeit der LEDs so zu kalibrieren, daß sie bei jeder Umgebungshelligkeit etwa gleich hell erscheinen, sowie ein (Software-) Schmitt-Trigger.

themaverick
29.03.2006, 18:10
@ SprinterSB

könntest du den Quellcode deines AVR posten ? würd mich sehr interessieren wie du das gelöst hast. ich probiere schon seit knapp 11 stunden dran rum und es klappt nicht wirklich :( ...

SprinterSB
30.03.2006, 08:26
Ok, werd bei Gelegenheit den C-Code mal rauskramen.

SprinterSB
31.03.2006, 08:44
Hier sind mal die Quelldateien des Moduls zur Helligkeitsmessung.

Erklären tu ich mal nix... Falls was unklar ist, kannst ja fragen (bevor ich hier ins Blaue rein was erkläre und mir den Wolf tippsle ;-))

Es ist als Anregung gedacht. "Out of the Box" wird es wohl eh nicht laufen...

hell.h

#ifndef _HELL_H_
#define _HELL_H_

extern void hell_init();
extern void hell_intro();
extern uint8_t hell_loop();
extern void hell_mess_start();

#define HELL_MAX 0x1ff

enum
{
HS_IDLE, // inaktiv
HS_AWAIT_INTRO, // warten auf Schedule von timer1-job
HS_AWAIT_PATTERN_GAP, // warten auf Loch im Anzeigen-MUX
HS_MESS,
HS_MESS_OK,
HS_MESS_TIMEOUT,
HS_MESS_DONE
};

#define HELL_NWERT 16

typedef struct
{
uint8_t debug;
uint8_t state;
uint16_t icr1;
uint16_t max;
uint16_t wert;
uint8_t count;
} hell_t;

extern hell_t hell;

#endif // _HELL_H_

hell.c

#include <AVR.h>
#include <avr/delay.h>

#include "ports.h"
#include "pattern.h"
#include "timer1-job.h"
#include "hell.h"
#include "main.h"

hell_t hell;

void hell_init()
{
hell.state = HS_IDLE;
// AIN+ als Eingang
MAKE_IN (PORT_AIN0);
CLR (PORT_AIN0);
// disable AC (ACD = 1)
// AC Input Capture on -> disconnect from Input Capture of Timer/Counter1
// AIN0(+) = kein Bandgap
ACSR = (0 << ACD) | (0 << ACIC) | (0 << ACBG);

// AC MUX enable (AC-Input = ADC-MUX-Output, falls ADC disabled)
SFIOR |= (1 << ACME);
ADMUX = 0x7 & PORTC_AIN1;
}

void hell_intro()
{
hell.state = HS_AWAIT_PATTERN_GAP;
hell.max = HELL_MAX;
}

void hell_mess_start()
{
SET_ANODES;

hell.state = HS_MESS;
hell.debug = 0;
// Kathoden wurden in pattern::pattern_out() gesetzt.

// enable AC (ACD = 0)
// AC Input Capture on -> connect to Input Capture of Timer/Counter1
// AIN0(+) = kein Bandgap
ACSR = (0 << ACD) | (1 << ACIC) | (0 << ACBG);

// stop timer1
// disconnect timer1 from output pin OC, no PWM
TCCR1A = 0;
TCCR1B = (1 << ICNC1) | (1 << ICES1);

// Load capacity via AIN- push-pull

cli();

OCR1A = hell.max;

// Reset Timer1
TCNT1 = 0;

// PortC.x Pegel
_delay_loop_1 (30);

// enable Input Capture Interrupt
TIMSK |= (1 << TICIE1) | (1 << OCIE1A);

// start timer 1 at 1/64 (011)
// no clear on compare match
// noise cancel
// input capture edge = raising (mess = AIN(-))
// don't change ICNC1 and ICES1 i.o. not to trigger an IRQ

// clear Timer1 Flags
TIFR = TIFR_T1;

TCCR1B = (1 << ICNC1) | (1 << ICES1) | (1 << CS11) | (1 << CS10);

// here we go!
// start discharging via external resistor
// AIN- to high Z
MAKE_IN (PORTC_AIN1);
CLR (PORTC_AIN1);

sei();
}


uint8_t hell_loop()
{
if (hell.state <= HS_MESS)
return T1_CLAIM;

if (hell.state == HS_MESS_TIMEOUT)
hell.icr1 = hell.max;

hell.state = HS_MESS_DONE;

// disconnect InCapt1 from AC
ACSR = (0 << ACD) | (0 << ACIC);

return T1_DONE;
}

SIGNAL (_handle_icp1_oc1a)
{
hell.icr1 = ICR1;

uint8_t state = HS_MESS_TIMEOUT;

if (ACSR & (1 << ACIC))
state = HS_MESS_OK;

hell.state = state;

SET (PORTC_AIN1);
MAKE_OUT (PORTC_AIN1);

TIMSK &= ~TIMSK_T1;
}

// Bit ACIS in ACSR merkt, ob ein Overflow (OC1A) auftrat,
// denn beim Betreten der ISR wird das OCF1A gelöscht.
void __attribute__((naked))
SIG_OUTPUT_COMPARE1A()
{
cbi (ACSR, ACIC);
rjmp (_handle_icp1_oc1a);
}


void __attribute__((naked))
SIG_INPUT_CAPTURE1()
{
rjmp (_handle_icp1_oc1a);
}

themaverick
31.03.2006, 11:11
danke
ich werds mal zerpflücken und schaun ob es damit geht.
effectiv kann ich den code garnicht selber nutzen da ich mit bascom auf basic angewiesen bin und daher c-code nicht verwenden kann. aber basic code auf bassis deines c codes zu schreiben sollte auch möglich sein :)

grüße
The Maverick

SprinterSB
31.03.2006, 12:28
WO willst du den Code denn einsetzen?
Ich verwende ihn wie gasagt zur Helligkeitssteuerung von LEDs, die auch die Umgebungshelligkeit bestimmen. Da muss der Code schon recht flott sein, sonst flackert die Anzeige, denn die Messungen müssen genau in die Lücken der Soft-PWM eingepasst werden.

Zudem müssen die Messungen bei der Auswertung der Tasterdrucke eingearbeitet werden, damit die Taster keinen Unsinn liefern und die Messung nicht ungültig wird, wenn eigentlich eine Taster-Abfrage ansteht.

Last not least brauche ich zur Messung Timer1. Diesen Timer brauche ich aber noch für andere Teile der Software (Soft-UART, PWM, etc..) und muss daher die Resource "Timer1" über ein Resourcen-Management verwalten. Die Messung wird dann vom Scheduler gestartet. Bei dir kann es also um einiges einfacher werden.

Zu bedenken möchte ich noch geben, daß die Messung recht stark streut und um so länger dauert, je dunkler es ist. Bei Schummerlicht ist die Messung also langsamer als sie mit einem ADC wäre.

Der große Vorteil des Ansatzes ist, daß man praktisch keine extra Hardware braucht. Eine Messung mit dem internen ADC wäre so gar nicht möglich, weil der ADC-Eingang viel zu niederohmig ist.

themaverick
31.03.2006, 12:58
ich möchte das hier für einsetzen:
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=170976&highlight=#170976

allerdings ohne taster
ich hab mit dem ADC zu meinem project schon rumgespielt aber das eigenlicht einer led ist scheinbar zu schwach oder ich bekomm den code nicht richtig hin. jedenfalls bekomm ich nennenswerte ausschläge nur wenn ich meine tischleuchte vor die led halte. aber einfach nur den finger auf die blinkende led halten um das rückstrahlende licht zu messen klappt nur minimal :(
für tips wär ich dankbar.
grüße
Tm

SprinterSB
31.03.2006, 13:29
Versuchst du etwa, das mit einer Led zu machen? :-k Nö, oder???

themaverick
31.03.2006, 13:55
bisher hab ichs nur mit einer led getestet :) aber später solls eine 8x8 matrix werden ..
schau dir das project das ich in dem anderen beitrag verlinkt hab an. es geht halt wirklich darum das licht von einer led selbst wieder einzufangen.
also led leuchtet und strahlt ins nichts. wenn mein finger drauf ist geht das licht an meine haut und wird zurückgeworden. das licht soll wieder eingefangen werden und gemessen werden.

SprinterSB
28.08.2006, 11:22
Super Idee! Halte uns unbedingt auf dem Laufenden.

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

1hdsquad
17.02.2007, 21:53
Respekt! Ich habe immer schon von der bidirektionalität jedes bauteils geträumt... ;.)