This is essentially a flashing LED project. However, the reason to build it was to demonstrate two techniques: generating pseudorandom numbers and using PWM for adjusting the LED brightness. The LEDs imitate the candle flame with the center LED lighting constantly. The battery lasts for several hours during the Halloween hight. The unit is intended to be put on a ground in one's garden.
The unit is assembled within a plastic pumpkin toy purchased in Walgreens. Actually, the toy had a handle and a motor used for a different purpose, but we needed just a pumpkin case from it. The PCB is mounted on a triple AAA batteries holder available from Radioshack. The round perf-board can also be found in Radioshack. The parts are connected by using the point-to-point method. The transistor is used to control the brightness of LEDs and is in turn controlled by a PWM signal generated by the PIC. The LEDs brightness is changing randomly, as well as the ON/OFF states of each LED and the state update time. The transistor can be replaced with a less powerful MOSFET, say 2N7000. The device is supposed to be used in darkness, so no ultra bright LEDs are used. The maximum drawing current is limited by choosing decent current limiting resistors and does not exceed 45mA. The average current consumption is about 30mA.
Schematic | Daylight demo | Night demo | ||
![]() AVI ~1.3Mb |
![]() AVI ~0.8Mb |
---|
The PIC runs at 2MHz frequency provided by internal oscillator. The PWM frequency for controlling the LED brightness is about 122Hz. The brightness is adjusted every 0.26sec provided by timer TMR1 with a 1:2 pre-scaler. The construction should be clear from the photos. When the pumpkin hull is closed, the plastic plate on the right photo below goes into a niche in the hull and locks there, so the entire construction is stable. There is no need in a power switch. Just put the batteries whenever you need it and let it go.
Construction | Top view | Back view | ||
The software implements two pseudorandom number generators. One of them generates 4-bit sequences which are used to setup time intervals for updating LEDs. The sequence numeric value is multiplied by 10msec to form a variable delay. The other generator generates 7-bit sequences which are used to light the LEDs. This generator is also used to modify the LEDs brightness by adjusting the PWM duty cycle. The duty cycle is modified every 100msec provided by timer TMR1 overflow interrupts.
The generator produce periodic sequences with periods 15 and 127, respectively. Their implementation mimics the Fibonacci LFSR (Linear Feedback Shift Register), see an article in Wikipedia for definitions. The polynomials representing the LFSRs are x4 + x3 + 1 and x7 + x6 + 1. Both polynomials are known to be primitive in GF(24) and GF(27), respectively, which guarantees the longest possible periods for the pseudorandom numbers. Run the supplied C code to convince yourself that this is indeed the case.
The main loop is pretty straightforward. The 4-bit and 7-bit pseudorandom sequences are stored in variables seq4 and seq7, respectively. We first display the old state by OR-ing these sequences together and forwarding the result to PORTC. The OR-ing is done to increase the number of LEDs on to increase the brightness. This affects the period of non-repeating sequences, of course, but it works quite well. Since PORTC in PIC16F684 has only 6 bits, and bit 5 is used to generate PWM, the two top bits of the 7-bit pseudorandom sequence are displayed by using pins 5 and 4 of PORTA. For this purpose we shift the sequence one bit to the right before forwarding it to PORTA.
loop movf seq4, w ; delay 0.1sec * seq4 movwf cnt call delay decfsz cnt, f goto $-2 movf seq4, w ; light the LEDs iorwf seq7, w movwf PORTC movwf cnt rrf cnt, f movf cnt, w movwf PORTA bcf STATUS, C ; update seq4 rlf seq4, f ; LFSR implementation of primitive movlw 1 ; polynomial x^4 + x^3 + 1 btfsc seq4, 3 xorwf seq4, f btfsc seq4, 4 xorwf seq4, f movlw 0x0F andwf seq4, f bcf STATUS, C ; update seq7 rlf seq7, f ; x^7 + x^6 + 1 movlw 1 btfsc seq7, 6 xorwf seq7, f btfsc seq7, 7 xorwf seq7, f movlw 0x7F andwf seq7, f goto loop ; endless loop |
The interrupt service routine (ISR) first saves the current state (STATUS and WREG registers) and then uses seq7 to setup the LED brightness. Note that 0 < seq7 < 128, so most of the time seq7 is quite a small number. If we use this number directly to setup the PWM duty cycle, the LEDs will be to dim most of the time. To improve the situation we first multiply this number by 3 (by adding it with itself) and then complement. The steps of this approach were figured out experimentally.
ORG 4 ; ISR movwf w_save ; save W reg swapf STATUS, w ; save stat reg without movwf stat_save ; changing flags movf seq7, w ; compute the complement of addwf seq7, w ; seq7*3 to noticeably dim the LEDs addwf seq7, w xorlw 0xFF movwf CCPR1L ; setup PWM duty cycle bcf PIR1, TMR1IF ; clear TMR1 interrupt flag swapf stat_save, w ; get orig flags in STATUS movwf STATUS swapf w_save, f ; restore the original W swapf w_save, w retfie |
Last modified:Mon, Jan 23, 2023.