; ***************************************************************************************
; ***************************************************************************************
; *  FILE..........: fms.asm   								*
; *  AUTHOR........: Bruce Abbott							*
; *  DESCRIPTION...: Conversion of PPM-signal to RS232 for FMS R/C flight simulator	*
; *  CPU...........: PIC12F629/75 & 12C508/9						*
; *  CREDITS.......: This code is based on 'rc_rs232.asm' by Rasmus Geidnert, an	* 
; *                  improvement of an interface by Vitaly Puzrin.			*
; *											*
; *  HISTORY.......: DATE   VERSION			COMMENT				*
; *  ---------------------------------------------------------------------------------- *
; *		18.08.2003    1.0	Removed 16F84 support and added 12C675 support	*
; *					Fixed RS232 timing and tidied code		*	
; *											*
; ***************************************************************************************
; ***************************************************************************************
;
; Anatomy of a PPM (Pulse Position Modulation) Frame:
;
; Each frame consists of 4 to 8 channels, followed by a sync gap. The distance 
; between each leading or trailing edge is 1 to 2mS (midpoint is 1.5mS). The 
; sync gap must be greater than 2.2mS. Frame length is approximately 20mS.
;
; Input signal looks like this (Polarity may be inverted): 
;
;            |<-------------------- 1 frame ---------------------->|
;             _	       _       _       _       _                    _
; ___________| |______| |_____| |_____| |_____| |__ // ____________| |_____
;   sync gap     ch1      ch2     ch3     ch4      etc.  sync gap      ch1
;
; One RS232 byte is sent after each pulse. The first byte is always
; 255. Subsequent bytes sent represent the width of each channel.
; Width is encoded as a number between 0 and 254, representing 0.7mS 
; to 2.2mS (+/-6uS). At center stick (1.5mS), the value sent is 127. 
;

                radix     dec

;#DEFINE __12C508	; enable one of these if processor is not otherwise  
;#DEFINE __12F675	; specified. In MPLAB use <Configure/Select Device>. 

; intosc = 1		; enable this if using internal RC oscillator

	ifdef	  __12F675
        PROCESSOR PIC12F675
        INCLUDE   <P12F675.inc>
	#DEFINE	  OUTPUT    GPIO,GP0	; rs232 serial data output pin 
	#DEFINE	  RAM_START 0x20 
	ifdef	  intosc
	__CONFIG  _MCLRE_OFF & _CP_OFF & _WDT_ON & _BODEN_ON & _INTRC_OSC_CLKOUT
	else	
	__CONFIG  _MCLRE_OFF & _CP_OFF & _WDT_ON & _BODEN_ON & _HS_OSC
	endif
	else
	PROCESSOR PIC12C508A
	include   <p12c508a.inc>
	#DEFINE	  OUTPUT    GPIO,0	; rs232 serial data output pin	
	#DEFINE	  RAM_START 0x07 	
	ifdef	  intosc	 
	__CONFIG  _MCLRE_OFF & _CP_OFF & _WDT_ON & _INTRC_OSC
	else
	__CONFIG  _MCLRE_OFF & _CP_OFF & _WDT_ON & _XT_OSC
	endif
	endif

;Variables
	CBLOCK 	RAM_START		; First user RAM
		Time			; Used to measure PPM width	
		Delay			; uS time delay counter
		Bit_Count		; used to count bits sent with RS 232
	ENDC

;------------------------------ code starts here -----------------------------------

	org	0
	goto	start

	org	8		
	dt	"--------"
	ifdef	__12C508
	dt	"-FMS508-"
	else
	dt	"-FMS675-"
	endif
	dt	"--V1.0--"
	dt	"--------"

start:
	ifdef	__12F675
	bsf	STATUS,RP0	; Register bank 1
	call	0x3ff		; get OSCCAL value
	endif
	ifdef	intosc
	movwf	OSCCAL		; Calibrate internal oscilator
	endif
	ifdef	__12F675
	clrf	ANSEL		; Analog I/O off
	bcf	STATUS,RP0      ; register bank 0
	movlw	b'00000111'      
	movwf	CMCON		; Comparator off
	endif

	clrwdt			; clear watchdog timer

; Weak pullups enabled. Tmr0 input GP2, negative edge.  wdt prescale = 16:1
 
	movlw	1<<T0CS|1<<T0SE|1<<PSA|b'010'

	option			; load option register

	movlw	0xFF		; Set all I/O pins high
	movwf	GPIO
	movlw	b'00101110'	; set GP0 and GP4 to Output
	tris	GPIO

;------------------------------------------------------------------------

	clrwdt				; Clear watchdog
	clrf	TMR0			; Clear pulse edge detector

Start_next_Frame
	movlw	0xFF
	movwf	Time			; first serial byte will be 0xFF

Wait_first_pulse
	movf	TMR0, W			
	btfsc	STATUS, Z		; if TMR0 > 0 then pulse detected
	goto	Wait_first_pulse	

; send byte to RS232 port at 19,200 baud (52uS per bit). Timing is based on
; a 4MHz CPU clock, which must be accurate to +/-2%. A quartz crystal or 
; ceramic resonator is recommended.   

Send_RS232	
	BCF	OUTPUT			; begin START bit
	MOVLW	8			 
	MOVWF	Bit_Count		; 8 DATA bits to do
	MOVLW	(52-7)/3		  
	MOVWF	Delay		
Del_srt	DECFSZ	Delay, F		; START bit time
	GOTO	Del_srt			 
	nop				
New_data
	RRF	Time, F			; get next bit
	BTFSC	STATUS, C		
	BSF	OUTPUT			; begin DATA bit = 1
	BTFSS	STATUS, C		
	BCF	OUTPUT			;                = 0
	MOVLW	(52-10)/3		  
	MOVWF	Delay		
Del_db	DECFSZ	Delay, F		; DATA bit time
	GOTO	Del_db			 
	nop				
	DECFSZ	Bit_Count, F		; next DATA bit
	GOTO	New_data		
	nop
	nop
	nop
	BSF	OUTPUT			; begin STOP bit
	movlw	(52-4)/3		
	movwf	Delay		
Del_stp	Decfsz	Delay, F		; STOP bit time
	goto	Del_stp			 

	movlw	174/3		
	movwf	Delay		
Del_ppm	Decfsz	Delay, F		; ppm time padding 
	goto	Del_ppm	

	clrwdt				; Clear watchdog 
	clrf	Time			; Clear PPM timer
	clrf	TMR0			; Clear pulse edge detector

; Elapsed time from last pulse to here is:
;  2 + 520uS + 174uS + 3 = 698uS 
;     (RS232) (delay)   

; measure time to next pulse, with 6uS resolution. 
; value =   0      127      254    
;  uS   =  698    1500     2222                       
;
Measure
	movf	TMR0, W			
	btfss	STATUS, Z		; pulse detected?
	goto	Send_RS232		; yes: send measured Time
	incfsz	Time, F			; Time+1, overflow? 
	goto	Measure			; no: continue measuring PPM width
	goto	Start_next_Frame	; end of frame, or no signal


; This subroutine is pre-programmed by Microchip. If your chip's flash memory
; has been erased, you can enter the calibration value here. Do NOT allow the 
; device programmer to overwrite this location, unless you have already saved 
; the original value!

	ifdef	__12F675
	org	0x3ff			
	retlw	0x90		; Calibration value for OSCAL				
	endif				
					
	end
