view src/i2c.asm @ 624:7bdcc591196c

Support for Compass3 hardware
author heinrichsweikamp
date Thu, 13 Jun 2019 10:07:55 +0200
parents c40025d8e750
children bf5fee575701
line wrap: on
line source

;=============================================================================
;
;   File i2c.asm                              combined next generation V3.03.2
;
;   I2C Interface
;
;   Copyright (c) 2012, JD Gascuel, HeinrichsWeikamp, all right reserved.
;=============================================================================
;
;   Compass0
;   --------
;   HMC5883L's read address  (8-Bit):  0x3D
;   HMC5883L's write address (8-Bit):  0x3C
;   MMA8452Q's read address  (8-Bit):  0x39
;   MMA8452Q's write address (8-Bit):  0x38
;
;   Compass1
;   --------
;   LSM303D's read address  (8-Bit):   0x3D
;   LSM303D's write address (8-Bit):   0x3C
;
;   Compass2
;   --------
;   LSM303AGR's Compass read address       (8-Bit):  0x3D
;   LSM303AGR's Compass write address      (8-Bit):  0x3C
;   LSM303AGR's Acceleration read address  (8-Bit):  0x33
;   LSM303AGR's Acceleration write address (8-Bit):  0x32
;    
;   Compass3
;   --------
;   LSM303C's Compass read address	    (8-Bit): 0x3D
;   LSM303C's Compass write address	    (8-Bit): 0x3C
;   LSM303C's Acceleration read address	    (8-Bit): 0x3B
;   LSM303C's Acceleration write address    (8-Bit): 0x3A
;
;   RX Circuity
;   -----------
;   RX Circuity read address  (8-Bit):  0x51
;   RX Circuity write address (8-Bit):  0x50
;
;

; HISTORY
;  2012-08-22 : [mH] Creation
;  2018-02-18 : [mH] Sync with hwOS Sport release


#include "hwos.inc"						; Mandatory header
#include "wait.inc"
#include "math.inc"
#include "external_flash.inc"


i2c	CODE

;=============================================================================

I2C_TX:
	movwf	SSP1BUF
	rcall	WaitMSSP
	bra		I2C_WaitforACK				; returns...

I2C_TwoBytesRX_div16:					; get two bytes and divide lo:hi/16 (signed)
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,hi					; data byte
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,lo					; data byte
I2C_TwoBytesRX_div16_2:					; divide lo:hi/16 (signed) only
	bcf		STATUS,C
	btfsc	hi,7						; copy sign bit to carry
	bsf		STATUS,C
	rrcf	hi							; /2
	rrcf	lo
I2C_TwoBytesRX_div8_2:					; divide lo:hi/8 (signed) only
	bcf		STATUS,C
	btfsc	hi,7						; copy sign bit to carry
	bsf		STATUS,C
	rrcf	hi							; /4
	rrcf	lo
	bcf		STATUS,C
	btfsc	hi,7						; copy sign bit to carry
	bsf		STATUS,C
	rrcf	hi							; /8
	rrcf	lo
	bcf		STATUS,C
	btfsc	hi,7						; copy sign bit to carry
	bsf		STATUS,C
	rrcf	hi							; /16
	rrcf	lo
	return

	global	I2C_RX_accelerometer
I2C_RX_accelerometer:
        btfsc   compass_type3	    ; compass3
	bra	    I2C_RX_accelerometer_compass3    ; yes
	btfsc	compass_type2					; compass2 ?
	bra		I2C_RX_accelerometer_compass2	; YES
	btfsc	compass_type					; compass1 ?
	bra		I2C_RX_accelerometer_compass1	; YES
I2C_RX_accelerometer_compass0:
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x38						; address
	rcall	I2C_TX
	movlw	0x00
	rcall	I2C_TX
	bsf		SSP1CON2,RSEN				; repeated start condition
	rcall	WaitMSSP
	movlw	0x39						; address
	rcall	I2C_TX

	rcall	I2C_OneByteRX				; get status byte
	movf	SSP1BUF,W

	; Non-flipped screen:
	; Chip orientation on the PCB requires
	; Original = corrected
	; x = -x
	; y = -y
	; z = -z

	; Flipped screen:
	; Chip orientation on the PCB requires
	; Original = corrected
	; x = x
	; y = y
	; z = -z

	rcall	I2C_TwoBytesRX_div16		; get two bytes and divide /16 (signed)
	btfsc	flip_screen					; 180° rotation?
	bra		I2C_RX_accelerometer2		; YES
	comf	hi							; 16 bit sign change
	negf	lo
	btfsc	STATUS,C					; carry to propagate?
	incf	hi,F						; YES - do it
I2C_RX_accelerometer2:
	MOVII	mpr,accel_DX				; copy result
	rcall	I2C_TwoBytesRX_div16		; get two bytes and divide /16 (signed)
	btfsc	flip_screen					; 180° rotation?
	bra		I2C_RX_accelerometer3		; YES
	comf	hi							; 16 bit sign change
	negf	lo
	btfsc	STATUS,C					; carry to propagate?
	incf	hi,F						; YES - do it
I2C_RX_accelerometer3:
	MOVII	mpr,accel_DY				; copy result
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,hi					; data byte
	bsf		SSP1CON2, RCEN				; Enable receive mode
	rcall	WaitMSSP
; According to data sheet there should be no master Acknowledge for the last byte (accel_DZ+0)...
	movff	SSP1BUF,lo					; data byte

	rcall	I2C_TwoBytesRX_div16_2		; divide lo:hi/16 (signed) only
	comf	hi							; 16 bit sign change
	negf	lo
	btfsc	STATUS,C					; carry to propagate?
	incf	hi,F						; YES - do it
	MOVII	mpr,accel_DZ				; copy result
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP					; ... and return

I2C_RX_accelerometer_compass1:
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x3C						; address
	rcall	I2C_TX
	movlw	b'10101000'					; 0x28 with auto-increment (MSB=1)
	rcall	I2C_TX
	bsf		SSP1CON2,RSEN				; repeated start condition (!)
	rcall	WaitMSSP
	movlw	0x3D						; address
I2C_RX_accelerometer_compass1_xx:		;  compass2 and 3 continue here... 
	rcall	I2C_TX

	; Non-flipped screen:
	; Chip orientation on the PCB requires
	; Original = Corrected
	; x = -x (Compass 1)
	; x = x (Compass 2)
	; y = -y
	; z = -z

	; Flipped screen:
	; Chip orientation on the PCB requires
	; Original = Corrected
	; x = x (Compass 1)
	; x = -x (Compass 2)
	; y = y
	; z = -z

	; Dump the accelerator data
	rcall	I2C_OneByteRX
	movff	SSP1BUF,lo					; accel_DX+0
	rcall	I2C_OneByteRX
	movff	SSP1BUF,hi					; accel_DX+1
	rcall	I2C_TwoBytesRX_div16_2		; divide lo:hi/16 (signed) only
	btfss	compass_type2				; compass 2?
	bra		I2C_RX_accelerometer1_c1	; NO  - compass 1
	; compass 2
	btfss	flip_screen					; 180° rotation?
	bra		I2C_RX_accelerometer2_c1	; NO  - continue with normal compass1 routines for Y and Z
	; flipped compass 2, negate x
	comf	hi							; 16 bit sign change
	negf	lo
	btfsc	STATUS,C					; carry to propagate?
	incf	hi,F						; YES - do it
	bra		I2C_RX_accelerometer2_c1	; continue with normal compass1 routines for Y and Z

I2C_RX_accelerometer1_c1:
	btfsc	flip_screen					; 180° rotation?
	bra		I2C_RX_accelerometer2_c1	; YES
	; non-flipped compass 1, negate x
	comf	hi							; 16 bit sign change
	negf	lo
	btfsc	STATUS,C					; carry to propagate?
	incf	hi,F						; YES - do it
I2C_RX_accelerometer2_c1:
	; flipped compass 1, non-flipped compass 2
	MOVII	mpr,accel_DX				; copy result
	rcall	I2C_OneByteRX
	movff	SSP1BUF,lo					; accel_DY+0
	rcall	I2C_OneByteRX
	movff	SSP1BUF,hi					; accel_DY+1

	rcall	I2C_TwoBytesRX_div16_2		; divide lo:hi/16 (signed) only
	btfsc	flip_screen					; 180° rotation?
	bra		I2C_RX_accelerometer3_c1	; YES
	comf	hi							; 16 bit sign change
	negf	lo
	btfsc	STATUS,C					; carry to propagate?
	incf	hi,F						; YES - do it
I2C_RX_accelerometer3_c1:
	MOVII	mpr,accel_DY				; copy result
	rcall	I2C_OneByteRX
	movff	SSP1BUF,lo					; accel_DZ+0
	bsf		SSP1CON2, RCEN				; Enable receive mode
	rcall	WaitMSSP
; According to data sheet there should be no master Acknowledge for the last byte (accel_DZ+1)...
	movff	SSP1BUF,hi					; accel_DZ+1
	bsf		SSP1CON2,PEN				; stop condition
	rcall	WaitMSSP
	rcall	I2C_TwoBytesRX_div16_2		; divide lo:hi/16 (signed) only
	comf	hi							; 16 bit sign change for Z
	negf	lo
	btfsc	STATUS,C					; carry to propagate?
	incf	hi,F						; YES - do it
	MOVII	mpr,accel_DZ				; copy result
	return

I2C_RX_accelerometer_compass2:
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x32						; address
	rcall	I2C_TX
	movlw	b'10101000'					; 0x28 with auto-increment (MSB=1)
	rcall	I2C_TX
	bsf		SSP1CON2,RSEN				; repeated start condition (!)
	rcall	WaitMSSP
	movlw	0x33						; address
	bra	I2C_RX_accelerometer_compass1_xx

I2C_RX_accelerometer_compass3:
	bsf	SSP1CON2,SEN	    ; Start condition
	rcall	WaitMSSP
	movlw	0x3A                ; address
	rcall   I2C_TX
	movlw	0x28		    ; 0x28 (OUT_X_L_A)
	rcall   I2C_TX
	bsf	SSP1CON2,RSEN	    ; Repeated start condition (!)
	rcall	WaitMSSP
	movlw	0x3B                ; address
	bra	I2C_RX_accelerometer_compass1_xx

I2C_OneByteRX:
	bsf		SSP1CON2,RCEN				; enable receive mode
	rcall	WaitMSSP
	bsf		SSP1CON2,ACKEN				; master acknowledge
	bra		WaitMSSP					; ... and return


;-----------------------------------------------------------------------------
 IFDEF _compass

	global	I2C_RX_compass
I2C_RX_compass:
        btfsc   compass_type3				; compass3
        bra	    I2C_RX_compass3				; YES
	btfsc	compass_type2				; compass2 ?
	bra		I2C_RX_compass2				; YES
	btfsc	compass_type				; compass1 ?
	bra		I2C_RX_compass1				; YES
I2C_RX_compass0:
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x3C						; address
	rcall	I2C_TX
	movlw	0x03
	rcall	I2C_TX
	bsf		SSP1CON2,PEN				; stop condition
	rcall	WaitMSSP

	bcf		PIR1,SSP1IF
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x3D						; address
	rcall	I2C_TX

	; Compass IC sends data in following order:
	; x MSB
	; x LSB
	; z MSB
	; z LSB
	; y MSB
	; y LSB

	; Non-flipped screen
	; Chip orientation on the PCB requires
	; Original = Corrected
	; x = -y
	; z = z
	; y = x

	; Flipped screen
	; Chip orientation on the PCB requires
	; Original = Corrected
	; x = y
	; z = z
	; y = -x

	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,compass_DY+1		; data byte
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,compass_DY+0		; data byte
	btfsc	flip_screen					; 180° rotation?
	bra		I2C_RX_compass0_2			; NO
	banksel	compass_DY					; YES - flip Y
	comf	compass_DY+1				;     - 16 bit sign change
	negf	compass_DY+0
	btfsc	STATUS,C					;     - carry to propagate?
	incf	compass_DY+1,F				;       YES - do it
	banksel	common
I2C_RX_compass0_2:
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,compass_DZ+1		; data byte
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,compass_DZ+0		; data byte
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,compass_DX+1		; data byte
	bsf		SSP1CON2, RCEN				; Enable receive mode
	rcall	WaitMSSP
	movff	SSP1BUF,compass_DX+0		; data byte
	bsf		SSP1CON2,PEN				; stop condition
	rcall	WaitMSSP
	btfss	flip_screen					; 180° rotation?
	return								; NO  - done
	banksel	compass_DX					; YES - flip X
	comf	compass_DX+1				;     - 16 bit sign change
	negf	compass_DX+0
	btfsc	STATUS,C					;     - carry to propagate?
	incf	compass_DX+1,F				;       YES - do it
	banksel	common
	return

I2C_RX_compass1:						; new compass
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x3C						; address
	rcall	I2C_TX
	movlw	b'10001000'					; 0x08 with auto-increment (MSB=1)
	rcall	I2C_TX
	bsf		SSP1CON2,RSEN				; repeated start condition (!)
	rcall	WaitMSSP
	movlw	0x3D						; address
	rcall	I2C_TX
	;rcall	WaitMSSP					; TODO needed? (mH)
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,lo					; data byte
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,hi					; data byte
	rcall	I2C_TwoBytesRX_div8_2
	MOVII	mpr,compass_DX
	btfss	flip_screen					; 180° rotation?
	bra		I2C_RX_compass1_1			; NO
	banksel	compass_DX					; YES - flip X
	comf	compass_DX+1				;     - 16 bit sign change
	negf	compass_DX+0
	btfsc	STATUS,C					;     - carry to propagate?
	incf	compass_DX+1,F				;       YES - do it
	banksel	common
I2C_RX_compass1_1:
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,lo					; data byte
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,hi					; data byte
	rcall	I2C_TwoBytesRX_div8_2
	MOVII	mpr,compass_DY
	btfss	flip_screen					; 180° rotation?
	bra		I2C_RX_compass1_2			; NO
	banksel	compass_DY					; YES - flip Y
	comf	compass_DY+1				;     - 16 bit sign change
	negf	compass_DY+0
	btfsc	STATUS,C					;     - carry to propagate?
	incf	compass_DY+1,F				;       YES - do it
	banksel	common
I2C_RX_compass1_2:
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,lo					; data byte
	bsf		SSP1CON2, RCEN				; Enable receive mode
	rcall	WaitMSSP
	movff	SSP1BUF,hi					; data byte
	rcall	I2C_TwoBytesRX_div8_2
	MOVII	mpr,compass_DZ
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP					; ... and return

I2C_RX_compass2:						; newest compass
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x3C						; address
	rcall	I2C_TX
	movlw	0xE8						; 0x68 with auto-increment (MSB=1)
	rcall	I2C_TX
	bsf		SSP1CON2,RSEN				; repeated start condition (!)
	rcall	WaitMSSP
	movlw	0x3D						; address
	rcall	I2C_TX
I2C_RX_compass2_xx:    ; compass 3 enters here	
;	rcall	WaitMSSP
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,lo					; data byte
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,hi					; data byte
;	rcall	I2C_TwoBytesRX_div8_2
	btfsc	flip_screen					; 180° rotation?
	bra		I2C_RX_compass2_1			; YES - do nothing with X
										; NO  - flip X
	comf	hi							;     - 16 bit sign change
	negf	lo
	btfsc	STATUS,C					;     - carry to propagate?
	incf	hi,F						;       YES - do it
I2C_RX_compass2_1:
	MOVII	mpr,compass_DX
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,lo					; data byte
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,hi					; data byte
;	rcall	I2C_TwoBytesRX_div8_2
	btfss	flip_screen					; 180° rotation?
	bra		I2C_RX_compass2_2			; NO  - do nothing with Y
										; YES - flip Y
	comf	hi							;     - 16 bit sign change
	negf	lo
	btfsc	STATUS,C					;     - carry to propagate?
	incf	hi,F						;       YES - do it
I2C_RX_compass2_2:
	MOVII	mpr,compass_DY
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,lo					; data byte
	rcall	I2C_OneByteRX				; get one byte
	movff	SSP1BUF,hi					; data byte
;	rcall	I2C_TwoBytesRX_div8_2
	MOVII	mpr,compass_DZ
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP ;(And return)

I2C_RX_compass3:
	bsf		SSP1CON2,SEN	    ; Start condition
	rcall	WaitMSSP
	movlw	0x3C                ; address
	rcall   I2C_TX
	movlw	0xA8		    ; 0x28 with auto-increment (MSB=1)
	rcall   I2C_TX
	bsf	SSP1CON2,RSEN	    ; Repeated start condition (!)
	rcall	WaitMSSP
	movlw	0x3D                ; address
	rcall   I2C_TX
	bra	I2C_RX_compass2_xx

 ENDIF	; _compass

;-----------------------------------------------------------------------------

	global	I2C_init_compass
I2C_init_compass:
	bsf		compass_enabled
	bcf		compass_type2
        bcf		compass_type3
    
    ; probe compass 3
        bsf	SSP1CON2,SEN		; Start condition
        rcall	WaitMSSP
        movlw	0x3A			; Address byte + Write bit
        movwf	SSP1BUF			; control byte
        rcall	WaitMSSP
        btfss	SSP1CON2,ACKSTAT	; ACK?
        bsf	compass_type3		; ACK send. compass2 present
        bsf	SSP1CON2,PEN		; Stop condition
        rcall	WaitMSSP
    
	btfsc   compass_type3
        bra	I2C_init_compass3		; Compass3

	; check for compass2
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x32						; address byte + Write bit
	movwf	SSP1BUF						; control byte
	rcall	WaitMSSP
	btfss	SSP1CON2,ACKSTAT			; ACK?
	bsf		compass_type2				; YES - ACK send, compass2 present
	bsf		SSP1CON2,PEN				; NO  - stop condition
	rcall	WaitMSSP
	btfsc	compass_type2
	bra		I2C_init_compass2			; compass2

	; check for compass0 or compass1...
	bsf		compass_type				; set flag
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x3C						; address
	rcall	I2C_TX
	movlw	0x0F
	rcall	I2C_TX
	bsf		SSP1CON2,PEN				; stop condition
	rcall	WaitMSSP
	bcf		PIR1,SSP1IF
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x3D						; address
	rcall	I2C_TX
	rcall	I2C_OneByteRX				; get one byte
	movlw	0x49						; 0x49 = Compass1
	cpfseq	SSP1BUF
	bcf		compass_type				; clear flag
	bsf		SSP1CON2,PEN				; stop condition
	rcall	WaitMSSP
	btfsc	compass_type				; compass1 ?
	bra		I2C_init_compass1			; YES

	; init compass0
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x3C						; address
	rcall	I2C_TX
	movlw	0x00
	rcall	I2C_TX
;	movlw	b'01101001'					; ConfigA: 3 Hz, 8 samples averaged, test mode (positive bias)
	movlw	b'01101000'					; ConfigA: 3 Hz, 8 samples averaged
	rcall	I2C_TX
I2C_init_compass_common:
	movff	opt_compass_gain,i2c_temp1	; 0-7 (230 LSB/Gauss to 1370 LSB/Gauss)
	swapf	i2c_temp1,F
	comf	i2c_temp1,F
	bcf		STATUS,C
	rlcf	i2c_temp1
	movf	i2c_temp1,W
	clrf	i2c_temp1
	rcall	I2C_TX
	movlw	b'00000000'					; continuous mode
	rcall	I2C_TX
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP					; ... and return

I2C_init_compass1:
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x3C						; address
	rcall	I2C_TX
	movlw	0x9F						; 1F with auto-increment (MSB=1)
	rcall	I2C_TX
	movlw	b'00000000'					; CTRL0
	rcall	I2C_TX
	movlw	b'00101111'					; CTRL1 (6.25 Hz, BDU=0, x,y,z = ON)
	rcall	I2C_TX
	movlw	b'11000000'					; CTRL2 (50 Hz, +/- 2g)
	rcall	I2C_TX
	movlw	b'00000000'					; CTRL3
	rcall	I2C_TX
	movlw	b'00000000'					; CTRL4
	rcall	I2C_TX
	movlw	b'01100100'					; CTRL5 HIGH res, 6.25 Hz
	rcall	I2C_TX
init_compass1_common:
	movff	opt_compass_gain,i2c_temp1	; 0-7 (230 LSB/Gauss to 1370 LSB/Gauss)
	movlw	b'01100000'					; CTRL6 Full scale (+/-12 Gauss -> 2730 LSB/Gauss)
	dcfsnz	i2c_temp1,F					; = 1?
	movlw	b'01100000'					; YES - CTRL6 Full scale (+/-12 Gauss -> 2730 LSB/Gauss)
	dcfsnz	i2c_temp1,F					; = 2?
	movlw	b'01000000'					; YES - CTRL6 (+/-8 Gauss)
	dcfsnz	i2c_temp1,F					; = 3?
	movlw	b'01000000'					; YES - CTRL6 (+/-8 Gauss)
	dcfsnz	i2c_temp1,F					; = 4?
	movlw	b'00100000'					; YES - CTRL6 (+/-4 Gauss)
	dcfsnz	i2c_temp1,F					; = 5?
	movlw	b'00100000'					; YES - CTRL6 (+/-4 Gauss)
	dcfsnz	i2c_temp1,F					; = 6?
	movlw	b'00000000'					; YES - CTRL6 (+/-2 Gauss)
	dcfsnz	i2c_temp1,F					; = 7?
	movlw	b'00000000'					; YES - CTRL6 (+/-2 Gauss)
	rcall	I2C_TX
	movlw	b'00000000'					; CTRL7 Continuous Mode
	rcall	I2C_TX
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP					; ... and return

I2C_init_compass2:
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x3C						; address
	rcall	I2C_TX
	movlw	0xE0						; 0x60 with auto-increment (MSB=1)
	rcall	I2C_TX
	movlw	b'00000000'					; CFG_REG_A_M        0x60 (10 Hz, continuous)
	rcall	I2C_TX
	movlw	b'00000000'					; CFG_REG_B_M        0x61 (low-pass filter off, set pulse is released every 63 ODR)
	rcall	I2C_TX
	movlw	b'00000000'					; CFG_REG_C_M BDU=0  0x62
	rcall	I2C_TX
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP					; ... and return

I2C_init_compass3:
	bsf	SSP1CON2,SEN	; Start condition
	rcall	WaitMSSP
	movlw	0x3C            ; address
	rcall   I2C_TX
	movlw	0xA0		; 0x20 with auto-increment (MSB=1)
	rcall   I2C_TX
	movlw	b'00010000'	; CTRL_REG1_M (10Hz) 0x20
	rcall   I2C_TX
	movlw	b'01100000'	; CTRL_REG2_M (Full-scale: +/- 16gauss) 0x21
	rcall   I2C_TX
	movlw	b'01000000'	; CTRL_REG3_M (Continuous) 0x22
	rcall   I2C_TX
	movlw	b'00000000'	; CTRL_REG4_M (Z in Low-power mode) 0x23
	rcall   I2C_TX
	movlw	b'00000000'	; CTRL_REG5_M 0x24
	rcall   I2C_TX
	movlw	b'00000000'	; CTRL_REG5_M 0x24
	rcall   I2C_TX
	bsf	SSP1CON2,PEN	; Stop condition
	bra	WaitMSSP	;(And return)
	

	global	I2C_sleep_compass
I2C_sleep_compass:
	bcf		compass_enabled
        btfsc	compass_type3				; compass3?
	bra		I2C_sleep_compass3			; YES
	btfsc	compass_type2				; compass2 ?
	bra		I2C_sleep_compass2			; YES
	btfsc	compass_type				; compass1 ?
	bra		I2C_sleep_compass1			; YES
I2C_sleep_compass0:
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x3C						; address
	rcall	I2C_TX
	movlw	0x00
	rcall	I2C_TX
	movlw	b'01101000'					; ConfigA
	rcall	I2C_TX
	movlw	b'00100000'					; ConfigB
	rcall	I2C_TX
	movlw	b'00000010'					; idle mode
	rcall	I2C_TX
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP					; ... and return

I2C_sleep_compass1:
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x3C						; address
	rcall	I2C_TX
	movlw	0x20						; CTRL_REG1
	rcall	I2C_TX
	movlw	b'00000000'					; data for CTRL_REG1: acceleration sensor power-down mode
	rcall	I2C_TX
	bsf		SSP1CON2,PEN				; stop condition
	rcall	WaitMSSP
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x3C						; address
	rcall	I2C_TX
	movlw	0x26						; CTRL_REG7
	rcall	I2C_TX
	movlw	b'00000010'					; data for CTRL_REG7: magnetic sensor power-down mode
	rcall	I2C_TX
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP					;    and return

I2C_sleep_compass2:
	; magnetic
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x3C						; address
	rcall	I2C_TX
	movlw	0xE0						; 0x60 with auto-increment (MSB=1)
	rcall	I2C_TX
	movlw	b'00000011'					; CFG_REG_A_M    0x60 (idle mode))
	rcall	I2C_TX
	movlw	b'00000100'					; CFG_REG_B_M    0x61 (set pulse is released only at power-on after PD condition)
	rcall	I2C_TX
	movlw	b'01010001'					; CFG_REG_C_M    0x62
	rcall	I2C_TX
	movlw	b'00000000'					; INT_CTRL_REG_M 0x63
	rcall	I2C_TX
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP					; ... and return

I2C_sleep_compass3:
    ; magnetic
        bsf	SSP1CON2,SEN	; Start condition
        rcall	WaitMSSP
        movlw	0x3C            ; address
        rcall   I2C_TX
	movlw	0xA2		; 0x22 with auto-increment (MSB=1)
	movlw	b'01000010'	; CTRL_REG3_M (Power-down) 0x22
	rcall   I2C_TX
	bsf	SSP1CON2,PEN	; Stop condition
	bra	WaitMSSP	;(And return)

I2C_sleep_accelerometer2:
	; accelerometer
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x32						; address
	rcall	I2C_TX
	movlw	0x9F						; 1F with auto-increment (MSB=1)
	rcall	I2C_TX
	movlw	b'00000000'					; TEMP_CFG_REG_A  0x1F (temp sensor off)
	rcall	I2C_TX
	movlw	b'00000000'					; CTRL_REG1_A     0x20 (all off)
	rcall	I2C_TX
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP					; ... and return

I2C_sleep_accelerometer3:
    ; accelerometer
	bsf	SSP1CON2,SEN		; Start condition
	rcall	WaitMSSP
	movlw	0x3A            ; address
	rcall   I2C_TX
	movlw	0x20
	rcall   I2C_TX
	movlw	b'00000000'	; CTRL_REG1_A (100Hz, x,y,z = ON) 0x20
	rcall   I2C_TX
	bsf	SSP1CON2,PEN		; Stop condition
	bra	WaitMSSP	; (And return)


WaitMSSP:
	decfsz	i2c_temp1,F					; check for timeout during I2C action
	bra		WaitMSSP2
	bra		I2CFail						; timeout occurred
WaitMSSP2:
	btfss	PIR1,SSP1IF
	bra		WaitMSSP
	clrf	i2c_temp1
	bcf		PIR1,SSP1IF
	return

I2C_WaitforACK:
	btfss	SSP1CON2,ACKSTAT			; checks for ACK bit from slave
	return
I2CFail:
	rcall	I2CReset					; I2C Reset
	bcf		PIR1,SSP1IF
	clrf	i2c_temp1
	bsf		i2c_error_flag				; set error flag
	return

I2CReset:								; something went wrong (slave holds SDA low?)
	clrf	SSP1CON1					; wake-up slave and reset entire module
	clrf	SSP1CON2
	clrf	SSP1STAT
	bcf		TRISC,3						; SCL OUTPUT
	bsf		TRISC,4						; SDA input
	bcf		PORTC,3
	movlw	d'9'
	movwf	i2c_temp1					; clock-out 9 clock cycles manually
I2CReset_1:
	bsf		PORTC,3						; SCL = 1
	nop
	nop
	nop
	nop
	btfsc	PORTC,4						; SDA = 1 ?
	bra		I2CReset_2					; YES - =1, SDA has been released from slave
	bcf		PORTC,3						; NO  - set SCL = 0
	nop
	nop
	bcf		PORTC,3
	nop
	nop
	decfsz	i2c_temp1,F
	bra		I2CReset_1					; check for nine clock cycles
I2CReset_2:
	bsf		TRISC,3						; SCL Input
	clrf	SSP1CON1					; setup I²C mode
	WAITMS	d'10'						; reset-timeout for I2C devices
	movlw	b'00000000'					; with slew rate control
	movwf	SSP1STAT
	movlw	b'00101000'
	movwf	SSP1CON1
	movlw	b'00000000'
	movwf	SSP1CON2
	movlw	0x27
	movwf	SSP1ADD
	return


	global	I2C_init_accelerometer
I2C_init_accelerometer:
        btfsc   compass_type3				; compass3?
	bra	    I2C_init_accelerometer3		; YES

	btfsc	compass_type2				; compass2 ?
	bra		I2C_init_accelerometer2		; YES

	btfsc	compass_type				; compass1 ?
	return								; YES - ignore

	rcall	I2C_sleep_accelerometer		; registers can only be changed in standby mode

	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x38						; address
	rcall	I2C_TX
	movlw	0x0E						; XYZ_DATA_CFG
	rcall	I2C_TX
	movlw	b'00000000'					; high pass filter = 0, +/- 2g range
	rcall	I2C_TX
	bsf		SSP1CON2,PEN				; stop condition
	rcall	WaitMSSP

	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x38						; address
	rcall	I2C_TX
	movlw	0x2A						; CTRL_REG1
	rcall	I2C_TX
;	movlw	b'00110000'					; CTRL_REG1: 160 ms data rate, standby mode
	movlw	b'00110100'					; CTRL_REG1: 160 ms data rate, standby mode, reduced noise mode
	rcall	I2C_TX
	movlw	b'00000010'					; CTRL_REG2: high-res in active mode
	rcall	I2C_TX
	bsf		SSP1CON2,PEN				; stop condition
	rcall	WaitMSSP

	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x38						; address
	rcall	I2C_TX
	movlw	0x2A						; CTRL_REG1
	rcall	I2C_TX
;	movlw	b'00110001'					; CTRL_REG1: 160 ms data rate, active  mode
	movlw	b'00110101'					; CTRL_REG1: 160 ms data rate, standby mode, reduced noise mode, active Mode
	rcall	I2C_TX
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP					; ... and return

I2C_init_accelerometer2:
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x32						; address
	rcall	I2C_TX
	movlw	0x9F						; 1F with auto-increment (MSB=1)
	rcall	I2C_TX
	movlw	b'00000000'					; TEMP_CFG_REG_A (Temp sensor off)
	rcall	I2C_TX
	movlw	b'01010111'					; CTRL_REG1_A (100Hz, x,y,z = ON)
	rcall	I2C_TX
	movlw	b'00000000'					; CTRL_REG2_A
	rcall	I2C_TX
;	movlw	b'00000000'					; CTRL_REG3_A
;	rcall	I2C_TX
;	movlw	b'00000000'					; CTRL_REG4_A (BDU=0, +/-2g,
;	rcall	I2C_TX
;	movlw	b'00000000'					; CTRL_REG5_A
;	rcall	I2C_TX
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP					; ... and return

I2C_init_accelerometer3:
	bsf	SSP1CON2,SEN		; Start condition
        rcall	WaitMSSP
	movlw	0x3A            ; address
	rcall	I2C_TX
	movlw	0x20
	rcall   I2C_TX
	movlw	b'00110111'	; CTRL_REG1_A (100Hz, x,y,z = ON) 0x20
	rcall   I2C_TX
	movlw	b'00000000'	; CTRL_REG2_A 0x21
	rcall   I2C_TX
	movlw	b'00000000'	; CTRL_REG3_A 0x22
	rcall   I2C_TX
	bsf	SSP1CON2,PEN		; Stop condition
	bra	WaitMSSP	; (And return)
	

	global	I2C_sleep_accelerometer
I2C_sleep_accelerometer:
        btfsc   compass_type3				; Compass3
        bra	    I2C_sleep_accelerometer3		; YES
	btfsc	compass_type2				; Compass2
	bra		I2C_sleep_accelerometer2	; YES
	btfsc	compass_type				; compass1?
	return								; YES - ignore

	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x38						; address
	rcall	I2C_TX
	movlw	0x2A						; CTRL_REG1
	rcall	I2C_TX
	movlw	b'00000000'					; standby mode
	rcall	I2C_TX
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP					; ... and return

lt2942_init_again:
	clrf	i2c_temp1
	movlw	0x02									; point to accumulated charge registers
	rcall	I2C_TX_GAUGE
	movff	battery_accumulated_charge+1,SSP1BUF	; data byte
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	movff	battery_accumulated_charge+0,SSP1BUF	; data byte
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	bsf		SSP1CON2,PEN							; stop condition
	rcall	WaitMSSP
	MOVII	battery_accumulated_charge,sub_a
	; and init again...

	global	lt2942_init
lt2942_init:							; setup control register B
	clrf	i2c_temp1
	movlw	0x01						; point to control reg B
	rcall	I2C_TX_GAUGE
	movlw	b'11111000'					; automatic conversion every two seconds
	movff	WREG, SSP1BUF				; data byte
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP					; ... and return

	global	lt2942_get_status
lt2942_get_status:						; read status register
	bcf		battery_gauge_available		; clear flag
	clrf	i2c_temp1
	movlw	0x00						; point to status register
	rcall	I2C_TX_GAUGE
	rcall	I2C_RX_GAUGE
	movff	SSP1BUF,WREG
	btfss	WREG,7						; 2942 found?
	bsf		battery_gauge_available		; YES - set flag
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP					; ... and return


	global	lt2942_get_voltage
lt2942_get_voltage:						; read battery voltage registers
	clrf	i2c_temp1
	movlw	0x08						; point to voltage registers
	rcall	I2C_TX_GAUGE
	rcall	I2C_RX_GAUGE
	bsf		SSP1CON2,ACKEN				; master acknowledge
	rcall	WaitMSSP
	movff	SSP1BUF,xA+1
	bsf		SSP1CON2, RCEN				; enable receive mode
	rcall	WaitMSSP
	movff	SSP1BUF,xA+0
	bsf		SSP1CON2,PEN				; stop condition
	rcall	WaitMSSP

	; convert voltage from raw value to Volt
	MOVLI	.6000,xB					; load conversion multiplicand into xB
	call	mult16x16					; xC = xA * xB -> multiply raw value in xA with conversion multiplicand
										; divide by 65536 instead of 65535, introducing an error of 65536/65535 = 0.002 %
	movff	xC+2,batt_voltage+0			; divide by 65536 can easily be done by just taking the 3rd and 4th byte of the multiplication result
	movff	xC+3,batt_voltage+1			; ...

	tstfsz	batt_voltage+1				; < 256 mV ?
	return								; NO  - done
	bra		lt2942_init					; YES - ... and return


	global	lt2942_get_temperature
lt2942_get_temperature:					; read battery temperature
	clrf	i2c_temp1
	movlw	0x0C						; point to temperature register
	call	I2C_TX_GAUGE
	call	I2C_RX_GAUGE
	bsf		SSP1CON2,ACKEN				; master acknowledge
	rcall	WaitMSSP
	movff	SSP1BUF,xA+1				; store raw temperature, high byte
	bsf		SSP1CON2, RCEN				; enable receive mode
	rcall	WaitMSSP
	movff	SSP1BUF,xA+0				; store raw temperature, low byte
	bsf		SSP1CON2,PEN				; stop condition
	rcall	WaitMSSP

	; convert temperature from raw value to Kelvin
	MOVLI	.6000,xB					; load conversion multiplicand into xB
	call	mult16x16					; xC = xA * xB -> multiply raw value in xA with conversion multiplicand
										; divide by 65536 instead of 65535, introducing an error of 65536/65535 = 0.002 %
	movff	xC+2,battery_temperature+0	; divide by 65536 can easily be done by just taking the 3rd and 4th byte of the multiplication result
	movff	xC+3,battery_temperature+1	; ...

	; check if battery is charged right now
	btfss	cc_active					; in CC charging mode?
	return								; NO  - not charging, done

	; check for over-temperature while charging
	MOVLI	max_battery_charge_temp,sub_a
	MOVII	battery_temperature,    sub_b
	call	cmpU16						; sub_a - sub_b (with UNSIGNED values)
	btfss	neg_flag					; result negative?
	return								; NO  - temperature <= threshold, ok, done
										; YES - too hot, disable charging circuitry
	bsf		charge_disable				;     - set      charging-inhibit signal
	bcf		charge_enable				;     - activate charging-inhibit signal
	bsf		battery_overtemp			;     - flag that the battery charging over-temperature protection has tripped
	return


	global	lt2942_get_accumulated_charge
lt2942_get_accumulated_charge:			; read accumulated charge and compute percent
	clrf	i2c_temp1
	movlw	0x00						; point to status register
	rcall	I2C_TX_GAUGE
	rcall	I2C_RX_GAUGE
	bsf		SSP1CON2,ACKEN				; master acknowledge
	rcall	WaitMSSP
	movff	SSP1BUF,gauge_status_byte

	bsf		SSP1CON2, RCEN				; enable receive mode
	rcall	WaitMSSP					; dummy read (control byte)
	movf	SSP1BUF,W
	bsf		SSP1CON2,ACKEN				; master acknowledge
	rcall	WaitMSSP

	bsf		SSP1CON2, RCEN				; enable receive mode
	rcall	WaitMSSP
	movff	SSP1BUF,sub_a+1
	bsf		SSP1CON2,ACKEN				; master acknowledge
	rcall	WaitMSSP

	bsf		SSP1CON2, RCEN				; enable receive mode
	rcall	WaitMSSP
	movff	SSP1BUF,sub_a+0
	bsf		SSP1CON2,PEN				; stop condition
	rcall	WaitMSSP

	btfsc	gauge_status_byte,0			; UVLO event ?
	rcall	lt2942_init_again			; YES

	MOVII	sub_a,battery_accumulated_charge	; save raw value

	; Compute batt_percent
	; (charge-battery_offset)/365
	MOVII	battery_offset,sub_b
	call	subU16						; sub_c = sub_a - sub_b (with signed values)
	clrf	batt_percent				; set to zero
	btfsc	neg_flag					; result negative?
	bra		lt2942_set_to_zero_percent	; YES - keep LT2942 at zero percent and return

	; > zero, set batt_percent properly
	MOVII	sub_c,xA
	MOVII	battery_capacity,xB
	call	div16x16					; xC = xA / xB with xA as remainder
	movff	xC+0,batt_percent
	return

lt2942_set_to_zero_percent:
	clrf	i2c_temp1
	movlw	0x02						; point to accumulated charge registers
	rcall	I2C_TX_GAUGE
	movff	battery_offset+1,SSP1BUF
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	movff	battery_offset+0,SSP1BUF
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP					; ... and return

	global	lt2942_charge_done
lt2942_charge_done:						; reset accumulating registers to 0xFFFF
	clrf	i2c_temp1
	movlw	0x02						; point to accumulated charge registers
	rcall	I2C_TX_GAUGE
	setf	SSP1BUF						; data byte
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	setf	SSP1BUF						; data byte
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP					; ... and return

I2C_TX_GAUGE:							; send a byte to the LT2942 gauge IC
	movwf	i2c_temp2					; data byte
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	b'11001000'					; address byte + Write bit
	movwf	SSP1BUF						; control byte
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	movf	i2c_temp2,W
	bra		I2C_TX						; ... and return

I2C_RX_GAUGE:
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	b'11001001'					; address byte + Read bit
	movwf	SSP1BUF						; control byte
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	bsf		SSP1CON2, RCEN				; enable receive mode
	bra		WaitMSSP					; ... and return


;=============================================================================
; Transmitter Functions
;
 IFDEF _rx_functions

	global	I2C_probe_OSTC_rx
I2C_probe_OSTC_rx:
	bcf		ostc_rx_present				; no TR module by default
	clrf	WREG						; bank-safe set to zero of ...
	movff	WREG,rx_firmware_cur_major	; ... current TR module firmware, major
	movff	WREG,rx_firmware_cur_minor	; ... current TR module firmware, minor
	movlw	.5							; max number of tries for detecting a TR module
	movwf	hy							; initialize counter for tries
I2C_probe_OSTC_rx_1:
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x50						; address byte + write bit
	movwf	SSP1BUF						; control byte
	rcall	WaitMSSP
	btfss	SSP1CON2,ACKSTAT			; ACK received?
	bsf		ostc_rx_present				; YES - TR module detected
	bsf		SSP1CON2,PEN				; stop condition
	rcall	WaitMSSP
	btfss	ostc_rx_present				; was a TR module detected?
	return								; NO  - done
	WAITMS	.1
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x50						; address byte + write bit
	movwf	SSP1BUF						; control byte
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	movlw	0x1B
	movwf	SSP1BUF						; data byte (get firmware)
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	bsf		SSP1CON2,PEN				; stop condition
	rcall	WaitMSSP
	WAITMS	.1
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x51						; address byte + Read bit
	movwf	SSP1BUF						; control byte
	rcall	WaitMSSP
	bsf		SSP1CON2,RCEN				; enable receive mode
	rcall	WaitMSSP
	movff	SSP1BUF,rx_firmware_cur_major ; store as firmware version, major
	bsf		SSP1CON2,ACKEN				  ; master acknowledge
	rcall	WaitMSSP

	; last byte in read from RX circuity always with a NACK!
	bsf		SSP1CON2,RCEN				; enable receive mode
	rcall	WaitMSSP
	movff	SSP1BUF,rx_firmware_cur_minor ; store as firmware version, minor
	bsf		SSP1CON2,ACKDT
	bsf		SSP1CON2,ACKEN				; master NOT acknowledge
	rcall	WaitMSSP
	bcf		SSP1CON2,ACKDT				; reset ACKDT flag
	bsf		SSP1CON2,PEN				; stop condition
	rcall	WaitMSSP

	; wait for TR module becoming ready
	movff	rx_firmware_cur_minor,i2c_temp1	; copy firmware version to bank common, minor
	movlw	.147							; code for not ready, minor
	cpfseq	i2c_temp1						; equal?
	bra		I2C_probe_OSTC_rx_2				; NO  - TR module ready
	movff	rx_firmware_cur_major,i2c_temp1 ; YES - copy firmware version to bank common, major
	movlw	.27								;     - code for not ready, major
	cpfseq	i2c_temp1						;     - equal?
	bra		I2C_probe_OSTC_rx_2				;       NO  - TR module ready
	bsf		active_reset_ostc_rx			;       YES - apply reset to TR module
	WAITMS	.5								;           - wait 5 ms
	bcf		active_reset_ostc_rx			;           - release reset
	WAITMS	.250							;           - wait for     250 ms
	WAITMS	.250							;           - wait another 250 ms
	clrf	i2c_temp1						;           - clean-up i2c_temp1
	decfsz	hy,F							;           - decrement counter for number of tries, became zero?
	bra		I2C_probe_OSTC_rx_1				;           - NO  - try again
	bcf		ostc_rx_present					;           - YES - something is wrong, flag TR module as not available
I2C_probe_OSTC_rx_2:
	clrf	i2c_temp1						; clean-up i2c_temp1
	return									; done


	global	I2C_get_tankdata
I2C_get_tankdata:
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x50						; address byte + write bit
	movwf	SSP1BUF						; control byte
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	movlw	0x1E						; read buffer2 (48 bytes)
	movwf	SSP1BUF						; data byte
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	bsf		SSP1CON2,PEN				; stop condition
	rcall	WaitMSSP
	WAITMS	.1
	; read 48 bytes
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x51						; address byte + read bit
	movwf	SSP1BUF						; control byte
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	movlw	.47							; 47 with ACK + 1 w/o ACK
	movwf	i2c_temp2
	lfsr	FSR2,rx_buffer
I2C_get_tankdata_loop_read:
	bsf		SSP1CON2, RCEN				; enable receive mode
	rcall	WaitMSSP
	movff	SSP1BUF,POSTINC2
	bcf		SSP1CON2,ACKDT
	bsf		SSP1CON2,ACKEN				; master acknowledge
	rcall	WaitMSSP
	decfsz	i2c_temp2,F
	bra		I2C_get_tankdata_loop_read
	; 1 w/o ACK
	bsf		SSP1CON2, RCEN				; enable receive mode
	rcall	WaitMSSP
	movff	SSP1BUF,POSTINC2
	bsf		SSP1CON2,ACKDT
	bsf		SSP1CON2,ACKEN				; master NOT acknowledge
	rcall	WaitMSSP
	bcf		SSP1CON2,ACKDT				; reset ACKDT flag
	bsf		SSP1CON2,PEN				; stop condition
	bra		WaitMSSP					; ... and return


	global	I2C_update_OSTC_rx
I2C_update_OSTC_rx:						; transfer 64 byte to RX co-processor
	; setup for write
	bcf		i2c_error_flag				; clear error flag
	lfsr	FSR2,buffer					; initialize pointer to send buffer used for verify
	movlw	.64							; initialize loop counter: 64 byte with ACK
	movwf	i2c_temp2					; ...
	; address write
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x50						; address byte + write bit
	movwf	SSP1BUF						; control byte
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	; write 64 bytes
I2C_update_OSTC_loop:
	TBLRD*+								; read a byte from program memory
	movff	TABLAT,POSTINC2				; copy to send buffer
	movff	TABLAT,SSP1BUF				; copy to I2C data buffer
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	decfsz	i2c_temp2,F					;decrement loop counter, became zero?
	bra		I2C_update_OSTC_loop		; NO  - loop
	bsf		SSP1CON2,PEN				; YES - stop condition
	rcall	WaitMSSP					;     - wait for stop condition done
	WAITMS	.1							;     - wait another 1 ms
	; setup for read-back
	lfsr	FSR2,buffer					; reset pointer to begin of send buffer
	movlw	.63							; initialize loop counter: 63 byte with ACK + 1 w/o ACK
	movwf	i2c_temp2
	; address read-back
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x51						; address byte + read bit
	movwf	SSP1BUF						; control byte
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	; read-back 64 bytes
I2C_update_OSTC_loop_read:
	bsf		SSP1CON2,RCEN				; enable receive mode
	rcall	WaitMSSP
	movf	SSP1BUF,W
	cpfseq	POSTINC2					; compare read-back byte with sent byte, equal?
	bsf		i2c_error_flag				; NO  - not equal, set error flag
	bcf		SSP1CON2,ACKDT
	bsf		SSP1CON2,ACKEN				; master acknowledge
	rcall	WaitMSSP
	decfsz	i2c_temp2,F					; decrement loop counter, became zero?
	bra		I2C_update_OSTC_loop_read	; NO  - loop
	; 1 w/o ACK
	bsf		SSP1CON2, RCEN				; YES - enable receive mode
	rcall	WaitMSSP					;     -
	movf	SSP1BUF,W					;     - get 64th byte
	cpfseq	POSTINC2					;     - compare read-back byte with sent byte, equal?
	bsf		i2c_error_flag				;       NO  - not equal, set error flag
	bsf		SSP1CON2,ACKDT				;     -
	bsf		SSP1CON2,ACKEN				;     - master NOT acknowledge
	rcall	WaitMSSP					;     -
	bcf		SSP1CON2,ACKDT				;     - reset ACKDT flag
	; stop
	bsf		SSP1CON2,PEN				; stop condition
	rcall	WaitMSSP
	WAITMS	.1
	; address commit
	bsf		SSP1CON2,SEN				; start condition
	rcall	WaitMSSP
	movlw	0x50						; address byte + write bit
	movwf	SSP1BUF						; control byte
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	movlw	0x1F						; write command
	movwf	SSP1BUF						; data byte
	rcall	WaitMSSP
	rcall	I2C_WaitforACK
	bsf		SSP1CON2,PEN				; stop condition
	rcall	WaitMSSP
	WAITMS	.5							; required waiting time
	; error check
	btfss	i2c_error_flag				; did an error occur?
	retlw	.0							; NO  - data transfered successfully
	retlw	.255						; YES - error in data transfer occurred

 ENDIF

;=============================================================================

	END