How to use a simple LED as a light sensor, and dim a 7-segment LED display with PWM automatically in the dark.
Overview
This project demonstrates how to use a simple LED as a light sensor, and dim a 7-segment LED display with PWM automatically in the dark. When ambient light is detected, the display runs at full brightness. When it gets dark, software PWM reduces the display brightness to avoid blinding the user.
A LED as Light Sensor
A LED emits electricity when light hits its junction. By connecting a LED in reverse (cathode to ADC input, anode to ground), we can measure ambient light levels.
In the dark, the ADC reads a phantom voltage from the floating input. In light, the reverse voltage generated by the LED cancels it out, and the ADC returns a near-null value.
PWM Dimming
Software PWM adjusts the duty cycle of the display: 0% means the display is off, 100% means full brightness. Six dimming levels are available, controlled by the user via buttons.
Build the Circuit
Insert a standard LED into a DIP28 socket. Connect the cathode to RA5 (ADC channel AN4) and the anode to ground. The microcontroller is a PIC16F877A running with an 8 MHz crystal.
Source Code
The firmware uses Timer0 ISR for display multiplexing and software PWM. The ADC reads AN4 (RA5) every second. The display shows "HIGH" in light conditions and "DIMM" in dark conditions. RD0/RD1 buttons adjust the dimming level, and PORTC LEDs show the current dimmer value.
/* * PIC PWM LED DISPLAY DIMMER WITH SIMPLE LIGHT SENSOR * source code example for MikroC * target : PIC16F877A, crystal frequency : 8 Mhz */ #define INCVALUE PORTD.F0 #define DECVALUE PORTD.F1 #define SA 1 #define SB 2 #define SC 4 #define SD 8 #define SE 16 #define SF 32 #define SG 64 #define DP 128 #define segH SB+SC+SE+SF+SG #define segI SB+SC #define segG SA+SC+SD+SE+SF+SG #define segD SB+SC+SD+SE+SG #define segM SA+SB+SC+SE+SF long cntr ; unsigned char data[4] ; unsigned char dcurr ; unsigned char dimm = 0 ; unsigned char dimmValue = 7 ; unsigned char duty ; void interrupt(void) { if(INTCON.T0IF) { cntr++ ; dcurr++ ; PORTB = 0 ; if(dcurr == 4) { PORTA = 0b00000001 ; dcurr = 0 ; } else { PORTA <<= 1 ; } PORTB = data[dcurr] ; if(duty <= dimm) { if(dimm > 1) TRISB = 0 ; } else { TRISB = 0xff ; } duty++ ; duty &= 0b00111111 ; INTCON.T0IF = 0 ; } } main() { unsigned int v ; TRISA = 0xf0 ; TRISB = 0 ; PORTB = 0 ; TRISC = 0 ; TRISD = 0xff ; OPTION_REG = 0x80 ; INTCON = 0xA0 ; for(;;) { if(cntr >= 7812) { ADCON1 = 0x00 ; v = Adc_Read(4) ; ADCON1 = 0x07 ; if(v < 8) { dimm = 255 ; data[0] = segH ; data[1] = segI ; data[2] = segG ; data[3] = segH ; } else if(v > 16) { dimm = dimmValue ; data[0] = segD ; data[1] = segI ; data[2] = segM ; data[3] = segM ; } cntr = 0 ; } if(PORTD) { if(INCVALUE) { if(dimmValue != 0x3f) { dimmValue <<= 1 ; dimmValue |= 1 ; } } else if(DECVALUE) { if(dimmValue != 0x01) { dimmValue >>= 1 ; } } while(PORTD) ; } PORTC = dimmValue >> 1 ; } }