I/O LED (Part 2)

Part 123456

In the previous example, we observed the discharge time of the parasitc LED capacitance. In this way, LEDs can be used as light sensors. Let’s now see what we could do else when combining the emitting properties of the LED along with its sensing properties. In the next application, an LED will light up when darkness come. This application could be used for backlighting an LCD screen at night without the need for extra photodiode and additional circuitry (amplifier, reference voltage and comparator).

/*

	SelfLightening LED
	The LED will light up when the ambiant light is lower than a trigger point
	ATmega328 powered Arduinos only
	Copyright (C) 2011 Didier Longueville

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#define CATHODE_PIN PINB0
#define ANODE_PIN PINB1
#define LED_PIN PINB5

uint32_t defaultMaxTime = 20000; /* In us */
uint32_t maxTime = defaultMaxTime; /* In us */
uint8_t hysteresis = 5; /* In % */

void setup()
{  
	/* Initialize serial comm port */
	Serial.begin(115200); 
	/* Set output ports */
	DDRB |= ((1 << CATHODE_PIN) | (1 << ANODE_PIN));
	/* Make the led pin an output pin */
	DDRB |=  (1 << LED_PIN);
	blinkLed(3);
};

void loop() 
{
	ledLevel(lightPowerLevel());
	delay(1000); /* Pause */
};

void blinkLed(uint8_t cycles) 
/* Blink control led */
{
	PORTB &= ~(1 << LED_PIN); /* Turn control led off */
	for (uint8_t i = 0; i < (cycles << 1); i++)	{
		delay(200);
		PORTB ^= (1 << LED_PIN);
	}
};

uint8_t lightPowerLevel(void) 
/* Compute the light power ratio versus absolute triggering value
   In this way the measurement time is fixed */
{
	/* Mark Event */
	PORTB |= (1 << LED_PIN); 
	/* Turn LED off: reverse polarity, charge capacitor */
	PORTB |= (1 << CATHODE_PIN); /* Cathode to VCC */
	PORTB &= ~(1 << ANODE_PIN); /* Anode to Ground */
	/* Mandatory delay necessary for the pn junction equilibrium when the LED is on before sensing light */
	delay(10);
	/* Set input port for Cathode */
	DDRB &= ~(1 << CATHODE_PIN); /* Set Cathode pin in input mode */
	PORTB &= ~(1 << CATHODE_PIN); /* Set Cathode pin in high Z mode */
	delayMicroseconds(maxTime);
	uint8_t level = ((PINB >> CATHODE_PIN) & 0x01);
	/* Mark Event */
	PORTB &= ~(1 << LED_PIN); 
	DDRB |= (1 << CATHODE_PIN); /* Reset Cathode pin in output mode */
	/* Returned value */
	return(level);
};

void ledLevel(uint8_t level) 
{
	if (level == 0x01) {
		/* Turn LED on: forward polarity */
		PORTB |= (1 << ANODE_PIN); /* Anode to VCC */
		PORTB &= ~(1 << CATHODE_PIN); /* Cathode to Ground */
		maxTime = uint32_t(defaultMaxTime * (1.0 - (hysteresis / 100.0))); /* Hysteris */
	}
	else  {
		/* Turn LED off: no bias */
		PORTB &= ~(1 << ANODE_PIN); /* Anode to Ground */
		PORTB &= ~(1 << CATHODE_PIN); /* Cathode to Ground */
		maxTime = uint32_t(defaultMaxTime * (1.0 + (hysteresis / 100.0))); /* Hysteris */
	}
};

Prior to compiling the code, you must determine the triggering level above which darkeness occurs and set the maxTime variable accordingly. I noticed that the reading is unstable when the LED is on and light measurement occurs. I unfortunately cannot explain the reason why, just draw an hypothesis based on the impact of the chip temperature on its capacitance. So do not remove the delay(10); line. I added an hysteresis that you can change in order to avoid flikering under certain light conditions.

Next post on same subject

Leave a Reply

You must be logged in to post a comment.