;Dick Cappels' Project Pages
;Contents ©2002 Richard Cappels All Rights Reserved. email projects@cappels.org
;HOME

;*********************************
;2313 frequency meter and pulse generator program
;frequency meter (C)2002 by Richard Cappels sap@cappels.org
;original pulse generator (C)2000 by info@avr-asm-tutorial.
; To quote the author of the pulse generator code, 
; "©2001 by Gerhard Schmidt, 
; Wilhelmstrasse 6, D-64646 Heppenheim/Germany
; You may use the information on these pages and distribute it, 
; but keep the copyright information with it.
;
;
;When assembled, 12 warnings will be generated stating that
;registers have already been declared in .DEF statments. This is ok.
;Some registers are used by both the frequency meter program and
;the pulse generator program. When control is handed from one to 
;the other, everything starts fresh, with the exception of
;initialization of the I/O port, which is handled in the 
;frequency meter program, since it runs first after power-on
;reset. 

;When running frequency meter program input is on Port D, bit 5.
;When running pulse generator program output is on Port D, bits 3, 4, and 5
;The default is to drive all three pins when in pulse generator, the three pins
;are driven in various combinations when the amplitude is adjusted. A Ladder network
;can optionally be connected to pins 3,4, and 5 which functions as a D-to-A converter
;and also provides some resistive isolation between the inputs and the source. 
;The default for the frequency meter is to Port D bits 3,4, and 5 inputs. Thus,
;the output of the ladder network can be used as the input for the frequency meter, 
;making it possible to use a single pin for input and output. 
;
;PD-6 goes low during frequency measurements. Can be used to drive a indicator LED.
;Layout of this doucment:
;1. Frequency meter code
;2. BCD conversion application note code
;3. Pulse generator code
;4. Strings



;*********************************

.include "2313def.inc"     

.equ     clock = 4000000          ;clock frequency
.equ     baudrate = 9600          ;choose a baudrate

.equ     baudconstant = (clock/(16*baudrate))-1



;***** 16 bit binary-to-packed-BDC Subroutine Register Variables

.equ	AtBCD0	=13		;address of tBCD0
.equ	AtBCD2	=15		;address of tBCD1

.def	tBCD0	=r13		;BCD value digits 1 and 0
.def	tBCD1	=r14		;BCD value digits 3 and 2
.def	tBCD2	=r15		;BCD value digit 4

.def	fbinL	=r16		;binary value Low byte
.def	fbinH	=r17		;binary value High byte
.def	cnt16a	=r18		;loop counter
.def	tmp16a	=r19		;temporary value

;***** other register assignments for frequency meter

.def	inbyteh = r20     ;	higher byte for asci-hex conversion
;.def	currentadd = r21  	;pointer to write address for first page of memory	
.def	temp 	= r22     	;general purpose register

.def delaycounter = r23
.def delaycounter1 = r24
.def loopmultiplier = r25


.def	inchar = r26     ;char destined to go out the uart
.def	outchar = r27     ;char coming in from the uart
.def	inbytel = r28     ;lower byte for asci-hex conversion

.def 	reallybigloopcounter =r1


.def	presetreallybigloopcounter =r2 ;1, $0A number of seconds when loopmultiplier is = $64
.def	presetloopmultiplier	   =r3; $64, $01 number of 10 ms increments
.def	presetdelaycounter 	   =r4; $F4 at all times (for 4 MHz)
.def	presetdelaycounter1 	   =r5; $27 st all times (for 4 MHz)

.def	dacvalue		=r6; Actuall pulse amplitude for pulse generator.
.def	daczero			=r7
.org     $00
rjmp	reset
rjmp	reset
rjmp	reset
rjmp	reset
rjmp	reset
rjmp	reset
rjmp	reset
rjmp	pull_the_plug	; uart receiver interrupt -do watchdog reset if fmeter measurement is interrupted
rjmp	reset
rjmp	reset
rjmp	reset

jumprunnngtotal:
	cbi	portd,6
	ldi	temp,$00		;set tccr1a (contorl of 16 bit counter) to all zeros 
	out	tccr1a,temp			
	ldi	temp,$00		;clear 16 bit counter
	out	tcnt1h,temp
	out	tcnt1l,temp
	ldi	temp,$06		;enable input to counter 1 
	out	tccr1b,temp
displayoverandover:
	rcall 	displaythedata
	sbi     ucr,rxen     	;set reciver bit.
	sbis     usr,rxc  	;repeat until char	
	rjmp	displayoverandover
	rjmp 	pull_the_plug	     	;exit when char received	


handleretun:
	rjmp	measurefreqjump	;if return handle same as if R was pressed


reset:     
	ldi     temp,low(ramend)
	out     spl,temp     	;set spl
	ldi     temp,baudconstant     
	out     ubrr,temp     	;load baudrate
		 	
	ldi	temp,$40 ;no pullups on PORTD inputs, bit 6 set high  
	out	portd,temp	 	
	ldi	temp,$40			;preset DDRD
	out 	ddrd,temp			;
	ldi	temp,$38		;preset values to drive pulse dac with
	mov	dacvalue,temp
	ldi	temp,$00
	mov	daczero,temp
	ldi	inchar,$00

	rcall	TypeGreeting	
	rjmp	set1s		;set default measurement time to 1 second and jump to loop
	
loop:		;***** command interpretation loop for frequency meter *****
		;*****     supports jump to pulse generator program    *****
		
	ldi	outchar,$3A	;send prompt (colon char) to terminal
	rcall	rs_send
	ldi	outchar,$20	;send a couple of blank spaces after the return
	rcall	rs_send
	ldi	outchar,$20
	rcall	rs_send	
	rcall	rs_rec		;get char from terminal and interpret char
	cpi	inchar,$3F	;if ? then display menu
	breq	domenu
	cpi	inchar,$0D	;if CR then handle it as R instead of echoing it
	breq	handleretun
	cpi	inchar,$21	;if ! then use watchdog timer to reset the
	breq	pull_the_plug

	andi	inchar,$DF	;make upper-case ascii
				;Echo char after making upper-case
	mov 	outchar,inchar	;copy input char to output char buffer to echo
	rcall	rs_send
	ldi	outchar,$20
	rcall	rs_send
	rcall	rs_send
	
	cpi	inchar,$52	;if R or r, measure frequency
	breq	measurefreqjump	
			
	cpi	inchar,$4D	;if M or m, monitor memory location
	breq	monitormemory
	
	cpi	inchar,'T'	;if T, get running total
	breq	jumprunnngtotal
		
	cpi	inchar,$41	;A 10ms
	breq	set10ms	
	cpi	inchar,$42	;B 100ms
	breq	set100ms
	cpi	inchar,$43	;C 1s	
	breq 	set1s
	cpi	inchar,$44	;D 10s
	breq 	set10s	
	cpi	inchar,$45	;E 100s
	breq 	goto100secondrealy
	cpi	inchar,$50	;P start up pulse generator (will/can not return to this one)
	breq	gotopulsegen	

	rcall	crlf		;if no valid input, send carriage return and line feed
	
	rjmp	loop		;keep going

measurefreqjump:
	rcall	measurefreq
	rjmp 	loop
	

gotopulsegen:
	rjmp pulsegenerator
	
	
decrementanddisplay:	;decrement current address and display current memory contenst
	rjmp 	loop        
     
domenu:			;temp menu part of greeting message
     	ldi     ZH,high(2*menumessage)     ; Load high part of byte address into ZH
     	ldi     ZL,low(2*menumessage)     ; Load low part of byte address into ZL
     	rcall   sendstring
	rjmp 	loop
	
pull_the_plug:			;enable watchdog timer and wait for hardware reset
	wdr
	ldi	temp,$08
	out	wdtcr,temp
wait_for_reset:
	rjmp	wait_for_reset

;inctimebase:	;increase timebase 
;	rjmp 	loop        
    
	     	
monitormemory:	;send contents of memory until char received from terminal
	rcall	measurefreq
	sbi     ucr,rxen     	;set reciver bit.
	sbis     usr,rxc  	;repeat until char
     	rjmp     monitormemory
	rjmp 	pull_the_plug	     	;exit when char received	

goto100secondrealy: rjmp set100s

set10ms:
	ldi	temp,$01
	mov	presetreallybigloopcounter,temp
	ldi	temp,$01
	mov	presetloopmultiplier,temp	 
	ldi	temp,$F4
	mov	presetdelaycounter,temp
	ldi	temp,$27
	mov	presetdelaycounter1,temp 	  
     	ldi     ZH,high(2*tenmsmessage)     ; Load high part of byte address into ZH
     	ldi     ZL,low(2*tenmsmessage)     ; Load low part of byte address into ZL
     	rcall   sendstring
	rjmp	loop


set100ms:
	ldi	temp,$01
	mov	presetreallybigloopcounter,temp
	ldi	temp,$0A
	mov	presetloopmultiplier,temp	 
	ldi	temp,$F4
	mov	presetdelaycounter,temp
	ldi	temp,$27
	mov	presetdelaycounter1,temp 	  
     	ldi     ZH,high(2*hundredmsmessage)     ; Load high part of byte address into ZH
     	ldi     ZL,low(2*hundredmsmessage)     ; Load low part of byte address into ZL
     	rcall     sendstring
	rjmp	loop

     
set1s:
	ldi	temp,$01
	mov	presetreallybigloopcounter,temp
	ldi	temp,$64
	mov	presetloopmultiplier,temp	 
	ldi	temp,$F4
	mov	presetdelaycounter,temp
	ldi	temp,$27
	mov	presetdelaycounter1,temp 	  
     	ldi     ZH,high(2*onesmessage)     ; Load high part of byte address into ZH
     	ldi     ZL,low(2*onesmessage)     ; Load low part of byte address into ZL
     	rcall     sendstring
	rjmp	loop
     
     
set10s:
	ldi	temp,$0A
	mov	presetreallybigloopcounter,temp
	ldi	temp,$64
	mov	presetloopmultiplier,temp	 
	ldi	temp,$F4
	mov	presetdelaycounter,temp
	ldi	temp,$27
	mov	presetdelaycounter1,temp 	  
     	ldi     ZH,high(2*tensmessage)     ; Load high part of byte address into ZH
     	ldi     ZL,low(2*tensmessage)     ; Load low part of byte address into ZL
     	rcall     sendstring
	rjmp	loop
     
set100s:
	ldi	temp,$64
	mov	presetreallybigloopcounter,temp
	ldi	temp,$64
	mov	presetloopmultiplier,temp	 
	ldi	temp,$F4
	mov	presetdelaycounter,temp
	ldi	temp,$27
	mov	presetdelaycounter1,temp 	  
     	ldi     ZH,high(2*hundredsmessage)     ; Load high part of byte address into ZH
     	ldi     ZL,low(2*hundredsmessage)     ; Load low part of byte address into ZL
     	rcall     sendstring
	rjmp	loop
     
rs_send:
     	sbi     ucr,txen     ;set sender bit
     	sbis    usr,udre     ;wait till register is cleared
     	rjmp  	rs_send     
     	out     udr,outchar     ;send the byte
     	cbi     ucr,txen     ;clear sender bit
     	ret               ;go back
     
     
     
rs_rec:     
	sbi     ucr,rxen     ;set reciver bit...               
	sbis    usr,rxc          ;wait for a byte
	rjmp    rs_rec
	in      inchar,udr     ;read valuable
	cbi     ucr,rxen     ;clear register
	ret               ;go back
     

rs_rec_echo:               ;receive and echo char
	rcall 	rs_rec
	mov     outchar,inchar
     	rcall  	rs_send          ;send to comX               
     	ret

crlf:                    ;send carriage return and line feed.
     	ldi     ZH,high(2*crlfmessage)     ; Load high part of byte address into ZH
     	ldi     ZL,low(2*crlfmessage)     ; Load low part of byte address into ZL
     	rcall     sendstring
     	ret



sendstring:          ;call with location of string in Z
     lpm                    ; Load byte from program memory into r0
     tst     r0               ; Check if we've reached the end of the message
     breq     finishsendstering     ; If so, return
     mov     outchar,r0
     rcall     rs_send
     adiw     ZL,1               ; Increment Z registers
     rjmp     sendstring
finishsendstering:
     ret



sendline:          	;send a string terminated in cariage return and line feed
               		;call with location of start of string in Z               
     	rcall     sendstring
     	rcall     crlf
     	ret


TypeGreeting:
	rcall      crlf
	rcall      crlf
	ldi     ZH,high(2*hellomessage)     ; Load high part of byte address into ZH
	ldi     ZL,low(2*hellomessage)     ; Load low part of byte address into ZL
	rcall     sendline          ; sent it.
	ret


byte_to_asciihex:     ;convert byte in inbytel to ascii in inbyteh,nbytel
mov     inbyteh,inbytel
lsr     inbyteh          ;convert the high nybble to ascii byte
lsr     inbyteh
lsr     inbyteh
lsr      inbyteh
subi     inbyteh,$D0     ;add $30
cpi      inbyteh,$3A     
brlo     PC+2          ;If less than 9 skip next instruction
subi     inbyteh,$F9     ;add 8 to ASCII (if data greater than 9)
     ; byte in inbyteh represents upper nybble that was in inbytel at start

andi     inbytel,0b00001111     ;convert the lower nybble to ascii byte
subi      inbytel,$D0     ;add $30
cpi      inbytel,$3A     
brlo     PC+2          ;If less than 9 skip next instruction
subi     inbytel,$F9     ;add 8 to ASCII (if data greater than 9)
     ; byte in inbyteh represents upper nybble that was in inbytel at start
ret


sendbyte:      ;send byte contained in inbytel to terminal

	rcall 	byte_to_asciihex
     	mov     outchar,inbyteh
     	rcall 	rs_send
     	mov     outchar,inbytel
     	rcall  	rs_send
	ret
	
sendbinarybyte:
	ldi	temp,$08
stillsendingbinary:			;rotate byte through carry; send 0 or 1 depending on carry bit
	ldi	outchar,$30
	rol	inbytel
	brcc	dontsendone
	ldi	outchar,$31
dontsendone:
     	rcall  	rs_send
     	dec 	temp
     	brne	stillsendingbinary    	
	ret


;*******************
;*******************
;*******************	MEASURE FREQUENCY
;*******************
;*******************


measurefreq:
	ldi	temp,$90;enable uart interrupts	-disabled when executed
	out	ucr,temp
	sei
	
	ldi	temp,$00		;set tccr1a (contorl of 16 bit counter) to all zeros 
	out	tccr1a,temp
		
			
	ldi	temp,$00		;clear 16 bit counter
	out	tcnt1h,temp
	out	tcnt1l,temp
	ldi	temp,$06		;enable input to counter 1 
	out	tccr1b,temp
	mov	reallybigloopcounter,presetreallybigloopcounter
	cbi	PORTD,6			;set portD bit 6 low (LED Counting indicator on)
reallybigloop:	
	
									
	mov	loopmultiplier,presetloopmultiplier	;****10 ms = $01, 100 ms =  $0A, 1 second = $64	
bigloop:
		
	mov delaycounter,presetdelaycounter		; set values for delay of 10 ms. delaycounter = $F4, delaycounter1 = $27
	mov delaycounter1,presetdelaycounter1		
		
dealylooproutine:		; 10 millisecond dealy loop

	dec delaycounter
	cpi delaycounter,$00
	brne dealylooproutine
	
	dec delaycounter1
	cpi delaycounter1,$00
	brne dealylooproutine
			
	;nop	;looking for a little extra dealy
	nop	
	nop
	
	
	dec 	loopmultiplier
	brne	bigloop	

	dec	reallybigloopcounter
	brne	reallybigloop

	ldi	temp,$00		;stop 16 bit counter 
	out	tccr1b,temp
	cli				;disable interrupts
	sbi	PORTD,6			;set port d bit 6 high (turn LED off)
displaythedata:			
			;Display the data
			
	in	fbinL,tcnt1l	;move counter contents to input for number conversion
	in	fbinH,tcnt1h	
	rcall	bin2BCD16	;Convert to 2.5-byte packed BCD format
	rcall 	crlf		
	mov	outchar,tBCD2
	ldi	temp,$30
	add	outchar,temp
	rcall 	rs_send
	mov	inbytel,tBCD1
	rcall	sendbyte
	mov	inbytel,tBCD0	;since leading digit on high byte is always zero, dont' sent it.
	rcall	sendbyte
	ldi	outchar,$20
	rcall	rs_send

	ret	;return to calling routine to eventually go back to loop


;**** A P P L I C A T I O N   N O T E   A V R 2 0 4 ************************
;* Title:		BCD Arithmetics
;* Version:		1.1
;* Last updated:	97.07.04
;* Target:		AT90Sxxxx (All AVR Devices)
;*
;* Support E-mail:	avr@atmel.com
;* 

;* DESCRIPTION
;* This Application Note lists subroutines for the following Binary Coded
;* Decimal arithmetic applications:
;*
;* Binary 16 to BCD Conversion (special considerations for AT90Sxx0x)



;***** Code

bin2BCD16:
	ldi	cnt16a,16	;Init loop counter	
	clr	tBCD2		;clear result (3 bytes)
	clr	tBCD1		
	clr	tBCD0		
	clr	ZH		;clear ZH (not needed for AT90Sxx0x)
bBCDx_1:lsl	fbinL		;shift input value
	rol	fbinH		;through all bytes
	rol	tBCD0		;
	rol	tBCD1
	rol	tBCD2
	dec	cnt16a		;decrement loop counter
	brne	bBCDx_2		;if counter not zero
	ret			;   return

bBCDx_2:ldi	r30,AtBCD2+1	;Z points to result MSB + 1
bBCDx_3:
	ld	tmp16a,-Z	;get (Z) with pre-decrement
;----------------------------------------------------------------
;For AT90Sxx0x, substitute the above line with:
;
;	dec	ZL
;	ld	tmp16a,Z
;
;----------------------------------------------------------------
	subi	tmp16a,-$03	;add 0x03
	sbrc	tmp16a,3	;if bit 3 not clear
	st	Z,tmp16a	;	store back
	ld	tmp16a,Z	;get (Z)
	subi	tmp16a,-$30	;add 0x30
	sbrc	tmp16a,7	;if bit 7 not clear
	st	Z,tmp16a	;	store back
	cpi	ZL,AtBCD0	;done all three?
	brne	bBCDx_3		;loop again if not
	rjmp	bBCDx_1		




; ************************************************************** 
; * Pulse Width Generator, programmable via Serial I/O 9k6 8N1 * 
; * Input cycle length in µs first, then active cycle in µs    * 
; * Default cycle length is 25,000 µs, default active cycle is * 
; * 2,000 µs. Output is on Port D, Bit 2                       * 
; * Written for the STK200 board, AT90S8515                    * 
; * (C)2000 by info@avr-asm-tutorial, error reports welcome    *
; ************************************************************** 
; modified to work with 2313 as run from a host program

;.NOLIST 
;.INCLUDE "2313def.inc" 
;.LIST 
; 
; 2313.def already called out in host program
; Used registers 
; 
.DEF   rlpm=R0; Used for LPM commands 
.DEF   rchar=R1; Character buffer for SIO communications 
.DEF   rilo=R2; Low byte word input/active loop 
.DEF   rihi=R3; High byte word input/active loop 
.DEF   rjlo=R4; Low byte for multiplication/inactive loop 
.DEF   rjhi=R5; High byte for multiplication/inactive loop 
; 
.DEF   rmpr=R16; A multipurpose register, byte/word 
.DEF   rcl=R18; Desired cycle time, word 
.DEF   rch=R19 
.DEF   ral=R20; Desired active time, word 
.DEF   rah=R21 
; X=R26/27: Counter for Active time 
; Y=R28/29: Counter for inactive time 
; Z=R30/31: Pointer for program memory read operation 
; 
; Constants 
; 
.EQU   OutPort=PortD; Desired output port
.EQU   DataDir=DDRD; Data direction register of that port 
.EQU   ActivePin=2; Desired output pin 
.EQU   ModeControl=0b00000100; Control word for port 
.EQU   cDefCyc=25000; Default cycle length in µs 
.EQU   cDefAct=2000; Default active high length in µs 
.EQU   fq=4000000; Xtal frequency on board in Hz 
.EQU   baud=9600; Baudrate for SIO communication 
.EQU   bddiv=(fq/(16*baud))-1; Baudrate divider 
.EQU   ccr=0x0D; Carriage return character 
.EQU   clf=0x0A; Line feed character 
.EQU   cnul=0x00; NUL character 
.EQU   cesc=0x1B; ESCAPE character 
.EQU   cbel=0x07; Bell character 
; 
; Macro Check input value for default and transfer to desired register 
; 
.MACRO   Default 
   MOV   @0,rilo; Copy result to desired register pair 
   MOV   @1,rihi 
   MOV   rmpr,rilo; Test if input is zero 
   OR   rmpr,rihi 
   BRNE   nodef; Not zero, don't set default value 
   LDI   @0,LOW(@2); Set to default value 
   LDI   @1,HIGH(@2) 
nodef: 
.ENDM 
;
; Code segment start 
; 
.CSEG 
; 
; all of these vectors are already taken care of in the host program 

; Reset- and interrupt-vectors, interrupts not used here 
; 
;   RJMP   Start; Reset vector 
 ;  RETI; Ext Int 0 
 ;  RETI; Ext Int 1 
;   RETI; Timer 1 Capt 
;   RETI; Timer 1 CompA 
;   RETI; Timer 1 CompB 
 ;  RETI; Timer 1 OVF 
;   RETI; Timer 0 OVF 
 ;  RETI; Serial Transfer Complete 
;   RETI; UART Rx Complete 
 ;  RETI; UART Data register empty 
 ;  RETI; UART Tx Complete 
 ;  RETI; Analog Comparator 
; 
; Subroutine for string transmit 
; 
TxStr: 
   SBIS   USR,UDRE; Wait until transmit buffer empty 
   RJMP   TxStr 
   LPM; Read next character from program memory 
   AND   rlpm,rlpm; NUL = end of string
   BRNE   txsend 
   RET 
txsend: 
   LPM; Read the same char again 
   OUT   UDR,rlpm; Tx character read 
   ADIW   ZL,1; point to next char im memory 
   RJMP   TxStr 
   
; this jump added to merge with frequency meter program   
resetchip:
rjmp   pull_the_plug	;initiate hardware reset via watchdog timer

; 
; Subroutine for receiving a number (word, 0..65535) 
; 
RxWord: 
   CLR   rilo; Set buffer to zero 
   CLR   rihi 
rxw1: 

   sbi	portd,6	;turn off led on port 6 if it was on (set pin high)
   SBIS   USR,RXC; Test if rx buffer empty 
   RJMP   rxw1; receive char not available, repeat 
   IN   rmpr,UDR; Get the char from the SIO 
   OUT   UDR,rmpr; Echo this character 
   cpi	rmpr,'f' ;  if "!" then do a hardware reset Modification to allow 
   		;initiation of watchdog reset to return to frequency meter 		
   breq	resetchip
    cpi	rmpr,'F'  		
   breq	resetchip
   cpi rmpr,'A'			;if A go to amplitude settng menu
   breq   setpulseamplitude ;(afterward, restart the pulsie generator)
   cpi rmpr,'a'	;		if a go to amplitude settng menu
   breq   setpulseamplitude ;(afterward, restart the pulse generator)
   CPI   rmpr,ccr; Return char = end of input 
   BREQ   rxwx 
   SUBI   rmpr,'0'; Subtract 48 
   BRCS   rxwerr; not a decimal number, return wit carry set 
   CPI   rmpr,10; number >9? 
   BRCS   rxwok; legal decimal number 
rxwerr: 
   LDI   ZL,LOW(2*ErrorStr); Echo error string 
   LDI   ZH,HIGH(2*ErrorStr)
   RCALL   TxStr 
   SEC; Set carry flag, not a legal number 
   RET 
rxwok: 
   MOV   rjlo,rilo; Copy word for multiplicaton 
   MOV   rjhi,rihi 
   LSL   rilo; Multiply with 2 = 2* 
   ROL   rihi 
   BRCS   rxwerr; Overflow, return with carry set 
   LSL   rilo; Multiply again by 2, = 4* 
   ROL   rihi 
   BRCS   rxwerr; Overflow, return with carry set 
   ADD   rilo,rjlo; Add copy, = 5* 
   ADC   rihi,rjhi 
   BRCS   rxwerr; Overflow, return with carry set 
   LSL   rilo; Multiply by 2, = 10* 
   ROL   rihi 
   BRCS   rxwerr; Overflow, return with carry set 
   CLR   rjhi; Add the decimal number 
   ADD   rilo,rmpr 
   ADC   rihi,rjhi 
   BRCS   rxwerr; Overflow, return with carry set 
   RJMP   rxw1; Get next character 
rxwx: 
   SBIS   USR,UDRE; Wait until transmit buffer empty 
   RJMP   rxwx 
   LDI   rmpr,clf; Transmit additional line feed char
   OUT   UDR,rmpr 
   CLC; Clear carry, no errors 
   RET 
   
setpulseamplitude:	;adjust amplidude of output pulses from ladder network

   LDI   ZL,LOW(2*Selectamplitudemessage); send menu greeting ;**********************
   LDI   ZH,HIGH(2*Selectamplitudemessage)
   RCALL   TxStr 
   rxwPULSEMENU: 
   SBIS   USR,RXC; Test if rx buffer empty 
   RJMP   rxwPULSEMENU; receive char not available, repeat 
   IN   rmpr,UDR; Get the char from the SIO 
   ldi	temp,$20
   out	UDR,temp
   andi rmpr,$DF	;make upper-case ascii
   OUT   UDR,rmpr; Echo this character  
   cpi	rmpr,'A'			
   breq doamplitudea
   cpi	rmpr,'B'
   breq	doamplitudeb
   cpi	rmpr,'C'
   breq	doamplitudec
   cpi	rmpr,'D'
   breq	doamplituded
   cpi	rmpr,'E'
   breq	doamplitudee
   cpi	rmpr,'F'
   breq	doamplitudef
    cpi	rmpr,'G'
   breq	doamplitudeg
   
doamplitudea:
   	ldi	temp,$08		;preset values to drive dac with
   	mov	dacvalue,temp
	rjmp	amplselected
doamplitudeb:
   	ldi	temp,$10		;preset values to drive dac with
   	mov	dacvalue,temp
	rjmp	amplselected
doamplitudec:
   	ldi	temp,$18		;preset values to drive dac with
   	mov	dacvalue,temp
	rjmp	amplselected
doamplituded:
   	ldi	temp,$20		;preset values to drive dac with
   	mov	dacvalue,temp
	rjmp	amplselected
doamplitudee:
   	ldi	temp,$28		;preset values to drive dac with
   	mov	dacvalue,temp
	rjmp	amplselected
doamplitudef:
   	ldi	temp,$30		;preset values to drive dac with
   	mov	dacvalue,temp
	rjmp	amplselected
	
doamplitudeg:
   	ldi	temp,$38		;preset values to drive dac with
   	mov	dacvalue,temp
	rjmp	amplselected
	
amplselected:				;done selecting amplitude. Now restart pulse gen.
  	LDI   ZL,LOW(2*valuesetmessage); send menu greeting 
   	LDI   ZH,HIGH(2*valuesetmessage)
   	RCALL   TxStr 
   	
	rjmp	pulsegenerator	
   
; 
; Start of program 
; 
pulsegenerator:
Start: 
 
 ;Setup the stack for the use of subroutines 
;           eliminate stack initialization - it was done in the host program. 
;   LDI   rmpr,HIGH(RAMEND); Stack setting to highest RAM adress 
;  OUT   SPH,rmpr 
   LDI   rmpr,LOW(RAMEND) 
   OUT   SPL,rmpr 
; 
; Initiate Port D to output on Bit 2 
; this initialization taken care of earlier
;   LDI   rmpr,ModeControl; Set output pin 
 ;  OUT   DataDir,rmpr; to Data Direction register 

    ldi		temp,$40
    out		portd,temp
    ldi		temp,$78			;set the pulse output pins to outputs (frequency meter cannot now see pulses)
    out 	ddrd,temp


; Initiate SIO communication 
; 
   LDI   rmpr,bddiv; Set baud rate 
   OUT   UBRR,rmpr 
   LDI   rmpr,0b00011000; Enable TX and RX 
   OUT   UCR,rmpr 
;
; Transmit the hello sequence 
; 
hello: 
   LDI   ZH,HIGH(2*InitStr); Point Z to string 
   LDI   ZL,LOW(2*InitStr) 
   RCALL   TxStr 
; 
; Get value for total cycle length 
; 
getcycle: 
   LDI   ZH,HIGH(2*CycleStr); Point Z to string 
   LDI   ZL,LOW(2*CycleStr) 
   RCALL   TxStr 
   RCALL   RxWord 
   BRCS   getcycle; Repeat if error 
   Default   rcl,rch,cDefCyc 
; 
; Get value for active cycle length 
; 
getactive: 
   LDI   ZH,HIGH(2*ActiveStr); Point Z to string 
   LDI   ZL,LOW(2*ActiveStr) 
   RCALL   TxStr 
   RCALL   RxWord 
   BRCS   getactive; Repeat if error 
   Default   ral,rah,cDefAct 
;
; Calculate counter value for active time 
; 
   MOV   XL,ral; Calculate active time 
   MOV   XH,rah 
   SBIW   XL,5; at least 4 cycles 
   BRCS   getcycle; illegal active time 
   ADIW   XL,1; At least one cycle required 
   MOV   rilo,XL 
   MOV   rihi,XH 
; 
; Calculate counter value for inactive time 
; 
   MOV   YL,rcl; Calculate inactive time 
   MOV   YH,rch 
   SUB   YL,XL; Subtract active time 
   SBC   YH,XH 
   BRCS   getcycle; Active time longer than cycle time 
   SBIW   YL,5; Subtract loop delays 
   BRCS   getcycle; Less than 3 loop cycles are illegal 
   ADIW   YL,1; minimum 1 loop 
   MOV   rjlo,YL 
   MOV   rjhi,YH 
   LDI   ZH,HIGH(2*WaitStr); Output operating string 
   LDI   ZL,LOW(2*WaitStr) 
   RCALL   TxStr 
   cbi	portd,6	;turn on led on port 6. Sink current to ground.
  ;
; Counting loop starts here, check char at SIO available?
; 
ctloop: 


;   SBI   OutPort,ActivePin; Start active phase ;instead of setting a bit, set pins high according to ampltude.
    nop
    out Outport,dacvalue
ActLoop: 
   SBIW   XL,1; 0.5 µs 
   BRNE   ActLoop; 0.5 µs 
   SBIC   USR,RXC; Test if rx buffer empty 
   RJMP   getcycle; get new cycle time 
   ;CBI   Outport,ActivePin; Start Inactive phase 
   nop
   out Outport,daczero    ;instead of clearing bit, set all bits low.
InactLoop: 
   SBIW   YL,1; 0.5 µs 
   BRNE   InactLoop; 0.5 µs 
   MOV   XL,rilo; Refresh counters 
   MOV   XH,rihi 
   MOV   YL,rjlo 
   MOV   YH,rjhi 
   NOP 
   NOP 
   RJMP   ctloop; start a new cycle 





	
hellomessage:
.db     "Frequency meter/pulse generator 2002.02.09 rev.B"
.db	$0A,$0D
.db	"Frequency meter (C)2002 by Richard Cappels,avr@cappels.org. "
menumessage:
.db	$0A,$0D
.db	"A=10ms,B=100ms,C=1s,D=10s,E=100s. " 
.db	$0A,$0D
.db	"R=read freq. M=continuous freq. T-running total."
.db	$0A,$0D
.db	"P to switch to pulse generator, !=reset "
.db	$0A,$0D
.db	"Max count= 65536, Max frequency< 4M Hz. No overflow detection."
.db	$0A,$0D
.db	$0A,$0D


crlfmessage:
.db     $0A,$0D
.db     00,00


tenmsmessage:
.db	"Timebase= 10 ms. Frequency= Count X100. "
.db     $0A,$0D
.db     00,00

hundredmsmessage:
.db	"Timebase= 100 ms. Frequency= Count X10"
.db     $0A,$0D
.db     00,00
onesmessage:
.db	"Timebase= 1 s. Frequency= Count X1"
.db     $0A,$0D
.db     00,00
tensmessage:
.db	"Timebase= 10 s. Frequency= Count X1/10"
.db     $0A,$0D
.db     00,00
hundredsmessage:
.db	"Timebase= 100 s. Frequency= Count X1/100  "
.db     $0A,$0D
.db     00,00



;//////////BELOW ARE MESSAGES FOR PULSE GENERATOR PROGRAM///////////////

valuesetmessage:
.db	$20,$20
.db	"**Pulse amplitude set."
.db	$0A,$0D,$00,$00

Selectamplitudemessage:
.db	$0A,$0D
.db	"Enter amplitude A..G (default= G,max)."
.db	$20,$20,$00,$00
; 
; Text strings for transmit, ANSI screen encoded! 
; 
ErrorStr: 
.DB   "Error in input. Repeat input. " 
.DB   ccr,clf,cnul,cnul 
ActiveStr:
.DB   "Input active time, A or F (default=2,000):" 
.DB   cnul,cnul 
CycleStr:  
.DB   "Input cycle time, A or F (default=25,000):" 
.DB   cnul,cnul 
WaitStr:  
.DB   "Continued operation." 
.DB   "Enter new cycle length to stop and restart generator" 
.DB   ccr,clf,cnul,cnul 
InitStr: 
.db   $0A,$0D,$0A,$0D
.DB   "PULSE GENERATOR. (C)2000 by info@avr-asm-tutorial."
.db    $0A,$0D
.db   "TO RETURN TO FREQUENCY METER, ENTER F KEY.  " 
.DB   ccr,clf 
.db	"For pulse amplitude menu, press A."
.db	$0A,$0D
.DB   "All times is in micro-seconds, the accepted range is 5..65535." 
.DB   ccr,clf 
.DB   "New value stops operation until all input is completed. " 
.db	$0A,$0D,$0A,$0A
.db	$00,$00

;http://projects.cappels.org/

;HOME