'****************************************************************
'*  Name    : UNTITLED.BAS                                      *
'*  Author  : Bogdan Pitic                                      *
'*  Notice  : Copyright (c) 2009 [select VIEW...EDITOR OPTIONS] *
'*          : All Rights Reserved                               *
'*  Date    : 2/14/2009                                         *
'*  Version : 1.0                                               *
'*  Notes   :                                                   *
'*          :                                                   *
'****************************************************************
'pic16F913
'----------------------------------------------------------------------------- 
@ __config _LP_OSC & _WDT_OFF & _PWRTE_OFF & _MCLRE_ON & _BOD_OFF & _IESO_OFF  & _FCMEN_OFF & _CP_OFF & _CPD_OFF & _DEBUG_OFF  ;commented the line in the picbasic file
'-----------------------------------------------------------------------------                              
'CLEAR THE EEPROM DATA FOR THE "nc"_variable=(cigarettes index)
 
DATA @00, 0, 0, 0, 0, 0, 0, 255, 255 
'----------------------------------------------------------------------------- 
'WRITE TO EEPROM the startup timer1 values DIVIDED by 4 to fit into the 14 bytes (Timer1_startup_value/4)

DATA @48,WORD 12963,WORD 13116,WORD 13270,WORD 13423,WORD 13577,WORD 13731,WORD 13884,WORD 14038
DATA WORD 12036,WORD 12229,WORD 12422,WORD 12615,WORD 12808,WORD 13001,WORD 13194,WORD 13387
DATA WORD 11105,WORD 11337,WORD 11569,WORD 11802,WORD 12034,WORD 12267,WORD 12499,WORD 12732
DATA WORD 9994,WORD 10266,WORD 10538,WORD 10809,WORD 11081,WORD 11353,WORD 11625,WORD 11897
DATA WORD 9067,WORD 9379,WORD 9690,WORD 10001,WORD 10312,WORD 10624,WORD 10935,WORD 11246
DATA WORD 8141,WORD 8492,WORD 8842,WORD 9193,WORD 9544,WORD 9894,WORD 10245,WORD 10596
DATA WORD 7214,WORD 7605,WORD 7995,WORD 8385,WORD 8775,WORD 9165,WORD 9555,WORD 9945
DATA WORD 6288,WORD 6717,WORD 7147,WORD 7577,WORD 8006,WORD 8436,WORD 8865,WORD 9295
DATA WORD 5361,WORD 5830,WORD 6299,WORD 6768,WORD 7237,WORD 7706,WORD 8175,WORD 8644
DATA WORD 4435,WORD 4943,WORD 5452,WORD 5960,WORD 6469,WORD 6977,WORD 7485,WORD 7994
DATA WORD 3509,WORD 4056,WORD 4604,WORD 5152,WORD 5700,WORD 6248,WORD 6796,WORD 7343
DATA WORD 2582,WORD 3169,WORD 3757,WORD 4344,WORD 4931,WORD 5518,WORD 6106,WORD 6693
DATA WORD 1656,WORD 2282,WORD 2909,WORD 3536,WORD 4162,WORD 4789,WORD 5416,WORD 6042
'-----------------------------------------------------------------------------
INCLUDE "DT_INTS-14.bas"     ' Base Interrupt System
INCLUDE "ReEnterPBP.bas"     ' Include if using PBP interrupts
'-----------------------------------------------------------------------------
CLEAR                       'Set all RAM registers to zero
'-----------------------------------------------------------------------------
ASM
INT_LIST  macro    ; IntSource,          Label,       Type, ResetFlag?
        INT_Handler    INT_INT,  _battery_Cover_open,   PBP,  yes
        INT_Handler   TMR1_INT,  _timer1_overflow,   PBP,  yes
    endm
    INT_CREATE               ; Creates the interrupt processor
ENDASM
'-----------------------------------------------------------------------------
'MCU CLOCK SOURCE

OSCCON.0 = 0                'clock source define by FOSC<2,0> = _LP_OSC(SCS bit)
'-----------------------------------------------------------------------------
'PORTS CONFIGURATION

CMCON0.2 = 1                'Comparators Off for PORTA.0 to 3 (CM bits)=save power
CMCON0.1 = 1                'Comparators Off for PORTA.0 to 3 (CM bits)=save power
CMCON0.0 = 1                'Comparators Off for PORTA.0 to 3 (CM bits)=save power

ANSEL = 0                   'Analog to Digital converter, needs to be disabled prior to use I/O


VRCON.7 = 0                 'CVref circuit powered down (VREN bit)=save power

TRISA = %10000000	        'sets the PORTA.0 to 6 to outputs and PORTA.7 (T1OSI=XTAL) to inputs
TRISB = %00000001           'sets the PORTB.1 to 7 to outputs and PORTB.0 (=external interrupts) to input
TRISC = %00000111	        'sets the PORTC.3 to 7 to outputs and PORTC.2 to 0 (=1/3 BIAS for LCD) to inputs

PORTA=%00000000
PORTB=%00000000
PORTC=%00000000

OPTION_REG.6 = 1             'external interrupt on rising edge of RB0 pin (INTEDG bit)
'-----------------------------------------------------------------------------
'Timer1 CONFIGURATION (part of)

T1CON.1 = 1                 'clock source for Timer1 is external (TMR1CS bit)

T1CON.2 = 1                 'do not sync with the internal clock (=asynchronous) (T1SYNC bit)

T1CON.3 = 1                 'enable LP oscillator for Timer1 clock (when INTOSCIO is active) (T1OSCEN bit)
'-----------------------------------------------------------------------------
'LCD CONFIGURATION

LCDCON.4 = 1                'enable bias voltage pins (VLCDEN bit)

LCDCON.3 = 0                'clock source crystall (T1OSC/32) (CS bits)
LCDCON.2 = 1                'clock source crystall (T1OSC/32) (CS bits)

LCDCON.0 = 1                '1/4 multiplex (all COM0, COM1, COM2 and COM3 are used) (LMUX bits)
LCDCON.1 = 1                '1/4 multiplex (all COM0, COM1, COM2 and COM3 are used) (LMUX bits)

LCDPS.7 = 0                 'waveform type-A (WFT bit)

LCDPS.6 = 0                 'bias mode (when LMUX=11) (BIASMD bit)
 
LCDPS.3 = 0                 '1:8 prescaler {by Microchip AN1070/pag3: LCD_Refresh_rate=Clock_source/(1*4*([LP3:LP0]+1))=[T1OSC/32]/(1*4*([%0111]+1))=[32.768kHz/32]/(1*4*(7+1))=32Hz} ...(LP bits)
LCDPS.2 = 1                 '1:8 prescaler {by Microchip AN1070/pag3: LCD_Refresh_rate=Clock_source/(1*4*([LP3:LP0]+1))=[T1OSC/32]/(1*4*([%0111]+1))=[32.768kHz/32]/(1*4*(7+1))=32Hz} ...(LP bits)
LCDPS.1 = 1                 '1:8 prescaler {by Microchip AN1070/pag3: LCD_Refresh_rate=Clock_source/(1*4*([LP3:LP0]+1))=[T1OSC/32]/(1*4*([%0111]+1))=[32.768kHz/32]/(1*4*(7+1))=32Hz} ...(LP bits)
LCDPS.0 = 1                 '1:8 prescaler {by Microchip AN1070/pag3: LCD_Refresh_rate=Clock_source/(1*4*([LP3:LP0]+1))=[T1OSC/32]/(1*4*([%0111]+1))=[32.768kHz/32]/(1*4*(7+1))=32Hz} ...(LP bits)

LCDSE0.1 = 1                'enable SEG1 (SE bits)
LCDSE0.2 = 1                'enable SEG2 (SE bits)
LCDSE0.3 = 1                'enable SEG3 (SE bits)
LCDSE0.4 = 1                'enable SEG4 (SE bits)
LCDSE0.5 = 1                'enable SEG5 (SE bits)
LCDSE1.0 = 1                'enable SEG8 (SE bits)
LCDSE1.5 = 1                'enable SEG13 (SE bits)
LCDSE1.6 = 1                'enable SEG14 (SE bits)

'CLEAR the LCD screen (because the RESET didn't clear it)...see AN1070
 
LCDDATA10.5 = 0             'CLEAR the "b" (as in Battery low)
LCDDATA4.5 = 0
LCDDATA7.5 = 0
LCDDATA4.6 = 0
LCDDATA1.5 = 0

LCDDATA9.2 = 0              'CLEAR hundreds seg A
LCDDATA6.2 = 0              'CLEAR hundreds seg B
LCDDATA3.2 = 0              'CLEAR hundreds seg C
LCDDATA0.3 = 0              'CLEAR hundreds seg D
LCDDATA3.3 = 0              'CLEAR hundreds seg E
LCDDATA9.3 = 0              'CLEAR hundreds seg F
LCDDATA6.3 = 0              'CLEAR hundreds seg G
  
LCDDATA9.4 = 0              'CLEAR tens seg A
LCDDATA6.4 = 0              'CLEAR tens seg B
LCDDATA3.4 = 0              'CLEAR tens seg C
LCDDATA0.1 = 0              'CLEAR tens seg D
LCDDATA3.1 = 0              'CLEAR tens seg E
LCDDATA9.1 = 0              'CLEAR tens seg F
LCDDATA6.1 = 0              'CLEAR tens seg G

LCDDATA10.0 = 0             'CLEAR ones seg A
LCDDATA7.0 = 0              'CLEAR ones seg B
LCDDATA4.0 = 0              'CLEAR ones seg C
LCDDATA0.5 = 0              'CLEAR ones seg D
LCDDATA3.5 = 0              'CLEAR ones seg E
LCDDATA9.5 = 0              'CLEAR ones seg F
LCDDATA6.5 = 0              'CLEAR ones seg G

LCDCON.6 = 0                'enable the LCD in SLEEP mode (SLPEN bit)
'PIR2.4 = 0                  '(why ???) clear LCD Interrupt Flag (LCDIF bit)
'-----------------------------------------------------------------------------
'VARIABILES

wsave VAR BYTE $70 SYSTEM   'to fix "DT_INTS-14.bas" compiling errors

adr4tmr1LO VAR BYTE         'RAM address for startup timer1_LOW _BYTE value
adr4tmr1HI VAR BYTE         'RAM address for startup timer1_HIGH _BYTE value

T1 VAR WORD                 'temporary variable for TMR1 startup value

nc0 VAR WORD				'number of cigarettes (index) at address 0+1
nc2 VAR WORD				'number of cigarettes (index) at address 2+3
nc4 VAR WORD				'number of cigarettes (index) at address 4+5
nc VAR WORD                 'number of cigarettes (index) after checksum

LCD VAR WORD                'number on LCD
hundreds VAR BYTE           'temporary LCD variable
tens VAR BYTE               'temporary LCD variable
ones VAR BYTE               'temporary LCD variable

VbatTEMP VAR WORD           'temporary battery voltage variable for each ADC reading cicle
Vbat VAR WORD               'battery voltage after averaging the VbatTEmp
readADCloop VAR BYTE        'read ADC loop temporary variable
'-----------------------------------------------------------------------------
LCDCON.7 = 1                                  'enable the LCD module (LCDEN bit)
'=============================================================================
@   INT_ENABLE   INT_INT                      ; enable external (INT) interrupts
'=============================================================================
'CHECK THE BATTERY VOLTAGE

TRISA.0 = 1                                   'set PORTA.0 to input
ANSEL.0 = 1                                   'set PORTA.0 to analog

ADCON0.7 = 1                                  'A/D result have to be right justified for 10 bits (2^10=1024)resolution (ADFM bit)

ADCON0.6 = 0                                  'set voltage_reference=Vss (VCFG1 bit)
ADCON0.5 = 0                                  'set voltage_reference=Vdd (VCFG0 bit)

ADCON0.4 = 0                                  'set analog_channel=AN0=PORTA.0 (CHS bits)
ADCON0.3 = 0
ADCON0.2 = 0

ADCON1.5 = 1                                  'set A/D conversion clock source to F_rc=dedicated internal osc (ADCS bits)
ADCON1.4 = 1

ADCON0.0 = 1                                  'enable A/D module (ADON bit)

PAUSE 4                                       'wait 0.5sec (...acquisition time)  (32768*500/4000000)

'start_adc_conversion:
FOR readADCloop=8 TO 1 STEP -1
   ADCON0.1=1                                'start A/D conversion  (GO/DONE bit)
    WHILE ADCON0.1=1                          'wait for it to complete
    WEND
                                                     
    VbatTEMP.HighByte = ADRESH                'read high-byte of result
    VbatTEMP.LowByte = ADRESL                 'read low-byte of result

    Vbat=Vbat+VbatTEMP
    PAUSEUS 13                                'wait 10msec (10000*32768/4000000)
NEXT readADCloop

Vbat=Vbat/8                                   'take the average

SELECT CASE Vbat
    CASE IS < 900                             'attention: battery low
        LCDDATA10.5 = 1                       'write to lcd "b" (as in Battery low)
        LCDDATA4.5 = 1
        LCDDATA7.5 = 1
        LCDDATA4.6 = 1
        LCDDATA1.5 = 1
    CASE IS < 500
        STOP                                   'battery is too low to drive the solenoid: STOP THE PROGRAM                
END SELECT    

ADCON0 = 0                                     'disable A/D module
'TRISA.0 = 0                                   'set PORTA.0 to output
'ANSEL.0 = 0                                   'set PORTA.0 to digital

'-----------------------------------------------------------------------------
'...when the program start with the bat cover already open the ext_int will not notice the cover is open, so:
IF PORTB.0=1 THEN
        LCDDATA10.5 = 0                       'clear the "b" (as in Battery low)
        LCDDATA4.6 = 0
        LCDDATA7.5 = 1                        'write to lcd "c" (as in battery Cover open)
        LCDDATA4.5 = 1
        LCDDATA1.5 = 1
             ELSE
        GOTO lock_the_box
ENDIF
 
WHILE PORTB.0=1                                 'wait to close the battery cover
WEND

LCDDATA7.5 = 0                                  'clear from lcd "c" (as in battery Cover open)
LCDDATA4.5 = 0
LCDDATA1.5 = 0
'-----------------------------------------------------------------------------
'LOCK THE BOX

lock_the_box:

PORTC.3=1                     'armature out (lock)
PAUSEUS 164                   'wait 20msec (20000*32768/4000000)
PORTC.3=0
'-----------------------------------------------------------------------------
'GET THE CIGARETTE INDEX NUMBER

Interval_Tigari:

READ 0, nc0.LowByte             'read the last cigarette index from EEPROM_location 1
READ 1, nc0.HighByte            'read the last cigarette index from EEPROM_location 1
READ 2, nc2.LowByte             'read the last cigarette index from EEPROM_location 2
READ 3, nc2.HighByte            'read the last cigarette index from EEPROM_location 2
READ 4, nc4.LowByte             'read the last cigarette index from EEPROM_location 3
READ 5, nc4.HighByte            'read the last cigarette index from EEPROM_location 3

'IF power down at writing between (nc0.LowByte and nc0.HighByte) or (nc0 and nc2)
IF nc4-nc2=0 THEN 
    IF nc2-nc0<>0 THEN
        nc=nc2+1
        GOTO next_cigarette
    ENDIF
ENDIF

'IF power down at writing between (nc2.LowByte and nc2.HighByte)
IF nc4-nc2<>0 THEN
    IF nc2-nc0<>0 THEN
        nc=nc4+1
        GOTO next_cigarette
    ENDIF
ENDIF

'IF power down (or not) at writing anywere else
nc=nc0
'-----------------------------------------------------------------------------
next_cigarette:

IF nc = 16383 THEN             'EEPROM is just 14 bytes (2^8= 16383)
    GOTO get_adr4tmr1
ENDIF
nc=nc+1                        'next cigarette
nc=255
'-----------------------------------------------------------------------------
'GET START RAM ADDRESS RANGE FOR TIMER1 STARTUP VALUE BY CIGARETTE INDEX

get_adr4tmr1:

SELECT CASE nc
    CASE IS < 141       '1st week
        adr4tmr1LO=48
    CASE IS < 255       '2nd week
        adr4tmr1LO=64
    CASE IS < 349       '3rd week
        adr4tmr1LO=80
    CASE IS < 430       '4th week
        adr4tmr1LO=96
    CASE IS < 500       '5th week
        adr4tmr1LO=112
    CASE IS < 563       '6th week
        adr4tmr1LO=128
    CASE IS < 619       '7th week
        adr4tmr1LO=144
    CASE IS < 670       '8th week
        adr4tmr1LO=160
    CASE IS < 717       '9th week
        adr4tmr1LO=176
    CASE IS < 760       '10th week
        adr4tmr1LO=192
    CASE IS < 800       '11th week
        adr4tmr1LO=208
    CASE IS < 837       '12th week
        adr4tmr1LO=224
    CASE IS >=837       '13th week and more                
        adr4tmr1LO=240
END SELECT
'-----------------------------------------------------------------------------
'COUNTDOWN LCD
 
countdown:

FOR hundreds = 9 TO 0 STEP -1
    LOOKUP hundreds,[1,0,1,1,0,1,1,1,1,1],LCDDATA9.2        'hundreds seg A (0 to 9)
    LOOKUP hundreds,[1,1,1,1,1,0,0,1,1,1],LCDDATA6.2        'hundreds seg B (0 to 9)
    LOOKUP hundreds,[1,1,0,1,1,1,1,1,1,1],LCDDATA3.2        'hundreds seg C (0 to 9)
    LOOKUP hundreds,[1,0,1,1,0,1,1,0,1,1],LCDDATA0.3        'hundreds seg D (0 to 9)
    LOOKUP hundreds,[1,0,1,0,0,0,1,0,1,0],LCDDATA3.3        'hundreds seg E (0 to 9)    
    LOOKUP hundreds,[1,0,0,0,1,1,1,0,1,1],LCDDATA9.3        'hundreds seg F (0 to 9)
    LOOKUP hundreds,[0,0,1,1,1,1,1,0,1,1],LCDDATA6.3        'hundreds seg G (0 to 9)
    FOR tens = 9 TO 0 STEP -1
        LOOKUP tens,[1,0,1,1,0,1,1,1,1,1],LCDDATA9.4        'tens seg A (0 to 9)
        LOOKUP tens,[1,1,1,1,1,0,0,1,1,1],LCDDATA6.4        'tens seg B (0 to 9)
        LOOKUP tens,[1,1,0,1,1,1,1,1,1,1],LCDDATA3.4        'tens seg C (0 to 9)
        LOOKUP tens,[1,0,1,1,0,1,1,0,1,1],LCDDATA0.1        'tens seg D (0 to 9)
        LOOKUP tens,[1,0,1,0,0,0,1,0,1,0],LCDDATA3.1        'tens seg E (0 to 9)        
        LOOKUP tens,[1,0,0,0,1,1,1,0,1,1],LCDDATA9.1        'tens seg F (0 to 9)
        LOOKUP tens,[0,0,1,1,1,1,1,0,1,1],LCDDATA6.1        'tens seg G (0 to 9)   
        FOR ones = 9 TO 0 STEP -1
            LOOKUP ones,[1,0,1,1,0,1,1,1,1,1],LCDDATA10.0   'ones seg A (0 to 9)
            LOOKUP ones,[1,1,1,1,1,0,0,1,1,1],LCDDATA7.0    'ones seg B (0 to 9)
            LOOKUP ones,[1,1,0,1,1,1,1,1,1,1],LCDDATA4.0    'ones seg C (0 to 9)
            LOOKUP ones,[1,0,1,1,0,1,1,0,1,1],LCDDATA0.5    'ones seg D (0 to 9)
            LOOKUP ones,[1,0,1,0,0,0,1,0,1,0],LCDDATA3.5    'ones seg E (0 to 9)            
            LOOKUP ones,[1,0,0,0,1,1,1,0,1,1],LCDDATA9.5    'ones seg F (0 to 9)
            LOOKUP ones,[0,0,1,1,1,1,1,0,1,1],LCDDATA6.5    'ones seg G (0 to 9)
                LCD=hundreds*100+tens*10+ones
'-----------------------------------------------------------------------------
'GET START RAM ADDRESS FOR TIMER1 STARTUP VALUE BY LCD VALUE

                SELECT CASE LCD                                
                    CASE IS = 125
                    adr4tmr1LO = adr4tmr1LO+2
                    CASE IS = 250
                    adr4tmr1LO = adr4tmr1LO+2
                    CASE IS = 375
                    adr4tmr1LO = adr4tmr1LO+2
                    CASE IS = 500
                    adr4tmr1LO = adr4tmr1LO+2
                    CASE IS = 625
                    adr4tmr1LO = adr4tmr1LO+2
                    CASE IS = 750
                    adr4tmr1LO = adr4tmr1LO+2
                    CASE IS = 875
                    adr4tmr1LO = adr4tmr1LO+2
                END SELECT              
'-----------------------------------------------------------------------------          
get_timer1_value:

                READ adr4tmr1LO,T1.LowByte  'Timer1_startup_value/4
                adr4tmr1HI=adr4tmr1LO+1                                       
                READ adr4tmr1HI,T1.HighByte
                                   
                T1 = T1*4                   'Timer1 REAL startup value
           
                TMR1H = T1.HighByte
                TMR1L = T1.LowByte
                
                PAUSE 1
                                                                    
                INTCON.6 = 1                'enable all unmasked peripheral interrupts (PEIE bit)

                T1CON.5 = 1                 'Timer1 input clock prescale (1:8) (T1CKPS bits)
                                            'prescaler counter is cleared upon a write to TMR1H or TMR1L
                T1CON.4 = 1                 'Timer1 input clock prescale (1:8) (T1CKPS bits)
                                            'prescaler counter is cleared upon a write to TMR1H or TMR1L

                T1CON.0 = 1                 'enable Timer1 (TMR1ON bit)
@ INT_ENABLE  TMR1_INT
                
@ sleep
@ nop
@ nop              
                                                       
        NEXT ones 
    NEXT tens    
NEXT hundreds
'-----------------------------------------------------------------------------
'UNLOCK THE BOX
 
unlock_the_box:

PORTC.4=1                                     'armature in (unlock)
PAUSEUS 164                                   'wait 20msec (20000*32768/4000000)
PORTC.4=0

'write the same "nc" value at three locations to fix the power down issue at the writing time               
WRITE 1, nc.HighByte                          'write to RAM the "nc" LOW BYTE
WRITE 0, nc.LowByte                           'write to RAM the "nc" HIGH BYTE               
WRITE 3, nc.HighByte                          'write to RAM the "nc" LOW BYTE
WRITE 2, nc.LowByte                           'write to RAM the "nc" HIGH BYTE
WRITE 5, nc.HighByte                          'write to RAM the "nc" LOW BYTE
WRITE 4, nc.LowByte                           'write to RAM the "nc" HIGH BYTE

'write the "Vbat" reading for troubleshooting purposes
WRITE 7, Vbat.HighByte                          'write to RAM the "Vbat" LOW BYTE
WRITE 6, Vbat.LowByte                           'write to RAM the "Vbat" HIGH BYTE

'turn off the LCD
'LCDCON.4 = 0                                 'disable bias voltage pins (VLCDEN bit)
'LCDCON.6 = 1                                 'disable the LCD in SLEEP mode (SLPEN bit)
'LCDCON.7 = 0                                 'disable the LCD module (LCDEN bit)

T1CON.0=0                                     'stops Timer1

'PORTA=%00000000
PORTB=%00000000
PORTC=%00000000

@ sleep                                       ;END of the program: waiting to take out the cigarrete but still be prapared for the battery cover interrupt
@ nop
@ nop

GOTO Interval_Tigari                          'when the interrupt occured at the LCD=000 time after execution of the battery_Cover_open handler the next step will be this line
'-----------------------------------------------------------------------------
 'TIMER1 OVERFLOW
timer1_overflow: 

@ INT_RETURN 
'-----------------------------------------------------------------------------
'INTERRUPT WHEN THE BATTERY COVER IS OPEN

battery_Cover_open:

PORTC.3=1                                     'armature out (lock)
PAUSEUS 164                                   'wait 20msec (20000*32768/4000000)
PORTC.3=0

LCDDATA10.5 = 0                               'clear the "b"
LCDDATA4.6 = 0
LCDDATA7.5 = 1                                'write to lcd "c" (as in battery Cover open)
LCDDATA4.5 = 1
LCDDATA1.5 = 1

WHILE PORTB.0=1                               'wait to close the battery cover
WEND

LCDDATA7.5 = 0                                'clear from lcd "c" (as in battery Cover open)
LCDDATA4.5 = 0
LCDDATA1.5 = 0
    
@ INT_RETURN