;***************************************************************************
;Dbt_kybd_echo_mstr
;This program exercises I2C bus in several ways:
;  sends an opening message to the hand controller
;  on interrupt receives a digit from the Hand Controller, 
;  stores it, and echoes it back. 
;Routines can be embedded into any program to provide user control of AGV.
;TJW 20.7.05					Tested and working 21.7.05
;***************************************************************************
;Clock is 4MHz
;
	list 	p=16F873A
	#include p16f873A.inc
;
;Set Configuration Word: crystal oscillator HS, WDT off, 
;     power-up timer on, code protect off, LV Program off.
	__CONFIG _HS_OSC & _WDT_OFF & _PWRTE_ON & _LVP_OFF
;
;Specify RAM
delcntr1 	equ	20	;used in delay5 & delayADC SRs
delcntr2 	equ	21
temp	 	equ	22  	;a temp location, to be used only in consecutive instructions
I2C_RX_word 	equ	23	;holds most recent I2C word recd
I2C_add	 	equ	24  	;holds address used in I2C message
I2C_TX_word 	equ	25	;holds word to be transmitted on I2C
pointer		equ 	26  	;table pointer for strings to lcd 
	
;Specify some port bits
;For Port A
mot_en_rt 	equ	2
mot_en_left	equ	5

;For Port B
sounder		equ	1	;piezo electric sounder
us_rt		equ 	6	;right microswitch
us_left		equ 	7	;left microswitch

;For PortC
mot_left 	equ 	1	;left motor direction bit, 1=forward
mot_rt 		equ 	2	;right motor direction bit
led_rt		equ 	5	;diagnostic led
led_left	equ 	6	;diagnostic led
mode		equ	7	;mode switch

	org 	00
	goto 	start
;
	org 	04
	goto Interrupt_SR
;Initialise SFRs in Bank 1
start	bcf	status,rp1
	bsf	status,rp0 	;select memory bank 1
	movlw	B'00001011'	;set port A bits according to their function
	movwf	trisa		
	movlw	B'11001001'	;also port B bits
	movwf	trisb		
	movlw	B'10011000'	;set port C bits, I2C bits are both set as ip
	movwf	trisc		
	movlw	B'01011100' 
	movwf	adcon1		;select port A bits 0,1,3 for analog input
	movlw	07 		;set up 125kHz baud rate
	movwf	sspadd
;Initialise SFRs in Bank 0
	bcf	status,rp0
	movlw	B'00101000'	;SSPCON1:MSSP on, I2C Master 
	movwf	sspcon
;
;Switch all outputs off
	movlw 	00
	movwf 	porta
	movwf 	portb
	movwf 	portc
;diagnostic, flash leds
	bsf 	portc,6
	bsf 	portc,5
	call 	delay500
	bcf 	portc,6
	bcf 	portc,5
	call 	delay500	;extra delay
;Send opening string
	clrf	pointer
	movlw	0a4		;send slave address, R/W is write 
	movwf	I2C_add
	call    I2C_send_add
loop_str1 movf	pointer,0
	call	table1
	movwf	I2C_TX_word
	sublw	0ff		;test and move on if end marker reached
	btfsc	status,z
	goto	string_end
	call	I2C_send_word
	incf	pointer,1
	call 	delay1		;give LCD time to write
	call 	delay1
	call 	delay1
	goto	loop_str1
string_end call I2C_send_stop
;Enable interrupts
	bcf	intcon,intf	;clear pending interrupts
	bsf	intcon,inte	;enable external interrupt
	bsf	intcon,gie
;Wait for interrupts from Hand Controller
loop 	goto	loop
;
;*******************************************************************
;ISR. On external interrupt, SSP reads byte from Hand Controller, 
;and echoes it back, ie two I2C messages.
;Received Byte stored in I2C_word for further action.
;*******************************************************************
Interrupt_SR
	bsf 	portc,6		;diagnostic
;Start new I2C message, requesting word from slave.
	movlw	0a5		;this is slave address, R/W is read 
	movwf	I2C_add
	call    I2C_send_add
;now wait for byte to come in
	call 	I2C_rec_word
	call 	I2C_send_stop
	bcf	status,rp0
	call 	delay20u
;Now echo byte - start new message
	movlw	0a4		;this is slave address, R/W is write
	movwf	I2C_add
	call    I2C_send_add
;send the echoed character	
	movf 	I2C_RX_word,0 ;move received word to transmit store
	movwf 	I2C_TX_word 
	call	I2C_send_word
	call 	I2C_send_stop
	bcf	status,rp0
	bcf 	portc,6		;clear diag led
	bcf	intcon,intf
	retfie
	
;*******************************************************************
;SUBROUTINES
;*******************************************************************
;initiates I2C message, by sending the word found in I2C_add, which 
;must include R/W bit. Waits for all acknowledgement and completion 
;states. Leaves RAM in Bank 0.
I2C_send_add
	bsf	status,rp0
	bsf	sspcon2,sen	;force start bit
	btfsc	sspcon2,sen	;check for its completion
	goto	$-1
	bcf	status,rp0
	movf	I2C_add,0	;load address and data dirn bit
	movwf	sspbuf		;and send 
	bcf	pir1,sspif	;will test this soon
	bsf	status,rp0
	btfsc 	sspstat,bf ;test for write complete
	goto	$-1
	btfsc 	sspcon2,ackstat	;wait for 0 acknowledge bit 
	goto 	$-1
	bcf	status,rp0
	btfss	pir1,sspif ;test for int flag to show completion
	goto	$-1
	bcf	pir1,sspif
	return
;
;Receives (single) word from I2C bus, and stores in I2C_RX_word. Returns Ack of 1,
;signalling this is last byte. Leaves RAM in Bank 0
I2C_rec_word bsf status,rp0
	bsf	sspcon2,rcen	;set receive enable bit 
	btfss	sspstat,bf	;wait for buffer full
	goto 	$-1
	bcf	status,rp0	;read the data
	movf	sspbuf,0
	movwf	I2C_RX_word	;store it for use somewhere
	bcf	pir1,sspif	;preclear int flag, as we are about to use it
	bsf	status,rp0
	bsf	sspcon2,ackdt 	;set required acknowledge state, 1 as it's last byte
	bsf	sspcon2,acken	;and enable it
	bcf	status,rp0
	btfss	pir1,sspif	;use interrupt flag to test for end of ack
	goto	$-1
	bcf	status,rp0	
	return
;
;Sends word on I2C bus, and awaits acknowledgement. Leaves RAM in Bank 0.
I2C_send_word bcf status,rp0
	movf	I2C_TX_word,0	;get the word	
	movwf	sspbuf		;this starts the transfer
	bsf	status,rp0
	btfsc 	sspstat,r_w 	;test for write complete
	goto	$-1
	btfsc 	sspcon2,ackstat	;wait for 0 acknowledge bit 
	goto 	$-1
	bcf	status,rp0
	return
;
;Sends I2C stop bit, and awaits completion. Leaves RAM in Bank 0.
I2C_send_stop bsf status,rp0
	bsf	sspcon2,pen	;force stop bit. 
	btfss	sspstat,p 	;test for stop bit completion
	goto	$-1
	bcf 	status,rp0
	return
;
;introduces delay of 20us approx
delay20u  movlw D'5'		;5 cycles called, 
	;each taking 3us, plus call, return (2 ea), and 2 move insts
	;less one cycle lost when last goto is hopped
        movwf  	delcntr1
dela    decfsz 	delcntr1,1 	;3 inst cycles in this loop, ie 3us
        goto 	dela
        return
;
;introduces delay of 1ms approx
delay1  movlw 	D'250'		;250 cycles called, 
;			each taking 4us
        movwf  	delcntr1
del1    nop			;4 inst cycles in this loop, ie 4us
        decfsz 	delcntr1,1
        goto 	del1
        return
;
;10ms delay (approx)		;10 calls to delay1
delay10  movlw D'10'
        movwf  	delcntr2
del10    call 	delay1
        decfsz  delcntr2,1
        goto 	del10
        return  
;200ms delay (approx)		;200 calls to delay1
delay200  movlw D'200'
        movwf  	delcntr2
del2    call 	delay1
        decfsz  delcntr2,1
        goto 	del2
        return  
;
;500ms delay (approx)		;500 calls to delay1
delay500  movlw D'250'
        movwf  	delcntr2
del5    call 	delay1
	call 	delay1
        decfsz  delcntr2,1
        goto 	del5
        return  
;
;Character String Tables
Table1 addwf pcl,1
	retlw ' '
	retlw 'D'
	retlw 'e'
	retlw 'r'
	retlw 'b'
	retlw 'o'
	retlw 't'
	retlw 0ff
;
	end
