;=============================================================================== ; Title: Electronic Candle ; ; Author: Rob Jansen, Copyright (c) 2016..2016, all rights reserved. ; ; Revision: ; 2016-12-10 : Inital version copied from 3 independent LEDs. ; 2016-12-13 : Changed to increment steps of 3 instead of 5 so timer 2 ; frequency increased. Clock also changed from 4 MHz to 8 MHz. ; Upgraded to control 5 LEDs ; 2016-12-17 : Reduced delaytime in main loop to improve candle effect. ; Compiler: jalv24q6 ; ; Description: Simulate a candle for 5 LEDs independently. ; ;=============================================================================== include 12f615 ; target PICmicro ; Not use external crystal but use internal clock and internal reset pragma target OSC INTOSC_NOCLKOUT ; Internal Clock pragma target PWRTE Enabled ; Power up timer pragma target MCLR Internal ; Reset internal pragma target WDT Disabled ; No watchdog Pragma target IOSCFS F8MHZ ; Set internal oscillator to 8 MHz pragma target clock 8_000_000 ; oscillator frequency 8 MHz enable_digital_io() ; make all pins digital I/O ; Definition of the pins to control the LEDs. Alias Led_1 Is pin_GP0 ; Pin 7. pin_GP0_direction = Output Alias Led_2 Is pin_GP1 ; Pin 6. pin_GP1_direction = Output Alias Led_3 Is pin_GP2 ; Pin 5. pin_GP2_direction = Output Alias Led_4 Is pin_GP4 ; Pin 3. pin_GP4_direction = Output Alias Led_5 Is pin_GP5 ; Pin 2. pin_GP5_direction = Output ; ================== Constant and variable declarations ======================= ; 16 bit shift register used to generate a random number. It generates all ; numbers between 0x0001 and 0xFFFF and has to be started with a seed. ; See http://en.wikipedia.org/wiki/Linear_feedback_shift_register Var Word Random_Shift_Word = 0xACE1 ; Intialize with Seed for Random Generator ; Specify the values for the minimum and maximum brightness of the LED ; and the value to increase or decrease it. The increase and decrease values ; are different as to create a better candle effect. Const Byte MIN_BRIGHT = 30 Const Byte MAX_BRIGHT = 250 Const Byte UP_BRIGHT = 3 -- Increase to MAX_BRIGHT must be < 255 Const Byte DOWN_BRIGHT = 25 -- Decrease from MIN_BRIGHT must be > 0 Const Bit LED_ON = FALSE ; Led is common anode so control is active low. Const Bit LED_OFF = TRUE ; Initial value for the Duty Cycle and so the brightness of the Candle. -- Initial value for the Duty Cycle and so the brightness of the Candle. Var Byte LED_1_Timer Var Byte LED_2_Timer Var Byte LED_3_Timer Var Byte LED_4_Timer Var Byte LED_5_Timer Var Byte Duty_Cycle_1 = 128 Var Byte Duty_Cycle_2 = 128 Var Byte Duty_Cycle_3 = 128 Var Byte Duty_Cycle_4 = 128 Var Byte Duty_Cycle_5 = 128 ; ========================= Functions and Procedures ========================== Function Give_Random_Word Return Word is ; Generate a new random number using a Linear Feedback Shift Register (LFSR). Var Word Value ; Use Fibonacci LSFR x16 + x14 + x13 + x11 + 1 Value = (Random_Shift_Word ^ (Random_Shift_Word >> 2) ^ (Random_Shift_Word >> 3) ^ (Random_Shift_Word >> 5)) & 0x0001 Random_Shift_Word = (Random_Shift_Word >> 1) | (Value << 15) Return Random_Shift_Word End Function ; Give_Random_Word Procedure PWM_Frequency Is Pragma interrupt ; This interrupt procedure controls the PWM frequency using Timer 0. If the ; timer 0 flag is set the we start a new PWM cycle and we turn all LEDs on. If INTCON_TMR0IF Then INTCON_TMR0IF = FALSE LED_1_Timer = Duty_Cycle_1 LED_2_Timer = Duty_Cycle_2 LED_3_Timer = Duty_Cycle_3 LED_4_Timer = Duty_Cycle_4 LED_5_Timer = Duty_Cycle_5 LED_1 = LED_ON LED_2 = LED_ON LED_3 = LED_ON LED_4 = LED_ON LED_5 = LED_ON End If ; INTCON_TMR0IF End Procedure ; Timer0_Interrupt Procedure PWM_Duty_Cycle Is Pragma interrupt ; Interrupt procedure control the PWM duty cycle using timer 2. if the ; timer 2 flag is set then we check the remaining duty cycle. If it is zero ; then the LEDs have to be switched OFF, otherwise we decrement the duty cycle. ; This routine is called every 96 us and takes about 25 us. If PIR1_TMR2IF Then PIR1_TMR2IF = FALSE ; If the timer of the LED is 0 then the PWM time has passed. If (LED_1_Timer >= UP_BRIGHT) Then LED_1_Timer = LED_1_Timer - UP_BRIGHT Else LED_1 = LED_OFF End If ; LED_1_Timer If (LED_2_Timer >= UP_BRIGHT) Then LED_2_Timer = LED_2_Timer - UP_BRIGHT Else LED_2 = LED_OFF End If ; LED_2_Timer If (LED_3_Timer >= UP_BRIGHT) Then LED_3_Timer = LED_3_Timer - UP_BRIGHT Else LED_3 = LED_OFF End If ; LED_3_Timer If (LED_4_Timer >= UP_BRIGHT) Then LED_4_Timer = LED_4_Timer - UP_BRIGHT Else LED_4 = LED_OFF End If ; LED_4_Timer If (LED_5_Timer >= UP_BRIGHT) Then LED_5_Timer = LED_5_Timer - UP_BRIGHT Else LED_5 = LED_OFF End If ; LED_5_Timer End If ; PIR1_2IF End Procedure ; PWM_Duty_Cycle ; ========================= Main program starts here ========================== ; Enable weak pull up for all ports since some inputs are not connected WPU = 0b0011_1111 ; Weak pull-up enabled for all IO pins OPTION_REG_NGPPU = FALSE ; Enable Weak Pull-Up ; Initialize Timer 0. This timer is used to create the PWM frequency. ; With the prescaler at 32 and letting the timer overflow we get a frequency ; of 2.000.000 / 256 / 64 = 122 Hz or 8,2 ms OPTION_REG_T0CS = FALSE ; Timer counts on instruction cycle. OPTION_REG_PSA = FALSE ; Prescaler assigned to Timer 0 OPTION_REG_PS = 0b101 ; Prescaler at 64 INTCON_TMR0IF = FALSE ; Clear Timer 0 interrupt flag. INTCON_TMR0IE = TRUE ; Enable Timer 0 interrupt. ; Timer 2 is used to generate the PWM pulse with by decrementing counters that ; determines the duty cycle. Since we increment in minimun steps of 3 ; (UP_BRIGHT) so we have 255 / 3 = 85 positions so we have to run at 85 times ; the speed of Timer 0 or at a frequency of 122 Hz * 85 = 10.370 Hz = 96,4 us ; Register PR2 holds the Timer Period using the following formula: ; Period = (PR2 + 1) * 4 * Tosc * Timer2 prescale value ; where Tosc = 1/Fosc and Fosc = 8.000.000 Hz. ; With a PR2 reload value of 191 and no prescaler ; Period = (191 + 1) * 4 * 1/8.000.000 * 1 = 96 us or 10.416 HZ T2CON_TMR2ON = FALSE ; Timer 2 off T2CON_TOUTPS = 0b0000 ; Postscaler is 1:1 T2CON_T2CKPS = 0b00 ; Prescaler divide by 1 PR2 = 191 ; Reload value. PIE1_TMR2IE = TRUE ; Enable Timer 2 interrupt PIR1_TMR2IF = FALSE ; Clear Timer 2 interrupt flag. T2CON_TMR2ON = TRUE ; Start Timer 2 INTCON_PEIE = TRUE ; Enable peripheral interrupt. INTCON_GIE = TRUE ; Enable global interrupt. Forever Loop ; This loop detemines the candle effect for the 3 LEDs. ; LED 1. If (Give_Random_Word > 0x1fff) Then If (Duty_Cycle_1 < MAX_BRIGHT) Then Duty_Cycle_1 = Duty_Cycle_1 + UP_BRIGHT End If Else If (Duty_Cycle_1 > MIN_BRIGHT) Then Duty_Cycle_1 = Duty_Cycle_1 - DOWN_BRIGHT End If End If ; Give_Random_Word ; LED 2. If (Give_Random_Word > 0x1fff) Then If (Duty_Cycle_2 < MAX_BRIGHT) Then Duty_Cycle_2 = Duty_Cycle_2 + UP_BRIGHT End If Else If (Duty_Cycle_2 > MIN_BRIGHT) Then Duty_Cycle_2 = Duty_Cycle_2 - DOWN_BRIGHT End If End If ; Give_Random_Word ; LED 3. If (Give_Random_Word > 0x1FFF) Then If (Duty_Cycle_3 < MAX_BRIGHT) Then Duty_Cycle_3 = Duty_Cycle_3 + UP_BRIGHT End If Else If (Duty_Cycle_3 > MIN_BRIGHT) Then Duty_Cycle_3 = Duty_Cycle_3 - DOWN_BRIGHT End If End If ; Give_Random_Word ; LED 4. If (Give_Random_Word > 0x1FFF) Then If (Duty_Cycle_4 < MAX_BRIGHT) Then Duty_Cycle_4 = Duty_Cycle_4 + UP_BRIGHT End If Else If (Duty_Cycle_4 > MIN_BRIGHT) Then Duty_Cycle_4 = Duty_Cycle_4 - DOWN_BRIGHT End If End If ; Give_Random_Word ; LED 5. If (Give_Random_Word > 0x1FFF) Then If (Duty_Cycle_5 < MAX_BRIGHT) Then Duty_Cycle_5 = Duty_Cycle_5 + UP_BRIGHT End If Else If (Duty_Cycle_5 > MIN_BRIGHT) Then Duty_Cycle_5 = Duty_Cycle_5 - DOWN_BRIGHT End If End If ; Give_Random_Word ; Wait some time to get a candle effect. _usec_delay(10_000) End Loop ; Forever