;Copyright 2003 Richard Cappels, projects@cappels.org  http://projects.cappels.org;;A Microcontroller Based Digital Lock-In Millihommeter;Using the Atmel AT90S2313 Microcontroller or Equivalent.;Firmware expects a 4 Mhz AT90S2313. 313def.inc, available;Atmel (www.atmel.com) is needed to assemble this file.;You may need to correct the path to this file.;This microcontroller provides 1 kHz pulses on opposite;phases on PB4 and PB6 to drive the switches in the;synchronous demodulator, 1 Khz bursts on PB3 to drive;current into the resistance under test via an inverting;driver during every other measurement cycle, charge measure;and capacitor clamp signals for the A/D conversion on PD5;and PD4 respectively, a comparator to measure the analog;signal on the non-inverting comparator input, a zero button;input with and internal weak pull-up on PD6, and a positive;going measurement in progress LED drive pulse that goes high;while the 1 kHz burst is being applied to the resistance;under load on PD7. The output of this circuit is ASCII on EIA-232;and an inverting buffer is expected on the transmit data pin..include "2313def.inc"		;Include file in same directory as project..def	chargecount	= r2	;Value of measuered charge in integrator.def	counterlow	= r3	;Lower byte of the preload for 4 kHz interrupt. Used for programmability.def	zerochargecount	= r4	;Value of charge on prior measurement.def	quarterycles	= r5	;Value to change timer low.def	meascount	= r6	;Count of measurements modulus 256. Used for alternative meas output.def	zerovalue	= r7	;Value measured when looking at "zero" ohms..def	temp		=r16	;Scratch register.def	temp2		=r17	;Second scratch register .def	outpulse	=r18	;Interrupt counter.def	datab		=r19	;Temporary data for PORTB, to be output every interrupt time (only 4 lsb available).	.def	flagreg		=r21	;Flags stored here.def	H		= r22	;Used to send decimal ascii (Hundreds).def	T		= r23	;Used to send decimal ascii (Tens).def	U		= r24	;Used to send decimal ascii (Units);NOTE: Y register is used as integration timer;definition of I/O;B0	COMPARITOR NONINVERTING;B1	COMPARITOR INVERTING;B2	(not assigned);B3	SAMPLE CLOCK PHASE 1 ON EVEN MEASUREMENTS;B4	SAMPLE CLOCK PHASE 1;B5	SAMPLE CLOCK PHASE 2;B6	SAMPLE CLOCK PHASE 3;B7	SAMPLE CLOCK PHASE 4;D0	USED FOR UART RECEIVE	INPUT;D1	Reserved FOR UART TRANSMIT;D2	(not assigned - configure as INPUT);D3	LED - SIGNAL ABOVE THRESOLD;D4	CLAMP INTEGRATOR TO ZERO;D5	MEASURE;D6	Zero pushbutton (closure to ground) INPUT;D7	Not available on AT90S2313;definition of flagreg bit assignments.equ	meastaken	=0	;Set after new measurement taken. .equ	adcover		=1	;Set by measurecharge if ADC counts past max.(Overflow).equ	zeropend	=2	;Set to indicate zero button has been pressed;3;4;5;6;7.equ	counterhigh	=$03	;High byte of the perolaod for 4 kHz interrupt.equ	measuretime	=$E7	;Points to detector cycle that measurement is taking place.equ	measuretimeH	=$03.equ	resettime	=$E8.equ	resettimeH	=$03.equ	outpulsepattern =0b00110011 ;Note: As used here, the high and low nybble are used sequentially,and;repeats every two cycles. For odd and even cycles to be identicle, both nybbles should be identicle.;The pattern "0b00110011 would result in a 50% duty cycle at 1 kHz..equ	Zeroloc		=$2A	;EEPROM location in which zero value is stored. 	;UART baud rate calculation.equ	clock = 4000000	;clock frequency.equ	baudrate = 9600		;choose a baudrate.equ	baudconstant = (clock/(16*baudrate))-1 .cseg	.ORG $0000			;Main program entry point on reset	rjmp start.ORG $0004 	rjmp timerservice	;Timer/counter compare interrupt handler		HelloString:	;TEXT TO BE TYPED ON FIRST LINE WHEN POWER IS APPLIED.db	"V031002A Max=190"		;Version number, reminder of A/D maximum count.db	$0A,$0D.db	00,00	start:   	ldi	r16,RAMEND		;Init Stack Pointer	out	spl,r16 	ldi     temp,baudconstant      	out     ubrr,temp     		;Load baudrate. ;	sbi	ucr,rxen		;Enable WAUT receive ;Uart receive not used so not enabled bere. 	sbi	ucr,txen		;Enable UART Transmit	ldi	outpulse,outpulsepattern;Initial pulse ouput pattern 	ldi	flagreg, 0b00000000	;Initialize all flags			clr	YL			;Initialize integration cycle counter	clr	YH	ldi	temp,$04		;Initialize quarter cycle counter	mov	quarterycles,temp	ldi	temp,$E8		;Initialize the variable for interrupt timer	mov	counterlow,temp 	ldi	temp,$FF		;Set data on PORTD and PORTB high to act as weak pullups.	out	PORTD,temp	out	PORTB,temp		ldi	temp,0b11111100		;Make pins on PORTB, except comparitor pins, ouputs.	out	DDRB,temp	ldi	temp,0b10111010		;Establish direction of PORTD pins.	out	DDRD,temp	ldi	temp,$09		;Set timer 1 to reset 0000 after compare match. Prescaler = 1X	out	TCCR1B,temp	ldi	temp,counterhigh	;Set compare register to $03E8 for 4 khZ interrupt	out	OCR1AH,temp	mov	temp,counterlow	out	OCR1AL,temp		;Read contents of EEPROM into zerovalue register (recall zero refernece)wratzA:	sbic	eecr,eewe		;Wait for EEPROM write to not be busy	rjmp	wratzA	ldi	temp,Zeroloc	out	eear,temp		;Move to EEPROM address register	sbi	eecr,eere		;Trigger the read	in	zerovalue,eedr		;Get the data into temp	clr	meascount		;Set measurement counter mod 256 to zero	ldi	temp,$40		;Enable interrupt on compare match	out	TIMSK,temp		sei				;Enable interrupts -start measurements		Wait1:	mov	temp,meascount		;Wait until meascount = 1 	cpi	temp,1			;Wait for display to come on before hello message.	brne	Wait1			;This is useful for display on the same power switch.		rcall	SendHello	Wait2:	mov	temp,meascount		 	cpi	temp,3			;(wait for circuit to settle)	brne	Wait2			rjmp	Main			;Go to main loop and wait for interruptSendHello:	;Send HelloString		ldi     ZH,high(2*HelloString)	; Load high part of byte address into ZH	ldi     ZL,low(2*HelloString)	; Load low part of byte address into ZL	rjmp	sendstring     	     	    	SendHedding:	;Send Hedding		ldi     ZH,high(2*Hedding)	; Load high part of byte address into ZH	ldi     ZL,low(2*Hedding)	; Load low part of byte address into ZL	rjmp	sendstring	Hedding:	;TEXT TO BE TYPED ON FIRST LINE BEFORE DISPLAYING EACH MEASUREMENT.db	"mOhms  Offset ".db	00,00    	Sendoverrange:	;Send Overrange message		ldi     ZH,high(2*erormessage)	; Load high part of byte address into ZH	ldi     ZL,low(2*erormessage)	; Load low part of byte address into ZL	rjmp	sendstring	erormessage:	;TEXT TO BE TYPED WHEN ADC OVER RANGE IS OBSERVED.db	"ERROR ADC>190 ".db	00,00    	Sendzero:	;Send Zeroing message		ldi     ZH,high(2*zeromessage)	; Load high part of byte address into ZH	ldi     ZL,low(2*zeromessage)	; Load low part of byte address into ZL	rjmp	sendstring	zeromessage:	;TEXT TO BE TYPED AT START OF ZEROING COUNT.db	"  SETTING ZERO".db	$0A,$0D.db	"  ************".db	00,00Sendstring:	;Send string pointed to by ZH,ZL.	lpm				; Load byte from program memory into r0	tst	r0			; Check if we've reached the end of the message	breq	stringdone		; If so, return	mov	temp,r0	rcall	emitchar	adiw	ZL,1			; Increment Z registers	rjmp	Sendstringstringdone:    	ret		Main: 		sbic	PIND,6			;Check to see if zero button is down.	rjmp	nozbutdown		;If not down, skip to next task.	sbrc	flagreg,zeropend	rjmp	nozbutdown	rcall	sendzero		;Send message to dispalay.	rcall	crlf	ori	flagreg,0b00000100	;Set flag to make zero operation pending.nozbutdown:	cbi	PORTD,3								mov	temp,meascount			andi	temp,0b00000001	brne	notdoinit			sbi	PORTD,3			;Light LED during measurement		notdoinit:	sbrs	flagreg,meastaken	rjmp	Main			;Return if this is not first call after signal measurement	andi	flagreg,0b11111110	;Clear the flag		mov	temp,meascount		;Label if offset	andi	temp,0b00000001	brne	notoffset			rjmp	main		notoffset:				;If actual meausrement either store as zero reference					;or send out via serial port	sbrc	flagreg,zeropend	rjmp	dozero			;Zero button is down, so save zero reference									sbrs	flagreg,adcover		;If ADC has overflowed then show error message	rjmp	dohedding		;instead of reagular hedding.	rcall	sendoverrange	andi	flagreg,0b11111101		;Clear ADC overflow flag	rjmp	nohedding	dohedding:	rcall	sendheddingnohedding:			rcall	crlf		mov	temp,chargecount	;Send the latest measuremnet followed by a CR and LF.	sub	temp,zerovalue		;Subtract zero offset.	brcc	notnegative	push	temp			;Save temp on stack.	ldi	temp,'-'		;Send minus sign.	rcall	emitchar	clr	temp	pop	temp2	sub	temp,temp2		;Make number positive.notnegative:	rcall	sendnumber		;Send resistance in milliohms to serial port.					ldi	temp,$20	rcall	emitchar	ldi	temp,$20	rcall	emitchar	ldi	temp,$20	rcall	emitchar	ldi	temp,$20	rcall	emitchar	ldi	temp,$20	rcall	emitchar	mov	temp,zerochargecount	;Send the amplifier zero SIGNAL offset	rcall	sendnumber		ldi	temp,$20	rcall	emitchar	ldi	temp,'['		;Send data separator	rcall	emitchar	mov	temp,zerovalue		;Send zero ohm reference count	rcall	sendnumber	ldi	temp,']'		;Send data separator	rcall	emitchar		rcall	crlf				rjmp	Maindozero:					;Save measurement value as zero reference;	rcall	sendzero		;Send message to dispalay.;	rcall	crlf;	ldi	temp,$2A;	rcall	emitchar	andi	flagreg,0b11111011	mov	zerovalue,chargecount		ldi	temp,Zeroloc	wrat1:	sbic	eecr,eewe		;Wait for EEPROM write to not be busy	rjmp	wrat1	out	eear,temp					out	eedr,zerovalue		;Set up the  write data	cli				;Inhibit interrupts -**** INTERRUPTS NOT RE-ENABLED CAUTION!	sbi	eecr,eemwe	sbi	eecr,eewe	sei				;Re-enable interrupts	butdown:		sbic	PORTD,6	;	rjmp	Main	rjmp	butdown		;Wait for button to come up sendnumber:	;Enter with value to be sent in temp, sends as three digit ascii  		;via serial port. 	mov	U,temp			;Enter with 8 bit value in U, Exits with numerals in H,T,U 	clr	H			;(Hundreds, Tens, and Units). 	clr	T anotherh: 	subi 	U,100			;Find out how many hundreds in U. 	brcs	hdone 	inc	H 	rjmp	anotherh hdone: 	subi	U,-100			;Subtracted one too many -add back. anothert: 	subi	U,10 	brcs	tdone 	inc	T 	rjmp	anotherT tdone: 	subi	U,-10		 	cpi	H,0		 	breq	dontsendh 	subi	H,-48 	mov	temp,H 	rcall	emitchar 	cpi	t,0 	brne	dontsendh 	ldi	temp,$30		;If U=0 then don't emit this zero 	rcall	emitchar dontsendh:	 	cpi	t,0 	breq	dontsendt 	subi	T,-48 	mov	temp,T 	rcall	emitchar dontsendt: 	subi	U,-48	 	mov	temp,U 	rcall	emitchar 	ret 	 	 crlf:			;Send return and linefeed  	ldi	temp,$0A		;Send return and line feed 	rcall	emitchar 	ldi	temp,$0D 	rcall	emitchar 	ret	 emitchar:	;Send character contained in temp with two stop bits. 	sbis	usr,udre  		;wait until the register is cleared. 	rjmp	emitchar      	cbi	ucr,0			;Set 9th bit to zero (second stop bit). 	sbi	ucr,2			;Set to send 9 data bits. 	out	udr,temp		;send the character. 	retmeasurecharge:			;Measure time it takes to zero integrator. Output range is $80 to $00,	ldi	temp,$42 ;WAS $42	mov	chargecount,temp;Set chargevalue to $42 so it will only spend 190 cycles in this routine.		ori	flagreg,1	;Set meastaken flag.	cbi	PORTB,4		;Make both phase 1 and phase 3 low during the measurement		cbi	PORTB,6		;so incoming signals don't affect measurement accuracy.	sbi	PORTD,5		;Turn on discharge resistor.;(Discharge resistor turned off after return).	sbic	acsr,aco	;If comparitor is high, then charge is zero or negative. In this case.	ret			;Skip measurement and go on to make sure capacitor is discharged.morch:	inc	chargecount	;Stay in a counting loop until the comparitor goes high.	breq	saturated	;Or chargecount counts up to zero	sbis	acsr,aco	rjmp	morch	ret			;Normal return - measurement completed.saturated:	ori	flagreg,0b00000010	;Set ADC overflow flag	ret			;(Discharge resistor turned off after return)	writeportb:	;Output outpulse combined with datab lower 4 bits to PORTB	mov	temp,datab	andi	temp,$0F	mov	temp2,outpulse	andi	temp2,$F0	or	temp,temp2	out	PORTB,temp	rettimerservice:		;Timer 1 interrupt service	push	temp	push	temp2	in	temp,sreg	push	temp	clc	rol	outpulse		;Rotate bit image and output to PORTB.	brcc	B1			;If carry set, set bit 0.	ori	outpulse,$01		B1:					;If meascount cycle is even, copy datab bit 6 to data bit 3					;so that pulses are sent from B3 on alternate measurements	mov	temp,meascount		;Test to see if Meascount is even or not	andi	temp,0b00000001	brne	noclockout		;If meascount lsb is not zero, skip to noclockout					;If meascount is zero, then copy outputle bit 4 into datab bit 3	mov	temp,datab	andi	temp,0b11110111		;Mask off all but the bit of interest	mov	datab,temp	mov	temp,outpulse			lsr	temp			;Shift the bit right to get it into position	lsr	temp	lsr	temp	andi	temp,0b00001000			or	datab,tempnoclockout:	rcall	writeportb	dec	quarterycles		;If four quarter cycles haven't passed, then don't	brne	notfullcycle		;increment resetcounter or see if its time to measure charge.	ldi	temp,$04	mov	quarterycles,temp			adiw	YL,1			;There will be a glitch on D4 during resets.	mov	temp,YL	mov	temp2,YH	subi	temp,measuretime	sbci	temp2,measuretimeH	brcs	noreset	brne	nomeasure		;If this is the first time through, measure the charge (250 us).	rcall	measurecharge		;First time is when resercounter = measuretime.	inc	meascount		;Increment measurement count	mov	temp,chargecount	;After measuring charge, adjust offset and save previoius charge	subi	temp,$42	mov	chargecount,temp	;Remove offset from count.	mov	temp,meascount		;On odd cycles, save measured value as zero	andi	temp,0b00000001	breq	savezero								sub	chargecount,zerochargecount	;If not saving it,then we just measured zero, so subrtact zero offset	rjmp	measuredonesavezero:	mov	zerochargecount,chargecount	;Save zero referencemeasuredone:nomeasure:	mov	temp,YL	mov	temp2,YH	subi	temp,resettime	sbci	temp2,resettimeH	brne	nozeroY	clr	YL	clr	YHnozeroY:	cbi	PORTD,5			;Turn off discharge resistor.	sbi	PORTD,4			;Turn on dump switch.	rcall	writeportb		;Restore PORTB	rjmp	finishedinterupt	noreset:	cbi	PORTD,4			;Turn off dump switch.			notfullcycle:				;Skip to here if this is not at the end of a full cycle.	finishedinterupt:	pop	temp	out	sreg,temp	pop	temp2	pop	temp	reti.exit					;don't assemble past here.											
