;**********************************************************
;*  fc005_compare.asm - CCP Compare tryout to generate a waveform.
;*  modified from fc004.asm - Event Logger 
;*
;*	It does not do anything except demo CCP compare mode.
;*
;*  Author: Doug Rice
;*  Copyright 2018 
;*
;*  Description:
;*
;*	Set up CCP for compare
;*  Turn on TMR1 to increment, do not touch it once running to ensure stable count.
;*  if you start and stop TMR1 and do TMR1 += constant, 
;*  the prescaler tend to be reset on write and add jitter.
;*  
;*  the CCP compare reduces processing jitter.
;*
;*  Toggle the CCP compare mode to toggle between SET and CLEAR of the CCP pin. 
;*
;*  If you use the prescale.
;*  
;*  You have to set CCPR1 so that TMR1 has to catch up.
;*
;*  CCPR1 += TMR1ticksToNextMatch
;*
;*	In service routing,  increment CCPR1 by different amounts for Hi and Low 
;*  CCPR1 has to be set to ahead or TMR1 so TMR1 catches up 
;*
;*  To see the software jitter, toggle another output in the service routine so that the software jitter can be seen.
;*
;*  On first compare copy TMR1 to CCPR1 and add increment.
;*
;*
;*  ideas:-
;*  with CCP pin set to output, capture TMR1 by changing the mode to capture and changing pin level.
;*  Not sure what happens if pin is output and CCP is in Capture mode - it triggers a capture.
;*  convert to compare mode to set CCP pin.
;*  Now Add timeout to CCP
;*  Let it time out
;*  convert to compare mode to CLEAR CCP pin.
;*  Now Add next timeout to CCP
;*  Let it time out
  
;**********************************************************
;*
;*  Copyright 2018 Douglas Rice
;*
;*  http://www.dougrice.plus.com/hp/freqCounter/freqCounter.html
;*
;**********************************************************
;*
;* Modify this to use compare.
;*
;* init
;*   TMR1 started
;*   CCP set up for compare.
;*
;* while(1){
;*
;*   wait for TMRR1 == CCPH
;*   toggle mode so output toggles.
;*   increment CCPR1H so it is ahead of TMR1. TMR1 catches up and in ISR increment CCPR1 by required timeout.
;* 
;*   Note: use modulo 2^16 in TMR1 and CCPH to keep TMR1 < CCPH 
;*}
;*
;*
;* 12F683 pins
;	1 U 8
;	2   7
;	3   6
;	4   5

;Pins for 12F683
;1	VDD
;2	GP5/TICKI/OSC1/CLKIN
;2	GP4/AN3/TIG/OSC2/CLKOUT
;4	GP3/MCLR/VPP
;
;8	VSS
;7	GP0/AN0/CIN+/ICSPDAT/ULPWU
;6	GP1/AN1/CIN-/VREF/ICSPCLK
;5	GP2/AN2/T0CKI/INT/COUT/CCP1
;*
;
;**********************************************************
;--------------------------------------------------------------------------
; Sec 1.0	Build support for 12F683
;-------------------------------------------------------------------------
; uncomment this below or define it on the command line
;
#define using12F683

;configure the GPIO bits ( not device pin )
#define	RS232pin 4
#define	CCPpin 	 2
#define	swCCPpin 5

#ifdef using12F683
	list p=12f683
	; Include file, change directory if needed
	include "p12f683.inc"
	__idlocs  0xF683	

; NOTE:- 12F683 has failsafe clock so can run off internal clock or external clock.
; MORE WORK IS REQUIRED TO SET THIS UP.
; Need to set up OSCON to select external oscillator. 
; Using internal 4Mhz Osc if FCMEM_ is not diabled explicitly.	
;
;_FCMEN_ON                    EQU     H'3FFF'
;_FCMEN_OFF                   EQU     H'37FF'

	__config  _PWRTE_ON & _WDT_OFF & _BOD_OFF & _MCLRE_OFF & _INTRC_OSC_NOCLKOUT
;	__config  _PWRTE_ON & _WDT_OFF & _BOD_OFF & _MCLRE_OFF & _INTRC_OSC_CLKOUT

; Using 10Mhz External clock with 
;	__config  _PWRTE_ON & _WDT_OFF & _BOD_OFF & _MCLRE_OFF & _EC_OSC & _FCMEN_OFF & _IESO_OFF

; Using 10Mhz  XT OSC with External clock, works   Set 10Mhz to 9.4 Mhz then 10.3Mhz and serial was readable. 
;	__config  _PWRTE_ON & _WDT_OFF & _BOD_OFF & _MCLRE_OFF & _XT_OSC & _FCMEN_OFF & _IESO_OFF

; Using 10Mhz  HS OSC with External clock, did not work with sine wave output.  
;	__config  _PWRTE_ON & _WDT_OFF & _BOD_OFF & _MCLRE_OFF & _HS_OSC & _FCMEN_OFF & _IESO_OFF

; Using 10Mhz  LP OSC with External clock, works 4Vpp on scope  - NOT DESIGNED FOR 10MHz
;	__config  _PWRTE_ON & _WDT_OFF & _BOD_OFF & _MCLRE_OFF & _LP_OSC & _FCMEN_OFF & _IESO_OFF

;
; 12F683 has extra CMCON1 for TMR1 sync
;
#define	CMCON CMCON0
#endif

#ifdef using16F690

; TBD 

#endif
;--------------------------------------------------------------------------
; Sec 1.2	Variables
;----1---------------------------------------------------------------------

	cblock 	0x20
	  ; variables used for context saving 
	  w_temp
	  status_temp

	  SRtxTemp
	  SRtxCnt
	  SRout
	  SRdel

    compareInc
	endc

;--------------------------------------------------------------------------
; Sec 1.4	MACRO for tables straddling memory page
;-------------------------------------------------------------------------

TEST_STRADDLE	MACRO	START
	if high( $ ) != high( START ) 
	    Error "Table straddles Page Boundary "
	endif
	endm

	; Start at the reset vector
	org	0x000
	goto	Init



;--------------------------------------------------------------------------
; Sec 2.0	Code
;-------------------------------------------------------------------------

Init

	org	0x010

#ifdef using12F683
	nop
;TBD
#endif
	
	movlw	0
	movwf	GPIO

	banksel	TRISIO 
	;make CCP1 pin output otherwise it does not work.
	;#define	RS232pin 4
	;#define	CCPpin 	 2
	;#define	swCCPpin 1

    ; set upoutputs
	movlw	~( 1<<RS232pin | 1 << CCPpin | 1 << swCCPpin )
	MOVWF	TRISIO		; Set output high to start Measurement

	; Configure the Comparator to low power
	; for 12F683 #define	CMCON CMCON0
	banksel  CMCON
	movlw	B'00010111'	;
	movwf	CMCON

	; turn off A to D converter
	banksel  ADCON0
	movlw	B'00000000'	;
	movwf	ADCON0

	banksel  ANSEL
	movlw	B'00000000'	;Fosc/8, A/D disabled
	movwf	ANSEL

	; Set up the comparator Vref
	banksel  VRCON
	movlw	0 << VREN | 0<<VRR | 5	; level = Vdd * 4 / 24 = 1.0 Volts if Vdd is 5 volts.
	movwf	VRCON

;;;;;;;;;;;;;;;;;;;

InitCCP
;
; set up TMR1 and CCP
;
;

	banksel	T1CON
	; set up but do not turn on
	movlw	0 << T1GINV | 0 << T1GE  | 0 << T1CKPS0 | 0 << T1OSCEN | 0 << NOT_T1SYNC | 0 << TMR1CS | 0 << TMR1ON
    movwf	T1CON
	; set up but and now turn on.
	movlw	0 << T1GINV | 0 << T1GE  | 0 << T1CKPS0 | 0 << T1OSCEN | 0 << NOT_T1SYNC | 0 << TMR1CS | 1 << TMR1ON
    movwf	T1CON

;
; set up CCP to capture Signal and capture Period.
; set up CCP to compare mode. 
;
;bit 3-0 CCP1M<3:0>: CCP Mode Select bits
;0000 = Capture/Compare/PWM off (resets CCP module)
;0001 = Unused (reserved)
;0010 = Unused (reserved)
;0011 = Unused (reserved)

;0100 = Capture mode, every falling edge
;0101 = Capture mode, every rising edge
;0110 = Capture mode, every 4th rising edge
;0111 = Capture mode, every 16th rising edge

;1000 = Compare mode, set output on match (CCP1IF bit is set)
;1001 = Compare mode, clear output on match (CCP1IF bit is set)
;1010 = Compare mode, generate software interrupt on match (CCP1IF bit is set, CCP1 pin is unaffected)
;1011 = Compare mode, trigger special event (CCP1IF bit is set, TMR1 is reset and A/D
;conversion is started if the ADC module is enabled. CCP1 pin is unaffected.)

;110x = PWM mode active-high
;111x = PWM mode active-low

;
; If measuring 50Hz, period os 20ms which fits in the 16bit 
;
	goto	StartCCP

; output CRLF
	movlw	0x0D
	call	SRtxChar

	movlw	0x0A
	call	SRtxChar

	movlw	1
    movwf	compareInc

	; skip 
	goto	StartCCP
    
;
; try and capture TMR1 by setting the CCP pin to output and setting up capture.
; This sets pin so may not be wanted
; So skip this code after testing it!
;
	bcf	  	GPIO,CCPpin

    banksel	CCP1CON
    ; turn off - output cleared
	movlw	0<<DC1B0 | B'0000' << CCP1M0 
    movwf	CCP1CON

    ; turn on capture on rising edge. 
	movlw	0<<DC1B0 | B'0101' << CCP1M0 
    movwf	CCP1CON

	;toggle ccp pin 
	movlw	1 << CCPpin 
	; rising edge to trigger capture
	xorwf	GPIO,f
	xorwf	GPIO,f

	;
	; this captures TMR1 in CCPR1
	; now convert to Compare mode.
 
	;Compare modes: 
	;1000 = Compare mode, set output on match (CCP1IF bit is set)
	;1001 = Compare mode, clear output on match (CCP1IF bit is set)
	;1010 = Compare mode, generate software interrupt on match (CCP1IF bit is set, CCP1 pin
	;is unaffected)
	banksel	CCP1CON
	movlw	0<<DC1B0 | B'1010' << CCP1M0 
    movwf	CCP1CON

StartCCP
	banksel	GPIO

	;Compare modes: 
	;1000 = Compare mode, set output on match (CCP1IF bit is set)
	;1001 = Compare mode, clear output on match (CCP1IF bit is set)
	;1010 = Compare mode, generate software interrupt on match (CCP1IF bit is set, CCP1 pin
	;is unaffected)

    ;set compare to not affect the CCP pin.
	banksel	CCP1CON
	movlw	0<<DC1B0 | B'1010' << CCP1M0 
    movwf	CCP1CON

	;
	; copy TMR1 into CCPR1
	;
	movfw	TMR1H
	movwf	CCPR1H
	; there may have been a roll over
	movfw	TMR1L
	movwf	CCPR1L

	; set CCPR1 ahead of TMR1
	; CCPR1 += 256
	movlw	1
	addwf	CCPR1H,f

	; ignore the Compare if any happened
	; clear the CCP1IF flag
	banksel PIR1
    movfw	PIR1
	bcf		PIR1 , CCP1IF

	; set up to set CCP on next compare.
	; You could clear CCP to keep outout cleared.
	movlw	0<<DC1B0 | B'1000' << CCP1M0 
    movwf	CCP1CON

waitCCPorTMR1
	;
	; the delay below emulates some other processing.	
	; use TRM1 to provide "random" delay
    ;
	call	delay	
	btfss	TMR1L,2
	call	delay	

	; wait for the CCP1IF 
 	banksel PIR1
    btfss	PIR1,CCP1IF

	goto	waitCCPorTMR1

    ; clear flag	
	bcf		PIR1,CCP1IF

	; for  compare mode toggle
    banksel	CCP1CON
	movlw	1 
    xorwf	CCP1CON,f
	banksel	GPIO

	; in software toggle a bit in GPIO to that the software jitter can be seen.
	movlw	1 << swCCPpin
    xorwf	GPIO,f
	nop
	nop	
;    xorwf	GPIO,f


;
; Test if CCP sets output or resets it 
;1000 = Compare mode, set output on match (CCP1IF bit is set)
;1001 = Compare mode, clear output on match (CCP1IF bit is set)
;
; test if waiting for SET or CLEAR
;
	btfss	CCP1CON,0
	goto waitingForSet
	goto waitingForClear

waitingForSet
    ;increment CCPR1H by 3
	banksel	CCPR1H
	movfw	compareInc
	movlw	3
	addwf	CCPR1H,f

	; in software toggle GPIO bit, to test if CCP pulsed output latch
	; the simulation does not show CCP pin toggle 
	banksel	GPIO
	movlw	1 << CCPpin
    xorwf	GPIO,f
    nop
    nop
    xorwf	GPIO,f

	goto	waitCCPorTMR1	

waitingForClear
    ;increment CCPR1H
	banksel	CCPR1H
	movfw	compareInc
	movlw	1
	addwf	CCPR1H,f

	; in software try to toggle CCP bit in GPIO. 
	; simulation does not toggle!
	banksel	GPIO
	movlw	1 << CCPpin
    xorwf	GPIO,f
	nop
	nop
    xorwf	GPIO,f

	goto	waitCCPorTMR1	

		

;--------------------------------------------------------------------------
; Sec 2.1	Functions 
;-------------------------------------------------------------------------

	
;--------------------------------------------------------------------------
; Sec 2.1	Serial code - originally for PIC16F84 clocked at 32kHz
;-------------------------------------------------------------------------

SRwrtHexNibble	; currently only displays 0..9, A..F
	banksel	SRtxTemp
	ANDLW	0x0F
	ADDLW	0x06	; is it A..F, if so trigger a digit overflow
	SKPNDC
	ADDLW	7; subtract 10, then add 'A'-'0'
	ADDLW	0x30-6	; Subtract extra 6 added to cause DC
	MOVWF	SRout
	GOTO	SRtxChar

SRtxChar
	;
	;
	;Output Start Bit
	;
	;Output LSB first
    ;
    ; -/=/=/=/=/=/=/=/=/=/__
	; START/lsb...msb/stop
	;
	; The PIC pin is directly connected to PIN2 of the RS232 socket
    ; Normally a MAX232 would invert the signal
    ; so Start is 1, the bits are sent LSB first and the stop is 0
    ;
    ;http://www.lookrs232.com/rs232/waveforms.htm
    ;
    ;

TxOnBit  equ RS232pin
	
	banksel	SRtxTemp
	movwf	SRtxTemp
	comf	SRtxTemp,f
#ifdef comment
; set up the RS232 pin elsewhere
;   make pin output, 
;   if other pins are outputs, these are turned to inputs.
	
	BCF		GPIO,TxOnBit
	MOVLW	~( 1 << TxOnBit ) 
;
;   make pin output, 
;   if other pins are outputs, these are turned to inputs.
	
;	TRIS	GPIO
;	BSF		STATUS, RP0
	banksel TRISIO
	MOVWF	TRISIO		; Set output high to start Measurement
;	BCF		STATUS, RP0
#endif

	banksel GPIO
	call	delay	

	; 9600 baud output
	; move the char into SRtxtemp, it is destroyed.
	;Start Bit
	BSF		GPIO,TxOnBit
	GOTO	$+1
	call	delay	
	movlw	8
	movwf	SRtxCnt
	GOTO 	$+1
SRtxCharLp1

	; This outputs one bits at a time 
	; this takes 6.5 cycles each when using osc =32768Hz and 1200 baud.
	
	; After start bit, which is 1, then test each bit
	; I need to set PA:0 to same as LSB SRtxTemp
	; this compares PA:0 with SRtxTemp so see if it needs toggling
	; The xorwf PORTA,f causes the bit to be toggled if required.

	; Toggle bit if next bit is different 
	RRF		SRtxTemp,f
	movfw	STATUS
	andlw	1 << C		; mask off bit
	addlw   ( 1 << TxOnBit) -1	; shift bit by using a ripple carry
		
	xorwf	GPIO,W
	andlw	0x1 << TxOnBit
	xorwf	GPIO,f		;t+6,19

	GOTO	$+1	
	call	delay	

	decfsz	SRtxCnt
	goto	SRtxCharLp1
	GOTO	$+1	
	GOTO	$+1	
	; output stop bit
	BCF		GPIO,TxOnBit
	call	delay	
	GOTO	$+1	
	GOTO	$+1	

	; add some more stop bits
	BCF		GPIO,TxOnBit
	call	delay	
	GOTO	$+1	
	GOTO	$+1	

	BCF		GPIO,TxOnBit
	call	delay	
	GOTO	$+1	
	GOTO	$+1	
	RETURN


delay	
	; one bit delay
;	nop

;   for 4Mhz Clock
;	movlw	0x03  ; delay for 32K
;	movlw	0x16  ; delay for 9600 baud = 104us ( 8us 
;	movlw	0xCD  ; delay for 1200 baud = 833 us	


;   for 10Mhz Clock, pad with nops as required
	nop	; 0.4us
;	nop 
;	nop
;	movlw	0x3c  ; delay for 9600 baud  = 104 us  
	movlw	.28   ; delay for 19200 baud = 52 us  
	movwf	SRdel
delay1	
	NOP
	decfsz	SRdel
	goto	delay1
	return

	end


