; VOIP7a.asm
; Uses 18F2620 chip @ 20.000 MHz clk

; Changes added since VOIP7.asm :
; LED driver routines added ( indicate TX and RX audio )
; UART disable input added
; 4 second wake-up delay added

; Changes added since VOIP6.asm : 
; 1. Ethernet UDP receive routine added ( interrupt driven )

; Pin assignments :

; TX  ( pin 17 )  : RS232 output to LanTronix module
; RX  ( pin 18 )  : RS232 input from LanTronix module
; AN0 ( pin 2 )   : ADC input
; CCP1( pin 13 )  : PWM1 output
; RB5 ( pin 26 )  : UART TX disable input ( ground = disabled)
; RB6 ( pin 27 )  : RX LED output
; RB7 ( pin 28 )  : TX LED output

; Pin definitions

#DEFINE		TX_LED_PIN		PORTB,7	;TX LED output pin
#DEFINE		RX_LED_PIN		PORTB,6	;RX LED output pin
#DEFINE		TX_ENABLE		PORTB,5	;gnd = UART TX disabled

; Program constants

BAUD_HI		equ	0x00		;115.2K baud BRG value, hi byte ( brgh = 1, 20.000 MHz )
BAUD_LO		equ	0x2a		;115.2K baud BRG value, lo byte ( brgh = 1, 20.000 MHz )

; These TMR0 bytes do NOT use PRESCALER ( 125 usec / interrupt = 8K sample rate )

TIM_HI		equ	0xfd		;125 usec TMR0 hi byte ( complement of 625 )
TIM_LO		equ	0x8f		;lo byte

; Program variables

	cblock	0x800		;start of variable RAM

	W_T				;temp W register ( interrupt routine )
 	STATUS_T 			;temp STATUS register ( interrupt routine )

	PLAY_BYTE

	TX_LED_TIME		;64 millisec timer for TX LED
	RX_LED_TIME		;64 millisec timer for RX LED

	NDX1				;general purpose loop index
	NDX2
	NDX3

; End of variables

	endc

;-------------------------------------------------------------------
;
;
;                ASSEMBLER DIRECTIVES AND CONFIG BYTE
;
;
;-------------------------------------------------------------------


		processor	18F2620		; identify the device

		#include 	<P18f2620.inc> 	; register + bit definitions

; Configuration bits
; Code protect = ON

; CONFIG1H	
		CONFIG	IESO=OFF
		CONFIG	FCMEN=OFF
		CONFIG	OSC=HS
; CONFIG2L	
		CONFIG	BORV=3
		CONFIG	BOREN=OFF
		CONFIG	PWRT=ON
; CONFIG2H
		CONFIG	WDTPS=32768
		CONFIG	WDT=OFF
; CONFIG3H
		CONFIG	MCLRE=OFF
		CONFIG	LPT1OSC=OFF
		CONFIG	PBADEN=OFF
		CONFIG	CCP2MX=PORTC
; CONFIG4L
		CONFIG	DEBUG=OFF
		CONFIG	XINST=OFF
		CONFIG	LVP=OFF
		CONFIG	STVREN=OFF
; CONFIG5L
		CONFIG	CP0=ON
		CONFIG	CP1=ON
		CONFIG	CP2=ON
		CONFIG	CP3=ON
; CONFIG5H
		CONFIG	CPD=ON
		CONFIG	CPB=ON
; CONFIG6L
		CONFIG	WRT0=ON
		CONFIG	WRT1=ON
		CONFIG	WRT2=ON
		CONFIG	WRT3=ON
; CONFIG6H
		CONFIG	WRTB=ON
		CONFIG	WRTC=ON
		CONFIG	WRTD=ON
; CONFIG7L	
		CONFIG	EBTR0=OFF
		CONFIG	EBTR1=OFF
		CONFIG	EBTR2=OFF
		CONFIG	EBTR3=OFF
; CONFIG7H
		CONFIG	EBTRB=OFF


;------------------------------------------------------------------
;
;
;                   EXECUTABLE CODE BEGINS HERE
;
;
;-------------------------------------------------------------------

;-------------------------------------------------------------------
;
;
; Coldstart routine
;
;
;-------------------------------------------------------------------

		org		0x00		;coldstart origin

CSTART	goto		MSTART	;jump to main start routine

;-------------------------------------------------------------------
;
;
; Interrupt routine
;
;
;-------------------------------------------------------------------

		org		0x08		;interrupt vector address

INT

; Save registers

		movwf 	W_T,1 		
		movf	 	STATUS,W 	
		movwf 	STATUS_T,1 	

; Identify interrupt source

		btfsc		PIR1,RCIF
		goto		INT_RX

; Interrupt came from TMR0 
; 125 usec per interrupt = 8K samples/sec

INT_TMR0

; Transmit routine begins here ( audio sent to peer via UDP )

; Get the most recent audio input sample
; Get the ADC result byte from previous measurement
; Ignore bottom 2 bits ( use top 8 bits only )

; Check if result = 0xff ( not allowed : XPORT doesn't like it )
; Decrement result if true

		movf		ADRESH,W		;get top 8 bits
		sublw		0xff
		btfsc		STATUS,Z
		decf		ADRESH,F

; Check if UART_TX is enabled ( pin 26 = high )
; Do not transmit byte if pin 26 = grounded

		btfss		TX_ENABLE
		goto		INT_TMR0_2

; UART TX is enabled
; Send the byte via RS232 to XPORT module
; ( transmit the byte to peer via UDP )

		movf		ADRESH,W
		movwf		TXREG

; Start a new ADC measurement
; Result will be read in next TMR0 interrupt cycle

INT_TMR0_2
		bsf 		ADCON0,1 		;start

; Transmit LED indicator routine
; Check if TX audio is present ( ADC reading > 192 decimal )
; If true, turn on TX LED for 64 millisec

		movlw		0xc0
		cpfsgt	ADRESH,0
		goto		INT_TX_LED

; Transmit audio is present
; Turn on TX LED for 64 millisec

		movlw		0xff
		movwf		TX_LED_TIME,1
		bsf		TX_LED_PIN

; Check if time = now to make TX LED = off

INT_TX_LED
		dcfsnz	TX_LED_TIME,F,1
		bcf		TX_LED_PIN

; End of transmit routine
; Playback routine begins here ( audio output to speaker )

; Get the next playback byte from 2K RAM buffer

		movf		INDF0,W		;get the next playback byte
		movwf		PLAY_BYTE,1

; Receive LED indicator routine
; Check if RX audio is present ( PLAY_BYTE reading > 192 decimal )
; If true, turn on RX LED for 64 millisec

		movlw		0xc0
		cpfsgt	PLAY_BYTE,1
		goto		INT_RX_LED

; Receive audio is present
; Turn on RX LED for 64 millisec

		movlw		0xff
		movwf		RX_LED_TIME,1
		bsf		RX_LED_PIN

; Check if time = now to make RX LED = off

INT_RX_LED
		dcfsnz	RX_LED_TIME,F,1
		bcf		RX_LED_PIN

; Send the PLAY_BYTE to PWM module
; bottom 2 bits go to CCP1CON register ( bits 4 and 5 )

		bcf		CCP1CON,4		;assume bits = 0
		bcf		CCP1CON,5
		btfsc		PLAY_BYTE,0,1	
		bsf		CCP1CON,4		;if B0 = 1
		btfsc		PLAY_BYTE,1,1
		bsf		CCP1CON,5		;if B1 = 1

; Top 6 bits go to CCPR1L ( after rotating 2 bit positions )

		rrcf		PLAY_BYTE,F,1
		rrcf		PLAY_BYTE,F,1
		bcf		PLAY_BYTE,6,1	;throw away top 2 rotated bits
		bcf		PLAY_BYTE,7,1
		movf		PLAY_BYTE,W,1
		movwf		CCPR1L

; Clear this byte and increment pointer

		movlw		0x80
		movwf		POSTINC0

; Check if playback pointer has reached end of buffer
; If true, reset playback pointer to start of buffer

		btfsc		FSR0H,3		;address = 0x800 ?
		clrf		FSR0H			;clear FSR0 if true

; Check if record pointer ( hi byte ) = playback pointer ( hi byte )
; If true, buffer time between the two pointers = only 32 millisec
; ( too small to be safe, should be bigger )
; Therefore adjust the record pointer ( hi byte ) to make buffer time = 125 millisec

		movf		FSR0H,W
		subwf		FSR1H,W
		btfss		STATUS,Z		;gap time = 32 millisec ?
		goto		INT_TMR0_DONE	;jump if not true

; Gap time = 32 millisec ( too small )
; Icrease gap to 125 millisec by adjusting the record pointer

		incf		FSR1H,F
		incf		FSR1H,F
		incf		FSR1H,F
		incf		FSR1H,F
		bcf		FSR1H,3		;overflow precaution 
		
; Done with transmit / playback routines
; Reload TMR0 

INT_TMR0_DONE
		movlw		TIM_HI
		movwf		TMR0H
		movlw		TIM_LO
		movwf		TMR0L

; Clear TMR0 interrupt 
; Restore registers and return from interupt

		bcf		INTCON,T0IF

		movf		STATUS_T,W,1
		movwf		STATUS
		movf		W_T,W,1

		retfie

; Interrupt came from UART RX
; Get the byte and store it in audio buffer
; ( this clears the interrupt flag bit )

INT_RX
		movf		RCREG,W
		movwf		POSTINC1

; Check if record pointer has reached end of buffer
; If true, reset record pointer to start of buffer

		btfsc		FSR1H,3		;address = 0x800 ?
		clrf		FSR1H			;clear FSR1 if true

; Done with UART RX
; Restore registers and return from interrupt

		movf		STATUS_T,W,1
		movwf		STATUS
		movf		W_T,W,1

		retfie
 
;-------------------------------------------------------------------
;
;
; Mainstart routine
;
;
;-------------------------------------------------------------------

MSTART

; Config the ADC and Vref

		movlw		0x01		;ADCON0 byte
						;ADC = on
						;selected input = AN0
		movwf		ADCON0	

		movlw		0x0e		;ADCON1 byte
						;vref = vdd + vss
						;input AN0 enabled
		movwf		ADCON1

		movlw		0x3a		;ADCON2 byte	
						;result = left justify
						;sample time = 20 Tad
						;clk div = 32
		movwf		ADCON2

; Config the UART and BRG

		movlw		0x90		;RCSTA config byte
						;serial port = enabled
						;RX data = 8 bits
						;recieve = enabled
						;address detect = off
		movwf		RCSTA	

		movlw		0x24		;TXSTA byte, BRGH = 1
						;TX data = 8 bits
						;TX = enabled
						;mode = asynchronous
		movwf		TXSTA	
	
		movlw		0x48		;RX polarity = normal
						;TX polarity = normal
						;BRG16 = 1
		movwf		BAUDCON

		movlw		BAUD_HI	;baud rate hi byte
		movwf		SPBRGH
		movlw		BAUD_LO	;baud rate lo byte
		movwf		SPBRG

; Config TMR0

		bsf		T0CON,TMR0ON	;TMR0 on
		bcf		T0CON,T08BIT	;16 bit timer
		bcf		T0CON,T0CS		;use CPU/4 clk

		bsf		T0CON,PSA		;prescaler not used

		movlw		TIM_HI		;time delay
		movwf		TMR0H
		movlw		TIM_LO
		movwf		TMR0L

; Config CCP1 module for PWM mode
; Preset output at 50% duty cycle
; 8 bit resolution @ 20 MHz = 78.125 KHz

		bcf		TRISC,2		;pin 13 = output
		movlw		0x3f			;78.125 KHz PWM freq @ 20 MHz
		movwf		PR2
		movlw		0x20			;50% top 6 bits = 0x100000b
		movwf		CCPR1L
		movlw		0x0c			;50% bottom 2 bits = 0x00b
		movwf		CCP1CON
		movlw		0x04			;PRESCALER = 1:1, TMR2 = on
		movwf		T2CON	

; Config PORTB pins for LED outputs
; Enable weak pull-ups for UART_ENABLE input

		movlw		0x3f			;RB6 + RB7 = outputs
		movwf		TRISB
		bcf		INTCON2,7		;enable weak pull-ups

; Fill the RAM buffer with 0x80

		clrf		FSR0L
		clrf		FSR0H
		movlw		0x80

MSTART_2
		movwf		POSTINC0
		btfss		FSR0H,3		;2K done yet ?
		goto		MSTART_2		;loop if not done

; Init the buffer pointers

		clrf		FSR0L			;FSR0 = 0x0
		clrf		FSR0H
		movlw		0x04
		clrf		FSR1L			;FSR1 = 0x400
		movwf		FSR1H

; Make BSR = 0x800
; ( first 2K of RAM are used for audio buffer )
; ( program variables therefore must begin at 0x800 )

		movlb		0x08
				
; Wait about 2 more seconds before continuing
; ( give Lantronix module time to wake up )

		clrf		NDX1,1
		clrf		NDX2,1
		movlw		0x15
		movwf		NDX3,1

MS_WAIT
		decfsz	NDX1,F,1
		goto		MS_WAIT
		decfsz	NDX2,F,1
		goto		MS_WAIT
		decfsz	NDX3,F,1
		goto		MS_WAIT

; Enable interrupts and proceed

MSTART_3	
		bcf		INTCON,T0IF	;clear TMR0 interrupt bit
		bsf		INTCON,T0IE	;enable TMR0 interrupts

		movf		RCREG,W		;empty the UART RX buffer
		movf		RCREG,W
		movf		RCREG,W
		bcf		RCSTA,CREN		;clear any RX buffer overrun errors
		bsf		RCSTA,CREN		;and re-enable UART RX

		bsf		PIE1,RCIE		;enable UART RX interrupts
		bsf		INTCON,PEIE

		bsf		INTCON,GIE		;enable all interrupts

;-------------------------------------------------------------------
;
;
; EXEC routine
;
;
;-------------------------------------------------------------------

EXEC

; This program is 100% interrupt driven
; There is no "main program loop" 

		goto		EXEC		;done, repeat


;-------------------------------------------------------------------
;
;                   END OF EXECUTABLE CODE 
;
;-------------------------------------------------------------------

		end
