;*******************************************************************
;Dbt_kybd_echo_async
;This program receives a digit from the Hand Controller on the I2C 
;bus, stores it, sends it through the asynchronous comms link,
;and echoes it back to the I2C. Each I2C message one byte only.
;Routines can be adapted and embedded into any Derbot program.			
;TJW 18.7.05							Tested and working 19.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
;***added for async demo
async_TX_word equ 26
async_RX_word equ 27
	
;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	09 			;set up 100k baud rate
	movwf	sspadd
;Initialise SFRs in Bank 0
	bcf		status,rp0
	movlw	B'00101000'	;SSPCON1:MSSP on, I2C Master 
	movwf	sspcon
;***Initialise USART in both banks
	movlw	B'10010000' ;set up async channel: port is on, 8-bit transfer,
	movwf	rcsta		;continuous receiving, no address detect
	bsf		status,rp0
	movlw	B'00100100'	;set up async channel:transmit enabled, 8-bit, 
	movwf	txsta		;high speed baud rate
	movlw	04			;baud rate of 50000
	movwf	spbrg
	bcf		status,rp0
;
;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
;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, 
;sends it out on USART, receives it back through USART
;and echoes it back to keypad, 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
;*****extra section inserted
;send out via async comm channel
	bcf 	pir1,rcif	;preclear receive interrupt flag
	movf	I2C_RX_word,0
	movwf	txreg
	btfss	pir1,rcif	;test for receive interrupt flag, indicating receive complete
	goto	$-1
	movf	rcreg,0
	movwf	async_RX_word ;get and store received word	
;*****end of extra section
;Now echo byte to I2C - 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 	async_RX_word ;move **async** 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.
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 (final) word from I2C bus, and stores in I2C_RX_word. Returns Ack of 1.
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		status,rp0
	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
	return
;
;Sends word on I2C bus, and awaits acknowledgement
I2C_send_word bcf status,rp0
	movf	I2C_RX_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
	return
;
;Sends I2C stop bit, and awaits completion
I2C_send_stop bsf status,rp0
	bsf		sspcon2,pen		;force stop bit. 
	btfss	sspstat,p 		;test for stop bit completion
	goto	$-1
;
;introduces delay of 20us approx
delay20u  movlw 	D'5'	;5 cycles called, 
;			each cycle 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  
;
		end
