LED Touch Sensor
Has anybody ever done this? Used an LED to sense light, basically
use it as a touch panel? Well, as it turns out, a bunch of
other people have done this...but I haven't, so that's what makes it
cool in my book. I think I earn style points. I was doing
some reading in the good book (otherwise known as 'The Art of
Electronics') and found a section talking about this, so I figured
I'd try it out. I ended up with a string of 7 IR LEDs ('cause
that's all I had on hand), anodes to a common pin, cathodes to
different pins on a PIC18F4620, all pins are schmidt trigger types
(PortD, PortC, PortA.4, etc. The TTL inputs draw too much current and
don't give a decent reading). I've also got an LCD hooked up for a
display of whatever was going on. (Note: I tried using
standard LEDs of various colors, but they didn't seem to work very
well for me. I would've liked to use visible light LEDs to give
a neat looking input panel type of thing, but it didn't work.
Jist of the story, the software runs, I've got the LEDs lined up in a row, I run my finger over (but not touching) the IR LEDs, I set up a block on the LCD to basically follow my finger running over the IR LEDs! This is neat.
To read the IR LEDs, set 2 pins as output. You first forward bias the IR LED (one pin high, one pin low) for about 1ms. Next you reverse bias (high goes low, and low goes high) to essentially 'charge' them up for another 1ms or so (every diode has a certain amount of capacitance when reverse biased. Then you set the pin that's high when the LED is reverse biased to an input. Immediately after that you set up a counter inside a tight if/then. When the input changes from a 1 to a 0 (because the voltage 'stored' in the LEDs capacitor is decaying), you save the count and exit the if/then. The count you get when you exit the if/then is inversely proportional to the amount of light hitting the IR LED, the lower the number, the more the light; the higher, the darker. I then compare all of the numbers, looking for the lowest number and display a solid block in that character position.
Here's a snippet of example PBP code (without the generic sections like LCD defines, config settings, etc. oh, and my PIC is running at 40mhz, I don't know how well it'll work at slower speeds, if at all.):
looper var byte : anode var porta.1 'common anode of all IR LEDs
t1 var portc.5 : t2 var portc.4 : t3 var portc.1 : t4 var portc.0 : t5 var porte.2 : t6 var porte.1 : t7 var porta.4 'all IR LEDs cathodes pins
touchval var word : touchmin var word : touchmax var word : touchrange var word : touchpos var word : touchaverage var word : touchmidpoint var word : maxval var byte : maxtemp var word
skipsubs: 'generic setup I use for all my programs, YMMV
flags=0 : pause 1000 : intcon=0 : intcon.7=1 : intcon.6=1 : intcon.5=1 : intcon2=0 : intcon2.2=1 : intco3=0 : pir1=0 : pir2=0 : pie1=0 : pie1.5=1 : pie1.4=1 : pie2=0 : t0con=0 : t0con.7=1 : t0con.6=1
t0con=t0con+2 : t1con=0 : t2con=0 : t3con=0 : ccp1con=0 : ccp2con=0 : pwm1con=0 : eccp1as=0 : sspstat=0 : sspcon1=0 : sspcon2=0 : txsta=0 : txsta.6=0 : txsta.5=1 : txsta.4=0 : txsta.2=1 : rcsta=0
rcsta.7=1 : rcsta.6=0 : rcsta.4=1 : baudcon=0 : baudcon.3=1 : spbrgh=4 : spbrg=16 : adcon0=0 : adcon1=$f : adcon2=$ff : cmcon=7 : cvrcon=0 : hlvdcon=0 : trisa=0 : porta=0 : trisb=0 : portb=0 : trisc=0
portc=0 : trisd=0 : portd=0 : trise=0 : porte=0 : input switch1 : input switch2 : output led1 : led1=1 : led1=0 : pause 200 : led1=1 : pause 200 : led1=0 : pause 200 : led1=1 : pause 200 : led1=0 : pause 200
led1=1 : pause 200 : led1=0 : pause 200 : led1=1 : pause 200 : led1=0 : pause 200 : lcdout $fe,1 : switchignorecount=0 : menu=1 : switchdelay=1000 : output serialdataoutputpin : input serialdatainputpin
touchmin = 65000 : touchmax = 100
led1 = counter.0
for looper = 1 to 7 'light an LED, even though you can't see it
output anode : anode=1 : input t1 : t1=0 : input t2 : t2=0 : input t3 : t3=0 : input t4 : t4=0 : input t5 : t5=0 : input t6 : t6=0 : input t7 : t7=0
select case looper
output t1 : t1 = 0
output t2 : t2 = 0
output t3 : t3 = 0
output t4 : t4 = 0
output t5 : t5 = 0
output t6 : t6 = 0
output t7 : t7 = 0
pause 1 : anode = 0
select case looper 'reverse bias that same LED
output t1 : t1 = 1
output t2 : t2 = 1
output t3 : t3 = 1
output t4 : t4 = 1
output t5 : t5 = 1
output t6 : t6 = 1
output t7 : t7 = 1
select case looper 'switch that LED to an input to read it
'run a counter in a tight loop (loop3a) and keep checking and waiting for the particular LED input to drop from a logic 1 to a logic 0 because the voltage sitting on the LED due to the internal capacitance drops 'slowly'
touchval[ looper ] = touchval[ looper ] + 1 : if touchval[ looper ] > 65000 then goto kickoutloop3a
'if we wait too long or the pin is stuck, we'll never get out of the loop
select case looper
if t1 = 1 then goto loop3a
if t2 = 1 then goto loop3a
if t3 = 1 then goto loop3a
if t4 = 1 then goto loop3a
if t5 = 1 then goto loop3a
if t6 = 1 then goto loop3a
if t7 = 1 then goto loop3a
counter = counter + 1 'just a counter to show a 'heartbeat'
maxval = 0 : maxtemp = 0 'maxval keeps track of which led had the lowest value, maxtemp keeps track of the lowest value
for looper = 1 to 7 'check al 7 LEDs to find the lowest one
if touchval[ looper ] > maxtemp then
maxtemp = touchval[ looper ] : maxval = looper
'clear out the line, then display the number of the darkest LED
lcdout $fe,$80," ":lcdout $fe,$80+(7-maxval),DEC1 (7-maxval ) : lcdout $fe,$c0," ":lcdout $fe,$c0+(7-maxval),$ff
for looper = 1 to 7 : touchval[ looper ] = 0 : next looper
For some reason, this only seems to work with the IR LEDs I have on hand. That might be because they are all cheap LEDs. Somebody might get this to work with visible light LEDs, I don't know. Then I wonder if this will work with those bargraph LEDs. You could do something silly like those slider thingies on Star Trek where they run their fingers up a panel and the lights follow.
As soon as I get some more ambition, I'll post a quick schematic of how this is all hooked up, but rest assured, it's all very simple:
All LED anodes to one pin (TTL input type, NOT Schmidt trigger)
All LED cathodes to a schmidt trigger I/O port (i.e. on a PIC18F4620, PortD, PortC, some of PortA, etc.)
Normal power, ground, oscillator, etc. Although I did have to run my PIC18F4620 at 40mhz to get any reliable results. You might get away with a lower clock speed, maybe with a cleaner circuit, not on a solderless breadboard, that sort of thing.
And a generic LCD display hookup.
Like I said, I'm getting a kick out of this....
Maybe somebody else will too..