CPU "F:\USER0\PROG\CROSS-32 4.0\PIC16XX.TBL" HOF "INHX8M" WDLN 2 ;Word lenght must be 2 TITL "TT Gate timer - V1.00" ; PMB ELECTRONICS ; PAUL BEALING ; www.pmb.co.mz ; paul@pmb.co.nz ; September 2001 ; Copyright PMB 2001 ; PIC16C711 (with ADC) ;****************************************************************************** ; GENERAL INTRODUCTION ; The TTG_1A1 is a 24 hour "minus a bit" timer. ; The whole idea is to pulse an output a fixed period of time before dawn. ; This is achieved by timing 24 hours minus a bit from dawn yesterday. ; This assumes that dawn is about the same each morning. ; It senses the outside light level, and starts timing for 24 hours minus a bit. ; At the end of the period pulses an output relay. ; 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 0 0 0 0 0 0 0 . ; 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 0 1 2 3 4 5 6 . ; M........................................N----------------------------glM . ; ; M = morning dawn ; N = night dusk ; g = gate release pulse ; l = gets light light sensor activates ; To take into account differences in dawn and to ensure that the output pulse ; does occur, a second pulse is generated at dawn. ; The dawn light level is saved even when power is lost, although timing does ; not continue during a period of lost power. ; Applying power to the timer causes a reset. It may then be nessecary to cycle ; through one day-night sequence to let it catch up. ; The dawn setting should be made at dawn to ensure the correct light level is ; recorded. ; OPERATION ; Pressing the button briefly at any time will generate an output pulse. ; Holding the button for 2 seconds will save the current light level, ; (it is dawn NOW). ; There is a 4 minute hysterisis on the light sensor to prevent false triggering. ; Each "detected" dawn restarts the main timer to run for 23:30 hours. ; "The output pulse occurs 34 minutes before the set dawn light-level" ;****************************************************************************** ; N O T E S ;****************************************************************************** ; Version Notes ; ttg_1a1_01 30-09-01 ; Created from tim_2a2_02. ;****************************************************************************** ; FLAGS USED BY PROGRAM ;****************************************************************************** ; FLAGS1 ; 00000000 ; |||||||| ; |||||||\-- I2C dummy write 1 = yes ; ||||||\--- ADC begin/process pass flag 1 = aquire ; |||||\---- button 1 first pass flag 1 = yes ; ||||\----- hysterisis "on" counter running 1 = yes ; |||\------ dusk/dawn done processing flag 1 = dark done ; ||\------- hysterisis "off" counter running 1 = yes ; |\-------- dusk (light level) 1 = dark ; \--------- piezo buzzer 1 = on ; FLAGS2 ; 00000000 = beep control, flags #2 ; |||||||| ; |||||||`- beep gap done ; 1 = yes ; ||||||`-- beep done ; 1 = yes ; |||||`--- beep running ; 1 = yes ; ||||`---- manual/auto beep mode ; 1 = auto ; |||`----- key beep ; 1 = yes ; ||`------ yes beep ; 1 = yes ; |`------- no beep ; 1 = yes ; `-------- alarm beep ; 1 = yes ; FLAGS3 ; 00000000 ; |||||||| ; |||||||\-- config mode timer (1) running 1 = yes ; ||||||\--- config mode timer (1) timed out 1 = yes ; |||||\---- gp timer 2 running 1 = yes ; ||||\----- gp timer 2 timed out 1 = yes ; |||\------ main timer (3) running 1 = yes ; ||\------- main timer (3) timed out 1 = yes ; |\-------- EEPROM write failure 1 = failed ; \--------- 1 = yes ; FLAGS4 ; 00000000 ; |||||||| ; |||||||\-- 1st pass for light level set-up 1 = yes ; ||||||\--- 1 = yes ; |||||\---- 1 = yes ; ||||\----- 1 = yes ; |||\------ 1 = yes ; ||\------- 1 = yes ; |\-------- 1 = yes ; \--------- 1 = yes ; INPUT1 ; 00000000 = INPUT STATUS REGISTER #1 ; |||||||| ; |||||||`- = . 1 = yes ; ||||||`-- = . 1 = yes ; |||||`--- = . 1 = yes ; ||||`---- = . 1 = yes ; |||`----- = . 1 = yes ; ||`------ = . 1 = yes ; |`------- = . 1 = yes ; `-------- = . 1 = yes ; SWSTAT switch status ; 0 = ; 0 = ; 0 = ; 0 = ; 0 = flag, allows off timer to run only once following a switch activation ; 0 = flag, set during window for double press ; 0 = flag, timing on period ; 0 = flag, timing off period ; INPUT2 ; 00000000 = INPUT STATUS REGISTER #2 ; |||||||| ; |||||||`- = . ; ||||||`-- = switch 1 ; |||||`--- = . ; ||||`---- = . ; |||`----- = switch 2 ; ||`------ = . ; |`------- = . ; `-------- = . ; SWACT switch activated (on-off) latched ; SWDBL switch double activation ; SWLONG switch long activation ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** NO EQU 0D YES EQU NOT NO ; ASSEMBLE TIME OPTIONS (normal default) PIC711 SETL YES ; N ; ASSEMBLE FOR 16C711 (WITH ADC) PIC84 SETL NO ; Y ; ASSEMBLE FOR 16C84 (NO ADC) DT_MODE SETL NO ; N ; DEVELOPMENT TEST MODE ;****************************************************************************** ;****************************************************************************** ORG 2007H ; DEVICE CONFIGURATION REGISTER IF PIC711 CONFIG EQU 00000000011111B ENDI IF PIC84 CONFIG EQU 00000000011111B ENDI ;****************************************************************************** ;****************************************************************************** ;* D E C L A R A T I O N S * ;****************************************************************************** ;****************************************************************************** ; REGISTER ASSIGNMENTS (indicates bank number, B = 1 & 2) W EQU 0000H ; F EQU 0001H ; INDF EQU 0000H ; 0 TMR0 EQU 0001H ; 0 TIMER 0 PCL EQU 0002H ; B PROGRAM COUNTER, LOWER 8 BITS STATUS EQU 0003H ; B STATUS FSR EQU 0004H ; B ? PORTA EQU 0005H ; 0 PORT A PORTB EQU 0006H ; 0 PORT B IF PIC711 ADCON0 EQU 0008H ; 0 ADC CONTROL 0 16C711 ADRES EQU 0009H ; B ADC RESULT 16C711 ENDI IF PIC84 ADCON0 EQU ADCONA ; 0 ADC CONTROL 0 16C84 ADRES EQU ADRESA ; B ADC RESULT 16C84 ENDI PCLATH EQU 000AH ; 0 PROGRAM COUNTER, UPPER 5 BITS INTCON EQU 000BH ; 0 INTERRUPT CONTROL OPTION EQU 0081H ; 1 OPTION TRISA EQU 0085H ; 1 DDRA TRISB EQU 0086H ; 1 DDRB PCON EQU 0087H ; 1 ADCON1 EQU 0088H ; 1 ADC CONTROL 1 ;----- STATUS Bits -------------------------------------------------------- IRP EQU 7H ; 0 RP1 EQU 6H ; 0 RP0 EQU 5H ; 0 NOT_TO EQU 4H ; 0 NOT_PD EQU 3H ; 0 Z EQU 2H ; 0 DC EQU 1H ; 0 C EQU 0H ; 0 ;----- ADCON0 Bits -------------------------------------------------------- ADCS1 EQU 7H ; 0 ADCS0 EQU 6H ; 0 CHS1 EQU 4H ; 0 CHS0 EQU 3H ; 0 GO_DONE EQU 2H ; 0 ADIF EQU 1H ; 0 ADON EQU 0H ; 0 PCFG1 EQU 1H ; 1 PCFG0 EQU 0H ; 1 ;----- INTCON Bits -------------------------------------------------------- GIE EQU 7H ; B ADIE EQU 6H ; B TOIE EQU 5H ; B timer overflow interrupt enable INTE EQU 4H ; B port B RB0 interrupt enable RBIE EQU 3H ; B TOIF EQU 2H ; B INTF EQU 1H ; B RBIF EQU 0H ; B ;----- OPTION Bits -------------------------------------------------------- NOT_RBPU EQU 7H ; 1 INTEDG EQU 6H ; 1 T0CS EQU 5H ; 1 T0SE EQU 4H ; 1 PSA EQU 3H ; 1 PS2 EQU 2H ; 1 PS1 EQU 1H ; 1 PS0 EQU 0H ; 1 ;----- PCON Bits ---------------------------------------------------------- POR EQU 1H ; 1 BOR EQU 0H ; 1 ;----- Configuration Bits ------------------------------------------------- _BODEN_ON EQU 3FFFH ; _BODEN_OFF EQU 3FBFH ; _CP_ON EQU 004FH ; _CP_OFF EQU 3FFFH ; _PWRTE_OFF EQU 3FFFH ; _PWRTE_ON EQU 3FF7H ; _WDT_ON EQU 3FFFH ; _WDT_OFF EQU 3FFBH ; _LP_OSC EQU 3FFCH ; _XT_OSC EQU 3FFDH ; _HS_OSC EQU 3FFEH ; _RC_OSC EQU 3FFFH ; ;****************************************************************************** ; RAM VARIABLE ASSIGNMENTS ORG 000CH ; USER RAM ADDRESS FLAGS1 DFS 1 ; 0C ; GP FLAGS REGISTER #1 FLAGS2 DFS 1 ; 0D ; GP FLAGS REGISTER #2 FLAGS3 DFS 1 ; 0E ; GP FLAGS REGISTER #3 FLAGS4 DFS 1 ; 0F ; GP FLAGS REGISTER #4 TIC DFS 1 ; 10 ; Number of 10ms intervals/sec 0-99. SEC DFS 1 ; 11 ; Number of seconds 0-59. MIN DFS 1 ; 12 ; Number of minutes. HRS DFS 1 ; 13 ; Number of hours. SAV_W DFS 1 ; 14 ; PLACE TO SAVE W DURING INT SAV_ST DFS 1 ; 15 ; PLACE TO SAVE STAT DURING INT ADCRES1 DFS 1 ; 16 ; LAST ADC RESULT (light level) ADCRES2 DFS 1 ; 17 ; LAST ADC RESULT (light level) GPTIM1 DFS 1 ; 18 ; general purpose timer CNTR1 DFS 1 ; 19 ; config mode timer CNTR2 DFS 1 ; 1A ; ? CNTR3H DFS 1 ; 1B ; main timer hours CNTR3M DFS 1 ; 1C ; main timer minutes OPDEL1 DFS 1 ; 1D ; short delay counter BEEPC1 DFS 1 ; 1E ; beep generation timer LLHC1H DFS 1 ; 1F ; light level hysterisis counter, HIGH LLHC1L DFS 1 ; 20 ; light level hysterisis counter, LOW CTOUT DFS 1 ; 21 ; config timeout CDCNTR DFS 1 ; 22 ; count-down counter (time-out) SWONT DFS 1 ; 23 ; switch timer SWOFT DFS 1 ; 24 ; switch timer SWTIM DFS 1 ; 25 ; switch timer SWTMP DFS 1 ; 26 ; switch temporary varuable SWTP DFS 1 ; 27 ; switch varuable, this pass SWLP1 DFS 1 ; 28 ; switch varuable, last pass SWLP2 DFS 1 ; 29 ; switch varuable, last pass -1 SWACT1 DFS 1 ; 2A ; activation history -1 SWACT2 DFS 1 ; 2B ; activation history -2 SWSTAT DFS 1 ; 2C ; switch varuable, processing status SWACT DFS 1 ; 2D ; switch varuable, ACTIVATED SWDBL DFS 1 ; 2E ; switch varuable, DOUBLE PRESSED SWLONG DFS 1 ; 2F ; switch varuable, LONG PRESSED INPUT1 DFS 1 ; 30 ; input status register INPUT2 DFS 1 ; 31 ; input status register LEDS1FS DFS 1 ; 32 ; LED slow flash mask LEDS1FF DFS 1 ; 33 ; LED fast flash mask LEDS1A DFS 1 ; 34 ; LED output data LOADCNT DFS 1 ; 35 ; I2C variable RXDAT DFS 1 ; 36 ; I2C receive data byte TXDAT DFS 1 ; 37 ; I2C transmit data byte I2CADD DFS 1 ; 38 ; I2C address I2CWRK DFS 1 ; 39 ; I2C working register BITCNT DFS 1 ; 3A ; I2C bit counter RDBCNT DFS 1 ; 3B ; I2C read byte counter TMP1 DFS 1 ; 3C ; temp value #1 TMP2 DFS 1 ; 3D ; temp value #2 DUSKT DFS 1 ; 3E ; dusk level (temporary) CFGFLG1 DFS 1 ; 3F ; config flags (from EEPROM) DUSK DFS 1 ; 40 ; dusk level (from EEPROM) TOHRS DFS 1 ; 41 ; timeout hours (from EEPROM) TOMINS DFS 1 ; 42 ; timeout hours (from EEPROM) PAUL DFS 1 ; 43 ; debug variable ADCONA DFS 1 ; 44 ; temp for 16F84 (ADC fake) ADRESA DFS 1 ; 45 ; temp for 16F84 (ADC fake) ; end = 4F ;****************************************************************************** ;****************************************************************************** ; CONSTANTS ;****************************************************************************** ;****************************************************************************** EXT_IO EQU 0H ; PORT B output diagnostic output RELAY_1 EQU 1H ; PORT B output main relay LIDVR EQU 2H ; PORT B output light sensor driver LED_GN EQU 3H ; PORT B output LED (green) LED_RD EQU 4H ; PORT B output LED (red) PIEZO EQU 5H ; PORT B output buzzer SW_1 EQU 6H ; PORT B input button 1 SW_2 EQU 7H ; PORT B input button 2 LDR EQU 0H ; PORT A analog light sensor SET_T EQU 1H ; PORT A input set timer pot DAT_I2C EQU 2H ; PORT A input I2C data line CLK_I2C EQU 3H ; PORT A output I2C clock line RTC_I2C EQU 4H ; PORT A output RTC/I2C DDRA EQU 00000111B ; PORT-A data direction default DDRB EQU 11000000B ; PORT-B data direction default ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;* R E S E T A N D I N T E R R U P T V E C T O R S * ;****************************************************************************** ORG 0000H ; RESET VECTOR GOTO START ORG 0004H ; INTERRUPT VECTOR GOTO ISR ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ; P R O G R A M C O D E S T A R T S H E R E ;****************************************************************************** ; INITALISATION CODE START BCF INTCON,GIE ; DISABLE GLOBAL INTERRUPTS BTFSC INTCON,GIE ; ensure global interrupt disabled GOTO START ; not disabled BSF STATUS,RP0 ; select bank 1 MOVLW DDRA ; default data direction MOVWF TRISA ; Port A DDR MOVLW DDRB ; default data direction MOVWF TRISB ; Port B DDR ( varies during operation) MOVLW 00000010B ; PORTA RA0-1 ANALOG, RA2-3 DIG INPUTS MOVWF ADCON1 ; (varies during operation) MOVLW 01000111B ; pullups; int on +ve edge, Prescale=256 on timer MOVWF OPTION BCF STATUS,RP0 ; select bank 0 MOVLW 00000000B ; port A outputs, default state MOVWF PORTA ; write MOVLW 00000000B ; port B outputs, default state MOVWF PORTB ; write BCF INTCON,RBIF ; clear RB int flag BSF INTCON,TOIE ; enable timer overflow interrupt MOVLW 10000001B ; OSC/32 CLK, CH=0, GO, NOINT, AD=ON MOVWF ADCON0 ; reload adc configuration ; 15uS input settling delay BSF ADCON0,GO_DONE ; start conversion ;************ ; CLEAR RAM MOVLW 0CH ; last byte of RAM MOVWF FSR ; indirect addressing register START1 CLRF INDF ; clear byte INCF FSR,F ; next address MOVLW 4FH ; last byte (+1) SUBWF FSR,W ; check BTFSS STATUS,Z ; finished GOTO START1 ; next byte ;************ ; preset variables & config BSF PORTB,LIDVR ; enable light sensor MOVLW 240D ; 240 = 10ms timeout MOVWF TMR0 ; preload timer. BCF PORTA,RTC_I2C ; make low for EEPROM BSF INTCON,GIE ; global interrupt enable ;************ ; startup ; load defaults from EEPROM CALL LOADCFG ; do it now ; test I/O BSF FLAGS2,6 ; long beep BSF LEDS1A,LED_GN ; LED 1 on CALL DEL25S ; 0.25 second delay BSF FLAGS2,4 ; short beep BSF LEDS1A,LED_RD ; LED 2 on BSF FLAGS1,7 ; buzzer = on CALL DEL25S ; 0.25 second delay BSF FLAGS2,4 ; short beep BCF FLAGS1,7 ; buzzer = off BCF LEDS1A,LED_GN ; LED 1 off CALL DEL25S ; 0.25 second delay BSF FLAGS2,4 ; short beep BCF LEDS1A,LED_RD ; LED 2 off ; preset light level ststus MOVF ADRES,W ; read current ADC level MOVWF ADCRES1 ; (light) BSF FLAGS4,0 ; first pass for light level set-up ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ; M A I N P R O G R A M L O O P ;****************************************************************************** CORE CLRWDT ; reset watchdog timer BSF PORTB,LIDVR ; enable light sensor ;------------------------------------------------------ ; main core loop functions ;------------------------------------------------------ ; single press processing ; button 1 CORE10 BTFSS SWACT,SW_1 ; button 1 pressed ? GOTO CORE20 ; no ; BSF FLAGS2,4 ; make key beep ; yes ; done CORE19 BCF SWACT,SW_1 ; clear button activation ;************ ; single button 2 CORE20 BTFSS SWACT,SW_2 ; button 2 pressed ? GOTO CORE30 ; no BSF FLAGS2,4 ; make key beep ; yes ; pulse output BSF PORTB,RELAY_1 ; relay = on MOVLW 04D ; start pulse timer MOVWF CNTR2 ; " BSF FLAGS3,2 ; set running flag BCF FLAGS3,3 ; clear timeout flag BCF FLAGS3,5 ; clear timeout flag GOTO CORE29 ; continue ; done CORE29 BCF SWACT,SW_2 ; clear button activation ;************ ; double button 1 CORE30 BTFSS SWDBL,SW_1 ; button 1 double pressed GOTO CORE40 ; no ; BSF FLAGS2,4 ; make key beep ; yes CORE39 BCF SWDBL,SW_1 ; clear button activation ;************ ; double button 2 CORE40 BTFSS SWDBL,SW_2 ; button 2 double pressed GOTO CORE50 ; no ; BSF FLAGS2,4 ; make key beep ; yes BCF SWDBL,SW_2 ; clear button activation ;************ ; long button 1 CORE50 BTFSS SWLONG,SW_1 ; button 1 long pressed GOTO CORE60 ; no ; BSF FLAGS2,4 ; make key beep ; yes CORE52 BCF SWLONG,SW_1 ; clear button activation ;************ ; long button 2 CORE60 BTFSS SWLONG,SW_2 ; button 2 long pressed GOTO CORE70 ; no BSF FLAGS2,4 ; make key beep ; yes MOVF ADCRES1,W ; read current light level MOVWF DUSK ; save dusk level CALL SAVECFG ; save now ; done CORE69 BCF SWLONG,SW_2 ; clear button activation ;************ CORE70 ;------------------------------------------------------ ; main timer, time-out ;------------------------------------------------------ ; initiate relay pulse TIME BTFSS FLAGS3,5 ; timeout ? GOTO TIME10 ; no, skip ; expired BSF PORTB,RELAY_1 ; relay = on MOVLW 04D ; start pulse timer MOVWF CNTR2 ; " BSF FLAGS3,2 ; set running flag BCF FLAGS3,3 ; clear timeout flag BCF FLAGS3,5 ; clear timeout flag ; not yet TIME10 ;------------------------------------------------------ ; dusk - dawn ;------------------------------------------------------ ; dusk, dawn light control ; reset at dusk ; restart timer for 23:45 hrs at dawn DDLC BTFSS FLAGS1,6 ; light/dark ??? (1 = dark) GOTO DDLC1 ; dark ; dark BTFSS FLAGS1,4 ; already done ? GOTO DDLC30 ; yes BCF FLAGS1,4 ; register done GOTO DDLC20 ; continue ; light DDLC1 BTFSC FLAGS1,4 ; already done ? GOTO DDLC30 ; yes BSF FLAGS1,4 ; register done ; pulse output to be sure DDLC10 BSF PORTB,RELAY_1 ; relay = on MOVLW 04D ; start pulse timer MOVWF CNTR2 ; " BSF FLAGS3,2 ; set running flag BCF FLAGS3,3 ; clear timeout flag BCF FLAGS3,5 ; clear timeout flag ; turn on, start main timer IF DT_MODE MOVLW 01D ; speed up for development testing ELSE MOVLW 23D ; normal time ENDI MOVWF CNTR3H ; " IF DT_MODE MOVLW 05D ; speed up for development testing ELSE MOVLW 30D ; normal time ENDI MOVWF CNTR3M ; " BSF FLAGS3,4 ; set running flag BCF FLAGS3,5 ; clear timeout flag GOTO DDLC30 ; continue ; turn off DDLC20 BCF PORTB,RELAY_1 ; relay = off BCF LEDS1FF,LED_RD ; \ BCF LEDS1FS,LED_RD ; > LED = off BCF LEDS1A,LED_RD ; > BCF PORTB,LED_RD ; / ; finished DDLC30 ;------------------------------------------------------ ; end of core loop ;------------------------------------------------------ CORE99 GOTO CORE ; loop core ;************************END OF MAIN LOOP**************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ; I N T E R R U P T S E R V I C E ;****************************************************************************** ; This Handler routes interrupts to the correct ISR. ;****************************************************************************** ; Push Status and W register to preserve core processing ISR MOVWF SAV_W ; save "W" for return MOVF STATUS,W ; MOVWF SAV_ST ; save "STATUS" for return ; LED indicates light/dark BTFSS FLAGS1,6 ; light or dark ?? GOTO ISR_1 ; light BSF PORTB,LED_RD ; red LED on (dark) GOTO ISR_2 ; continue ISR_1 BSF PORTB,LED_GN ; green LED on (light) ISR_2 BCF STATUS,RP0 ; ensure bank 0 is selected ; determine interrupt source BTFSC INTCON,TOIF ; test flag? CALL ISR_TO ; timer interrupt service routine BTFSC INTCON,INTF ; test flag? CALL ISR_INT ; external interrupt service routine ; diagnostic o/p ISR_99 MOVF FLAGS1,W ; CALL DAT_TX ; diagnostic TX MOVF DUSK,W ; CALL DAT_TX ; diagnostic TX MOVF CNTR3H,W ; CALL DAT_TX ; diagnostic TX MOVF CNTR3M,W ; CALL DAT_TX ; diagnostic TX MOVF ADCRES1,W ; CALL DAT_TX ; diagnostic TX ; first pass complete BCF FLAGS4,0 ; 1st pass complete ; restore Status and W registers MOVF SAV_ST,W MOVWF STATUS ; restore STATUS MOVF SAV_W,W ; restore W RETFIE ; return, enabling global INT ; GP subroutine to disable interrupts INT_OFF BCF INTCON,GIE ; disable global interrupts BTFSC INTCON,GIE ; ensure global interrupt disabled GOTO INT_OFF ; not disabled ; CLRWDT ; reset watchdog timer RETURN ; done ;****************************************************************************** ; EXTERNAL INTERRUPT SERVICE ROUTINE ;****************************************************************************** ISR_INT BCF INTCON,INTF ; clear external interrupt flag BCF INTCON,INTE ; disable external interrupt RETURN ; finished ;****************************************************************************** ; TIMER OVERFLOW INTERRUPT SERVICE ;****************************************************************************** ISR_TO BCF INTCON,TOIF ; clear TO Interrupt Flag ; 10ms=10,000us ; 10,000 /256 = 39.025 MOVLW 217D ; 256-39 = 217, 39counts=10ms IF DT_MODE MOVLW 246D ; speed up for development testing ENDI MOVWF TMR0 ; restore timer value for next interval ;************ ; copy LEDS out LED BTFSC LEDS1A,LED_GN ; test #1 GOTO LED1 ; BCF PORTB,LED_GN ; LED = off GOTO LED2 ; LED1 BSF PORTB,LED_GN ; LED = on LED2 BTFSC LEDS1A,LED_RD ; test #2 GOTO LED3 ; BCF PORTB,LED_RD ; LED = off GOTO LED4 ; LED3 BSF PORTB,LED_RD ; LED = on LED4 ;************************************************************************ ; BEEP GENERATION RTI40 BTFSC FLAGS2,2 ; already running GOTO RTI47 ; " ; start beep BTFSS FLAGS2,6 ; "no" beep GOTO RTI41 ; " BSF PORTB,PIEZO ; start beep MOVLW 40D ; 0.64 seconds MOVWF BEEPC1 ; counter BSF FLAGS2,2 ; flag running GOTO RTI50 ; done ; start beep RTI41 BTFSS FLAGS2,4 ; "key" beep GOTO RTI42 ; " BSF PORTB,PIEZO ; start beep MOVLW 04D ; 1 second MOVWF BEEPC1 ; counter BSF FLAGS2,2 ; flag running GOTO RTI50 ; done ;************ RTI42 BTFSS FLAGS2,5 ; "yes" beep GOTO RTI45 ; " BTFSC FLAGS2,1 ; beep done, make gap GOTO RTI44 ; " ; make beep RTI43 BSF PORTB,PIEZO ; start beep MOVLW 02D ; 1 second MOVWF BEEPC1 ; counter BSF FLAGS2,2 ; flag running & beep done BSF FLAGS2,1 ; " BTFSS FLAGS2,0 ; 1st beep GOTO RTI50 ; " BCF FLAGS2,0 ; BCF FLAGS2,1 ; BCF FLAGS2,5 ; last beep then finished GOTO RTI50 ; done ; time gap RTI44 BTFSC FLAGS2,0 ; gap done, make 2nd beep GOTO RTI43 ; " MOVLW 02D ; 1 second MOVWF BEEPC1 ; counter BSF FLAGS2,0 ; flag running & gap done BSF FLAGS2,2 ; " GOTO RTI50 ; done ; alarm beep RTI45 BTFSS FLAGS2,7 ; "alarm" beep GOTO RTI50 ; " BTFSC FLAGS2,1 ; beep done, make gap GOTO RTI46 ; " ; make beep BSF PORTB,PIEZO ; start beep MOVLW 126D ; 2 seconds MOVWF BEEPC1 ; counter BSF FLAGS2,1 ; flag running & beep done BSF FLAGS2,2 ; " BCF FLAGS2,0 ; clear for next gap GOTO RTI50 ; done ; time gap RTI46 MOVLW 126D ; 2 seconds MOVWF BEEPC1 ; counter BSF FLAGS2,0 ; flag running & gap done BSF FLAGS2,2 ; " BCF FLAGS2,1 ; clear for next beep GOTO RTI50 ; done ; update beep timer RTI47 MOVF BEEPC1,W ; \ BTFSC STATUS,Z ; > test for zero GOTO RTI48 ; / DECFSZ BEEPC1,F ; decrement counter GOTO RTI50 ; not finished RTI48 BCF PORTB,PIEZO ; beep off BCF FLAGS2,4 ; beeps finished BCF FLAGS2,6 ; " BCF FLAGS2,2 ; finished running ; finished RTI50 ;************************************************************************ ; SWITCH INPUT DEBOUNCE & COUNT ; Whenever a switch is accepted as being on, the timer is started and a flag is set ; to indicate timing. ; If the timer reaches ?? seconds, the switch is registered at SWLONG. ; Whenever a switch is released, the timer is started and a flag set to indicate timing. ; The ADC inputs are disabled while switch #1 is sampled. RTI80A MOVF PORTB,W ; read switches XORLW 11000000B ANDLW 11000000B ; mask other bits MOVWF SWTP ; "this pass" MOVF INPUT2,W ; current state, 1's = on MOVWF SWTMP ; save last pass state for activation processing MOVF SWTP,W ; get "this pass" ANDWF SWLP1,W ; last pass ANDWF SWLP2,W ; last pass -1 IORWF INPUT2,W ; add any switches that are now on (1 = debounced on) MOVWF INPUT2 ; & save MOVF SWTP,W ; current pass IORWF SWLP1,W ; last pass IORWF SWLP2,W ; last pass -1 IORLW 00111111B ; prevent masking of other bits ANDWF INPUT2,W ; merge any switches that are now off (0 = debounced off) MOVWF INPUT2 ; & save true debounced switch states IORWF SWLONG,W ; mask switches active as long (prevents activate & long at same time) XORLW 11000000B ; invert, 1's = off ANDLW 11000000B ; mask SWLONG other bits ANDWF SWTMP,W ; 1's = on, only if active on last pass MOVWF SWTMP ; save copy IORWF SWACT,W ; merge "switches have been activated" MOVWF SWACT ; & save ; save new activations to buffer (PIC) MOVF SWTMP,W ; retrieve copy BTFSC STATUS,Z ; test zero GOTO RTI82 ; no activations to save MOVF SWACT1,W ; move back for double- MOVWF SWACT2 ; press detection MOVF SWTMP,W MOVWF SWACT1 ; save for double press ; process double press (PIC) RTI81 BTFSS SWSTAT,5 ; off period not valid GOTO RTI82 ; " MOVF SWACT1,W ; read ANDWF SWACT2,W ; last switch to activate BTFSC STATUS,Z ; test zero GOTO RTI82 ; no double press detected IORWF SWDBL,W ; merge double press MOVWF SWDBL ; & save MOVLW 11000000B ; clear double press & timer flags ANDWF SWSTAT,W ; " MOVWF SWSTAT ; " GOTO RTI89 ; start on period timer (PIC) RTI82 MOVF INPUT2,W ; \ ANDLW 11000000B ; > no switches are on BTFSC STATUS,Z ; > GOTO RTI84 ; / BTFSC SWSTAT,6 ; already timing on period GOTO RTI87 ; " ; BSF FLAGS2,4 ; make key beep CLRF SWONT ; sw on detected, clear timer BSF SWSTAT,6 ; flag timer running BCF SWSTAT,4 ; clear timer running flag GOTO RTI85 ; done RTI84 BCF SWSTAT,6 ; clear timer running flag ; start off period timer (PIC) RTI85 MOVF INPUT2,W ; \ ANDLW 11000000B ; >mask top nibble BTFSS STATUS,Z ; > GOTO RTI87 ; / at least 1 switch active BTFSC SWSTAT,7 ; already timing off period GOTO RTI87 ; " BTFSC SWSTAT,4 ; has already run for this cycle GOTO RTI87 ; " CLRF SWOFT ; start timer for 0.1 sec BSF SWSTAT,7 ; flag timer running BSF SWSTAT,4 ; " BCF SWSTAT,6 ; clear timer running flag BCF SWSTAT,5 ; " ; check off period timer (PIC) ; These 2 time periods affect double press timing. The relationship between ; them must be maintained; or double press will not work. RTI87 BTFSS SWSTAT,7 ; timer not running GOTO RTI88 ; " INCF SWOFT,F ; decrement timer MOVF SWOFT,W ; read timer SUBLW 10D ; 20D ; (?? sec) BTFSS STATUS,Z ; test zero GOTO RTI87A ; not yet BSF SWSTAT,5 ; set double press flag RTI87A MOVF SWOFT,W ; read timer SUBLW 25D ; 50D ; (?? sec) BTFSS STATUS,Z ; test zero GOTO RTI88 ; not yet BCF SWSTAT,7 ; clear double press & timer flags BCF SWSTAT,5 ; " ; check on period timer (PIC) RTI88 BTFSS SWSTAT,6 ; timer running GOTO RTI89 ; no INCF SWONT,F ; increment timer MOVF SWONT,W ; read timer SUBLW 250D ; long press time (2.5 second) BTFSC STATUS,C ; test zero GOTO RTI89 ; not yet MOVF INPUT2,W ; which switch is on IORWF SWLONG,W ; merge MOVWF SWLONG ; & save ANDWF SWACT,W ; 1 = long switch only XORLW 11000000B ; invert MOVWF SWTMP ; temp save to B ANDWF SWACT,W ; off long switch in SWACT MOVWF SWACT ; & save MOVF SWTMP,W ; restore from temp save ANDWF SWDBL,W ; off long switch in SWDBL MOVWF SWDBL ; & save BCF SWSTAT,6 ; clear timer running flag ; update history (PIC) RTI89 MOVF SWLP1,W ; roll pass data back MOVWF SWLP2 ; to pass before MOVF SWTP,W ; roll pass data back MOVWF SWLP1 ; to pass before ; remove dbl presses from SWACT MOVF SWDBL,W ; get double press BTFSC STATUS,Z ; finish if none active GOTO ADC0 ; " XORLW 11000000B ; invert swdbl ANDWF SWACT,W ; merge MOVWF SWACT ; & save ;************************************************************************ ; SAVE ADC RESULT & INITALISE ADC FOR NEXT CONVERSION ; The adc conversion result is used to adjust the value up or down of the ; stored working result. This effectively filters the adc reading, preventing ; extcessive variation due to noise. ; ADCRES1 = LDR (higher number = darker) ADC0 MOVF ADRES,W ; read ADC result SUBWF ADCRES1,0 ; test result (F - W) BTFSC STATUS,Z ; test for zero GOTO ADC2 ; zero = no change, finished BTFSS STATUS,C ; test carry (set = +ve result) GOTO ADC1 ; clear, -ve result, increase IF PIC711 DECF ADCRES1,F ; decrease ENDI GOTO ADC2 ; done ADC1 IF PIC711 INCF ADCRES1,F ; increase ENDI ADC2 MOVLW 10000001B ; OSC/32 CLK, CH=0, GO, NOINT, AD=ON MOVWF ADCON0 ; reload adc configuration MOVLW 50D ; input settling delay time MOVWF GPTIM1 ; counter CALL BDEL1 ; run delay BSF ADCON0,GO_DONE ; start conversion ; THE FOLLOWING SECTION WORKS BUT IS REMED OUT TO MAKE IT THE SAME AS ; ANOTHER APPLICATION. ; THE ADVANTAGE OF THE FOLLOWING CODE IS THAT IT DOES NOT DELAY THE INTERRUPT ; ROUTINE WAITING FOR THE ADC CONVERSION. ;ADC0 BTFSC FLAGS1,7 ; clear = pass #1 ; GOTO ADC3 ; set = pass #2 ; pass #1 ; BTFSC FLAGS1,1 ; acquisition pass ? ; GOTO ADC2A ; yes ; BCF FLAGS1,1 ; reset flag ; MOVF ADRES,W ; read ADC result ; SUBWF ADCRES1,W ; test result (F - W) ; BTFSC STATUS,Z ; test for zero ; GOTO ADC2 ; zero = no change, finished ; BTFSS STATUS,C ; test carry (set = +ve result) ; GOTO ADC1 ; clear, -ve result, increase ; DECF ADCRES1,F ; decrease ; GOTO ADC2 ; done ;ADC1 ; INCF ADCRES1,F ; increase ;ADC2 MOVLW 10001001B ; OSC/32 CLK, CH=1, GO, NOINT, AD=ON ; MOVWF ADCON0 ; reload adc configuration ; BSF FLAGS1,1 ; acquisition start ; GOTO ADC7 ; continue ;ADC2A BSF ADCON0,GO_DONE ; start conversion ; BCF FLAGS1,1 ; finished acquisition ; BSF FLAGS1,7 ; next pass = #2 ; GOTO ADC7 ; continue ; pass #2 ;ADC3 BTFSC FLAGS1,1 ; acquisition pass ? ; GOTO ADC5A ; yes ; BCF FLAGS1,1 ; reset flag ; MOVF ADRES,W ; save ADC result ; SUBWF ADCRES2,W ; test result (F - W) ; BTFSC STATUS,Z ; test for zero ; GOTO ADC5 ; zero = no change, finished ; BTFSS STATUS,C ; test carry (set = +ve result) ; GOTO ADC4 ; -ve result ; DECF ADCRES2,F ; decrease ; GOTO ADC5 ; done ;ADC4 INCF ADCRES2,F ; decrease ;ADC5 MOVLW 10000001B ; OSC/32 CLK, CH=0, GO, NOINT, AD=ON ; MOVWF ADCON0 ; reload adc configuration ; BSF FLAGS1,1 ; acquisition start ; GOTO ADC7 ; continue ;ADC5A BSF ADCON0,GO_DONE ; start conversion ; BCF FLAGS1,7 ; next pass = #1 ; BCF FLAGS1,1 ; finished acquisition ;ADC6 GOTO ADC7 ; continue ; continue ;ADC7 ;************ ; process channel 1 result (light level) ; hysterisis = 4 minutes (5DC0H) ; result = flags1,6 (on = dark) ; Flags1,3 toggles allowing the hysterisis counter to be restarted only once per ; change in lingt level state (dusk/dawn). PC1_1 MOVF DUSK,W ; get threshold SUBWF ADCRES1,W ; compare BTFSS STATUS,C ; . GOTO PC1_20 ; . ; make = off BTFSC FLAGS4,0 ; 1st pass ? GOTO PC1_11 ; yes, bypass hysteresis (immediate) BTFSC FLAGS1,5 ; is "off" counter running ? GOTO PC1_10 ; yes, decrement MOVLW 5DH ; 5DH ; \ reset hysterisis counter MOVWF LLHC1H ; | " MOVLW 0C0H ; C0H ; | " MOVWF LLHC1L ; / " BSF FLAGS1,5 ; flag "off" counter as running BCF FLAGS1,3 ; reset "on" counter GOTO PC1_30 ; done for now PC1_10 DECFSZ LLHC1L,F ; \ decrement hysterisis counter low GOTO PC1_30 ; | not yet DECFSZ LLHC1H,F ; | decrement hysterisis counter high GOTO PC1_30 ; / not yet PC1_11 BCF FLAGS1,6 ; input off GOTO PC1_30 ; continue ; make = on PC1_20 BTFSC FLAGS4,0 ; 1st pass ? GOTO PC1_22 ; yes, bypass hysteresis (immediate) BTFSC FLAGS1,3 ; is "on" counter running ? GOTO PC1_21 ; same way, continue MOVLW 5DH ; 5DH ; \ reset hysterisis counter MOVWF LLHC1H ; | " MOVLW 0C0H ; C0H ; | " MOVWF LLHC1L ; / " BSF FLAGS1,3 ; flag "on" counter as running BCF FLAGS1,5 ; reset "off" counter GOTO PC1_30 ; done for now PC1_21 DECFSZ LLHC1L,F ; \ decrement hysterisis counter low GOTO PC1_30 ; | not yet DECFSZ LLHC1H,F ; | decrement hysterisis counter high GOTO PC1_30 ; / not yet PC1_22 BSF FLAGS1,6 ; input on ; continue PC1_30 ;************************************************************************ ; UPDATE TIMER ; every 10mS RTC MOVF TIC,W ; IF DT_MODE SUBLW 12D ; speed up for development testing ELSE SUBLW 100D ; test for 100 TIC count ENDI BTFSC STATUS,Z ; GOTO RTC1 ; ripple clock INCF TIC,1 ; DECF GPTIM1,F ; general purpose timer GOTO RTC99 ; RTC1 CLRF TIC ; restart TIC counter from zero INCF SEC,F ; +1 seconds MOVF SEC,W ; test SUBLW 60D ; check for SEC rollover BTFSC STATUS,Z ; GOTO RTC2 ; yes GOTO RTC99 ; no RTC2 CLRF SEC ; zero seconds CALL PER_1M ; 1 minute functions INCF MIN,F ; +1 minutes MOVF MIN,W ; test SUBLW 60D ; check for MIN rollover BTFSC STATUS,Z ; GOTO RTC3 ; yes GOTO RTC99 ; no RTC3 CLRF MIN ; zero minutes CALL PER_1H ; 1 hr functions INCF HRS,F ; +1 hours MOVF HRS,W ; test IF DT_MODE SUBLW 01D ; check for HRS rollover (test mode) ELSE SUBLW 24D ; check for HRS rollover (normal) ENDI BTFSS STATUS,Z ; GOTO RTC99 ; no CLRF HRS ; reset hours to 00 CALL PER_24 ; 00:00 (24 hr functions, if any) ;************ ; Check on 250ms and 500ms real-time tasks RTC99 MOVF TIC,W ; read TIC BTFSC STATUS,Z ; test for zero GOTO per_500 ; run 500mS & 250mS tasks MOVF TIC,W ; SUBLW 25D ; BTFSC STATUS,Z ; test for 25 GOTO per_250 ; run 250mS tasks MOVF TIC,W ; SUBLW 50D ; BTFSC STATUS,Z ; test for 50 GOTO per_500 ; run 500mS & 250mS tasks MOVF TIC,W ; SUBLW 75D ; BTFSC STATUS,Z ; test for 75 GOTO per_250 ; run 250mS tasks RETURN ; finished ;------------------------------------------------------ ; 500ms Periodic Processing ;------------------------------------------------------ ; flash leds slowly if flags set per_500 MOVF LEDS1FS,W ; get flash mask in W XORWF PORTB,F ; invert flashing leds ; GP COUNTER 2 P500_30 BTFSS FLAGS3,2 ; timer 2 running ? GOTO P500_40 ; no, skip DECF CNTR2,F ; decrement BTFSS STATUS,Z ; test for Z GOTO P500_40 ; no BCF PORTB,RELAY_1 ; relay off (end pulse) BCF FLAGS3,2 ; clear running flag BSF FLAGS3,3 ; set timeout flag P500_40 ; NOTE: 500ms code should 'fall through' to 250ms code. ;------------------------------------------------------ ; 250ms Periodic Processing ;------------------------------------------------------ ; flash leds fast if flags set PER_250 MOVF LEDS1FF,W ; get flash mask in W XORWF PORTB,F ; invert flashing leds ; configuration timeout BTFSS FLAGS3,0 ; timer 1 running ? GOTO P250_1 ; no, skip DECF CNTR1,F ; decrement BTFSS STATUS,Z ; test for Z GOTO P250_1 ; no BCF FLAGS3,0 ; clear running flag BSF FLAGS3,1 ; set timeout flag ; done P250_1 RETURN ; finished TO interrupt service ;------------------------------------------------------ ; 1 min period ;------------------------------------------------------ ; decrement main timer PER_1M BTFSS FLAGS3,4 ; timer running ? GOTO P1M_10 ; no, skip ; minutes, decrement DECFSZ CNTR3M,F ; decrement minutes GOTO P1M_10 ; still running MOVF CNTR3H,W ; test hours for zero ? BTFSC STATUS,Z ; not zero, skip GOTO P1M_1 ; 00:00 = TIMEOUT MOVLW 60D ; reset minutes MOVWF CNTR3M ; " ; hours MOVF CNTR3H,W ; test hours for zero ? BTFSC STATUS,Z ; not zero, skip GOTO P1M_10 ; zero, don't decrement DECF CNTR3H,F ; decrement hours GOTO P1M_10 ; still running ; timeout P1M_1 BCF FLAGS3,4 ; clear running flag BSF FLAGS3,5 ; set timeout flag ; done P1M_10 RETURN ; finished ;------------------------------------------------------ ; 1 hr period ;------------------------------------------------------ PER_1H P1H_1 RETURN ; finished ;------------------------------------------------------ ; 24 hr period ;------------------------------------------------------ PER_24 P24H_1 RETURN ; finished ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ; G E N E R A L S U B R O U T I N E S ;****************************************************************************** ; add more here ;************************************************************************ ; LOAD/SAVE EEPROM CONFIGURATION ;************************************************************************ ; Load configuration from EEPROM LOADCFG CALL INTOFF ; interrupts disabled MOVLW 00H ; set address to read MOVWF I2CADD ; " MOVLW 01D ; set byte counter to 01 MOVWF RDBCNT ; " MOVLW DUSK ; set address to put data MOVWF FSR ; " BSF FLAGS1,0 ; dummy write, sets address CALL EE_WR ; write eeprom BCF FLAGS1,4 ; clr flag (multiple bytes read) CALL EE_RD ; read byte/s now CALL INTON ; interrupts enabled RETURN ; done ;************ ; Save configuration to EEPROM ; dusk level SAVECFG MOVF DUSK,W ; get data MOVWF TXDAT ; data to save MOVLW 00H ; set address to write MOVWF I2CADD ; " CALL EE_WRV ; write and verify 1 byte ; done RETURN ;************************************************************************ ; INTERRUPT ENABLE/DISABLE ;************************************************************************ INTON BSF INTCON,GIE ; global interrupt enable RETURN ; done ;************ INTOFF BCF INTCON,GIE ; disable global interrupt BTFSC INTCON,GIE ; ensure global interrupt disabled GOTO INTOFF ; not disabled RETURN ; done ;************************************************************************ ; SERIAL EEPROM SUBROUTINES ;************************************************************************ ; WRITE 1 BYTE & VERIFY ; The data byte must already be in "TXDAT". ; The address to write to must already be in "I2CADD". EE_WRV MOVLW 05D ; write attempt count MOVWF LOADCNT ; counter EE_V1 BCF FLAGS1,0 ; ensure normal-write CALL EE_WR ; normal write BSF FLAGS1,0 ; dummy-write to reset the address CALL EE_WR ; dummy-write now BSF FLAGS1,4 ; set flag (read 1 byte) CALL EE_RD ; read data byte MOVF RXDAT,W ; get received data SUBWF TXDAT,W ; compare BTFSC STATUS,Z ; GOTO EE_V9 ; verify pass DECFSZ LOADCNT,F ; attempt counter GOTO EE_V1 ; retry ; fail EE_V8 BSF FLAGS3,6 ; flag EEPROM write failure RETURN ; finished ; pass EE_V9 BCF FLAGS3,6 ; flag EEPROM write passed RETURN ; finished ;************ ; WRITE 1 BYTE TO EEPROM ; The byte in "TXDAT" is written to the address in "I2CADD". ; This routine sends the device select byte and the address before the data to ; be written. ; A dummy-write is achieved by first setting (BSF FLAGS1,0) ; A dummy-write sends only the device select byte, address byte and stop. ; A data write also sends a data byte before the stop and waits for programming ; to complete before returning. ; The bytes are transfered to "W" for actual sending (I2CTX subroutine). EE_WR CALL I2CST ; generate start MOVLW 10100000B ; I2C device address into W CALL I2CTX ; send device address MOVF I2CADD,W ; get write address into W CALL I2CTX ; send address byte BTFSC FLAGS1,0 ; dummy write ? GOTO EE_WR1 ; skip (dummy write to set address) MOVF TXDAT,W ; get data into W CALL I2CTX ; send data byte EE_WR1 CALL I2CSTOP ; generate stop BTFSS FLAGS1,0 ; dummy write ? CALL DEL60 ; write delay EE_WR2 BCF FLAGS1,0 ; clr flag RETURN ; finished ;************ ; READ DATA FROM EEPROM ; The EEPROM address to be read is set with a dummy write (see EE_WR). ; The first address to read is passed in "I2CADD". ; The data read is saved, FSR indexed & consecutive bytes. ; If FLAGS1,4 is set, only 1 byte is read & returned in RXDAT (for verify) ; FSR register is incremented, unless FLAGS1,4 is set. ; The number of bytes to be read is set by RDBCNT (defaults to 1) ; read example ; MOVLW ??? ; set address to read ; MOVWF I2CADD ; " ; MOVLW 03D ; set byte counter to 03 optional ; MOVWF RDBCNT ; " " ; MOVLW DUSK ; set address to put data ; MOVWF FSR ; " ; BSF FLAGS1,0 ; dummy write, sets address ; CALL EE_WR ; write eeprom ; BCF FLAGS1,4 ; clr flag (multiple bytes read) ; CALL EE_RD ; read byte/s now ; start EE_RD CALL I2CST ; generate start ; send address MOVLW 10100001B ; I2C device address into W CALL I2CTX ; send device address ; read data EE_RD1 CALL I2CRX ; read data byte BTFSC FLAGS1,4 ; set = read only 1 byte GOTO EE_RD2 ; clear = read multiple bytes MOVF RXDAT,W ; save data byte MOVWF INDF ; " INCF FSR,F ; next address DECFSZ RDBCNT,F ; byte counter GOTO EE_RD3 ; continue ; stop EE_RD2 CALL I2CSTOP ; generate stop BSF FLAGS1,4 ; read only one byte RETURN ; finished ; ack EE_RD3 CALL I2CACK ; send ACK GOTO EE_RD1 ; read next byte ;****************************************************************************** ; I2C ROUTINES (low level) ;****************************************************************************** ; These subroutines handle communications with I2C slave devices. ; these can include: ; DS1621 TEMPERATURE SENSOR (address = 1001xxxd) ; 24C02 EEPROM (address = 1010xxxd) ; DATA line must have a pullup to +5v ; CLOCK line is always an output ; These are the low level routines. ; I2CST generate start condition ; I2CRX receive byte from slave (returned in "RXDAT") ; I2CTX transmit byte to slave (from "TXDAT") ; I2CACK generate acknowledge ; I2CNAK generate negative acknowledge ; I2CSTOP generate stop condition ; BIT_DLY I2C bit delay (adjust for clock speed)(10D = 4MHz) ;************************************************************************ ;************ ; GENERATE I2C START CONDITION (PIC) ; make DATA high to low while CLOCK high I2CST BSF STATUS,RP0 ; select bank 1 BCF TRISA,DAT_I2C ; data line = output BCF STATUS,RP0 ; select bank 0 BSF PORTA,CLK_I2C ; set clock bit BSF PORTA,DAT_I2C ; ensure data bit = high CALL BIT_DLY ; bit delay BCF PORTA,DAT_I2C ; make data bit = low CALL BIT_DLY ; bit delay BCF PORTA,CLK_I2C ; clear clock bit RETURN ; finished ;************ ; SEND DATA BYTE IN "W" (PIC) ; Single byte transmission to I2C slave (MSB first) ; The data is changed while the clock is low, the clock is then ; pulsed high. ; W holds the data byte to send. I2CTX MOVWF I2CWRK ; move to working register (for RLF) BSF STATUS,RP0 ; select bank 1 BCF TRISA,DAT_I2C ; data line = output BCF STATUS,RP0 ; select bank 0 MOVLW 08D ; bit count MOVWF BITCNT ; counter I2CTX1 CLRWDT ; reset watchdog timer RLF I2CWRK,F ; move bit to carry BTFSS STATUS,C ; GOTO I2CTX2 ; BSF PORTA,DAT_I2C ; data = 1 GOTO I2CTX3 ; I2CTX2 BCF PORTA,DAT_I2C ; data = 0 I2CTX3 CALL BIT_DLY ; bit delay BSF PORTA,CLK_I2C ; set clock bit CALL BIT_DLY ; bit delay BCF PORTA,CLK_I2C ; clear clock bit DECFSZ BITCNT,F ; decrement bit counter GOTO I2CTX1 ; next bit BSF STATUS,RP0 ; select bank 1 BSF TRISA,DAT_I2C ; data line = input BCF STATUS,RP0 ; select bank 0 BSF PORTA,DAT_I2C ; return data line to rest state BSF PORTA,CLK_I2C ; set clock bit CALL BIT_DLY ; bit delay ; SAMPLE ACK HERE IF REQUIRED BCF PORTA,CLK_I2C ; clear clock bit RETURN ; finished ;************ ; RECEIVE DATA BYTE TO "DIN" (PIC) ; Single byte reception from I2C slave (MSB first) returned in "RXDAT" ; Data from the slave is valid following the falling edge of the clock. ; Data should be read on the rising edge of the clock. ; Data is returned in RXDAT. I2CRX BSF STATUS,RP0 ; select bank 1 BSF TRISA,DAT_I2C ; data line = input BCF STATUS,RP0 ; select bank 0 MOVLW 08D ; bit count MOVWF BITCNT ; counter ; read I2CRX1 CLRWDT ; reset watchdog timer BSF PORTA,CLK_I2C ; set clock bit CALL BIT_DLY ; bit delay BTFSC PORTA,DAT_I2C ; test data bit GOTO I2CRX2 ; high = set carry BCF STATUS,C ; clear the carry GOTO I2CRX3 ; I2CRX2 BSF STATUS,C ; set the carry I2CRX3 RLF RXDAT,F ; move data in from carry BCF PORTA,CLK_I2C ; clear clock bit CALL BIT_DLY ; bit delay DECFSZ BITCNT,F ; decrement bit counter GOTO I2CRX1 ; next bit RETURN ; finished ;************ ; SEND ACK (PIC) ; Sends I2C acknowledge (after each received byte) ; A & X are unchanged I2CACK BSF STATUS,RP0 ; select bank 1 BCF TRISA,DAT_I2C ; data line = output BCF STATUS,RP0 ; select bank 0 BCF PORTA,DAT_I2C ; data = 0 CALL BIT_DLY ; bit delay BSF PORTA,CLK_I2C ; set clock bit CALL BIT_DLY ; bit delay BCF PORTA,CLK_I2C ; clear clock bit CALL BIT_DLY ; bit delay RETURN ; finished ;************ ; SEND NACK + STOP CONDITION (PIC) ; Sends I2C negative acknowledge (on completion of receive) ; A & X are unchanged I2CNAK BSF STATUS,RP0 ; select bank 1 BCF TRISA,DAT_I2C ; data line = output BCF STATUS,RP0 ; select bank 0 BSF PORTA,DAT_I2C ; data = 1 BSF PORTA,CLK_I2C ; set clock bit CALL BIT_DLY ; bit delay BCF PORTA,CLK_I2C ; clear clock bit ;************ ; SEND STOP CONDITION (PIC) ; make DATA low to high while CLK high ; A & X are unchanged I2CSTOP BSF STATUS,RP0 ; select bank 1 BCF TRISA,DAT_I2C ; data line = output BCF STATUS,RP0 ; select bank 0 BCF PORTA,DAT_I2C ; data = 0 CALL BIT_DLY ; bit delay BSF PORTA,CLK_I2C ; set clock bit CALL BIT_DLY ; bit delay BSF PORTA,DAT_I2C ; data = 1 BSF STATUS,RP0 ; select bank 1 BSF TRISA,DAT_I2C ; data line = input BCF STATUS,RP0 ; select bank 0 RETURN ; finished ;************ ; I2C BIT DELAYS (PIC) ; bit delay (depends on clock speed) BIT_DLY MOVLW 10D ; delay time MOVWF GPTIM1 ; counter BDEL1 CLRWDT ; reset watchdog timer DECFSZ GPTIM1,F ; test counter GOTO BDEL1 ; loop RETURN ; finished ;************************************************************************ ; Short Delays ;************************************************************************ ; 60mS (+ or - 10mS) (RTI based) DEL60 MOVLW 06D ; delay time MOVWF GPTIM1 ; counter DEL601 CLRWDT ; reset watchdog timer MOVF GPTIM1,F ; test counter BTFSS STATUS,Z ; GOTO DEL601 ; loop RETURN ; finished ; 2 SEC (+ or - 10mS) (RTI based) DEL25S MOVLW 25D ; delay time (0.25 seconds) MOVWF GPTIM1 ; counter GOTO DEL2S1 ; continue DEL2S MOVLW 200D ; delay time (2 seconds) MOVWF GPTIM1 ; counter DEL2S1 CLRWDT ; reset watchdog timer MOVF GPTIM1,F ; test counter BTFSS STATUS,Z ; GOTO DEL2S1 ; loop RETURN ; finished ;****************************************************************************** ; Data I/O ROUTINES ;****************************************************************************** ; Send data in W out via external I/O. ; 8 bits, short pulse = low, long pulse = high, MSB first. DAT_TX MOVWF TMP2 ; data to send MOVLW 8 ; bit counter MOVWF TMP1 ; " BCF PORTB,EXT_IO ; make output low NOP ; 1 bit delay DATTX1 BSF PORTB,EXT_IO ; make output high RLF TMP2,F ; get data bit BTFSS STATUS,C ; test BCF PORTB,EXT_IO ; make data low NOP ; 3 bit delay NOP ; " NOP ; " BCF PORTB,EXT_IO ; make data low NOP ; 1 bit delay DECFSZ TMP1,F ; bit count -1 GOTO DATTX1 ; loop MOVF TMP2,W ; restore W RETURN ;************************************************************************ ; DEBUG misc routine ;************************************************************************ ; copyright notice ORG 03E0H ; address MSG1 DFB "4T4T4G4_414A414 404V4041" ; TTG_1A1 0V01 RETLW 00H MSG2 DFB "4(4C4)4 4P4M4B4 424040414 " ; (C) PMB 2001 RETLW 00H END