;
; This code is in Parallax (Tech-Tools) assembler format.  It should work for
; just about any PIC microcontroller.
;
; The hardware is simply two 1M resistors in series connected to an input pin.
; The other side of the two resistors is connected to the 120 volt AC line (hot).
; The microcontroller's DC ground is connected to AC neutral.  This sounds
; dangerous, but works well.  I've used it for years on a number of different
; projects.  Two 1M resistors are used to guard against the unlikely failure
; of a resistor in the shorted condition, which would destroy the
; microcontroller.  This simple circuit uses the internal clamping diodes on
; every input pin to ensure that the voltage on the pin is clamped to within
; a diode drop (.6 volts) of Vss or Vdd.  The resistance limits the current to
; a value that is safe for the clamping diodes.  The Schmitt trigger snap
; response on the inputs provides a clean 60 Hz square wave free of any line
; noise.  For very noisy signals or critical applications, the simple code
; below could easily be modified to wait for several consecutive inputs at both
; the high and low state to debounce the signal and ensure that no noise
; triggering occurs.
;
; The 1/120 or 1/60 second ticks that can be easily generated are excellent
; for timing many real world events such as debouncing pushbuttons, etc.
; Using the AC line frequency is an excellent method to generate a low
; frequency clock with greater long term accuracy than low cost crystal
; oscillators will provide on a microcontroller.  This technique would be a
; very good way to provide a clock feature on a low cost line powered device.
;
; I've also used similar hardware to detect the high voltage telephone
; ring signal on a phone line, and a suitable voltage divider similar to this
; to detect the difference in telephone line voltage between on hook and
; off hook conditions.  It's a handy trick any time you need to detect AC or
; DC voltages above the power supply voltage (Vss).
;
; I didn't invent this simple and elegant technique, but I've used it a lot.
; Many designers already know this trick, but many don't and could improve
; performance and reduce cost by adding this to their bag of microcontroller
; tricks.
;
; Address any questions to bruce@thinkingdevices.com
;

AC	equ	RC.3		; Input - AC zero crossing detection
flag1	equ	25h		; Miscellaneous flags & bits
ticks	equ	34h		; Tick counter, counts 50/60 ticks in a second

sec	equ	flag1.1		; 1 if another second just passed
ACStat	equ	flag1.2		; 1 if AC in positive half cycle, 0 if negative
ACzero	equ	flag1.3		; 1 if the AC line is in a zero crossing
Hz50	equ	flag1.4		; 1 if 50 Hz line frequency, 0 for 60 Hz


;
; This routine detects the AC zero crossing so all AC device switching
; can be done at the zero voltage crossing to minimize electrical transients.
; Only the zero crossings at high to low transitions are used in my application
; but typically either zero crossing could be used.  The routine listed below
; was called every time through a main program loop.
;
; This routine also counts these AC zero crossings as ticks, and uses this
; count to generate the sec bit that signals that a second has just lapsed.
; This routine counts seconds for either 50 Hz or 60 Hz AC line frequency,
; depending on whether Hz50 is set or not.  Hz50 is set or reset automagically
; on power up when the AC line frequency is calibrated against the
; microcontroller crystal oscillator (calibration code not shown).
;
CkAC	clrb	ACzero		; Assume no AC zero crossing this time
	clrb	sec		; Assume another second hasn't elapsed
	jnb	ACstat,:ACneg	; If AC is neg cycle, look for pos transition
:ACpos	jb	AC,:back	; If still positive, go back
	clrb	ACstat		; Remember the negative transition
	setb	ACzero		; We have a negative zero crossing
	inc	ticks		; Count this AC crossing as a tick
	jb	Hz50,:time50	; Is this a 20 msec tick?
:time60	cjb	ticks,#60,:back	; No, 16.67 msec.  60 of them yet?
	setb	sec		; Yes.  Count it as a second
	clr	ticks		; Reset the tick counter for the next second
	jmp	:back		; Return to the calling routine
:time50	cjb	ticks,#50,:back	; 20 msec ticks.  50 of them yet?
	setb	sec		; Yes.  Count it as a second
	clr	ticks		; Reset the tick counter for the next second
	jmp	:back		; Return to the calling routine
:ACneg	jnb	AC,:back	; If still negative, go back
	setb	ACstat		; Remember the positive transition
:back	ret			; Return to the calling routine
