;Dick Cappels' project pages sap@cappels.org - you shoudl be able to copy and paste this text into an assembler file.
;©2002 Richard Cappels All Rights Reserved
;Use of information presented on this page is for personal, nonprofit educational
;and noncommercial use only. This material (including object files) is copyrighted
;by Richard Cappels and may not be republished or used directly for commercial
;purposes without explicit permission For commercial license, click HERE.
;;HOME
;*********************************
;2313 frequency meter with RF link
;*********************************
.include "2313def.inc"
;***** 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
.def inbyteh = r20 ; higher byte for asci-hex conversion
.def temp = r22 ;general purpose register
.def delaycounter = r23
.def delaycounter1 = r24
.def loopmultiplier = r25
.def inbytel = r28 ;lower byte for asci-hex conversion
.def tcounter = r24 ; 8 Bit loop timing counter.
.def runincount = r19 ; Used to keep track of consecutive zeros detected in runin code.
.def wirelessdat = r19 ; data to be sent via wireless port
.def inbyte = r26 ; Decoded data byte could be same register as runincoung
.def header = r21
.def outbyte = r21 ;Byte value to be sent (input to send routine)
.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 (I think)
.def presetdelaycounter1 =r5; $27 st all times (I think)
.def bitsout = r06 ; counter for bits sent (couple also use a delay counter)
.def sendheader = r07 ;headerr to place on outgoing RF byte
.def receiveaddress = r08 ; Address mask for RF received data header
.equ minzeros = 9 ;Minimum number of consecutive zeros to qualify as run-in code
.equ defaultsendheader = $10 ;Default value of RF sendingheaderr.
.equ defaultreceiveaddress =$18 ;Default value for RF receiveing address in header
.equ RFxmitdat = 4 Port bit used to send transmit data to RF transmitter
.equ RFrcvdat = 6 Port bit used to receive data from RF receiver
.org $00
rjmp reset
rjmp reset
rjmp reset
rjmp reset
rjmp reset
rjmp reset
rjmp reset
rjmp reset
rjmp reset
rjmp reset
rjmp reset
rjmp reset
rjmp reset
rjmp reset
rjmp reset
rjmp reset
reset: rjmp init ;start init
monitormemory: ;measure over and over until char received from terminal
rcall measurefreq
rjmp loop ;
init:
ldi temp,low(ramend)
out spl,temp ;set spl
cbi PORTD,RFrcvdat ;no pullup on receiver input
cbi DDRD,RFrcvdat ;make receier input pin an input
cbi PORTD,RFxmitdat ;initial state of transmitter output is low
sbi DDRD,RFxmitdat ;make transmitter output pin an output
cbi DDRD,$05;set DDRD bit 5 as input
cbi PORTD,$05
;Fetch default transmit address from EEPROM
ldi temp,defaultsendheader ;Establish the default RF sendingheaderr
mov sendheader,temp
cbi DDRD,$05;set DDRD bit 5 as input
rcall TypeGreeting
rjmp set1s ;set default measurement time to 1 second and jump to loop
loop: ;*****command interpretation loop****
ldi wirelessdat,$3A ;send prompt (colon char) to terminal
rcall Sendabyte
ldi wirelessdat,$20
rcall Sendabyte
ldi wirelessdat,$20
rcall Sendabyte
rcall rs_rec ;get char from terminal and
;interpret char
cpi inbyte,$3F ;if ? then display menu
breq domenu
cpi inbyte,$21 ;if ! then use watchdog timer to reset the
breq pull_the_plug
andi inbyte,$DF ;make upper-case ascii
cpi inbyte,$52 ;if R or r, measure frequency
breq measurefreqjump
cpi inbyte,$0D ;if Retrun measure frequency
breq monitormemory
cpi inbyte,$41 ;A 10ms
breq set10ms
cpi inbyte,$42 ;B 100ms
breq set100ms
cpi inbyte,$43 ;C 1s
breq set1s
cpi inbyte,$44 ;D 10s
breq set10s
cpi inbyte,$45 ;E 100s
breq goto100secondrealy
rjmp loop ;keep going
measurefreqjump:
rcall measurefreq
rjmp loop
decrementanddisplay: ;decrement current address and display current memory contenst
rjmp loop
domenu:
rcall TypeGreeting
rjmp loop
pull_the_plug: ;enable watchdog timer and wait for hardware reset
ldi ZH,high(2*resetmessage) ; Load high part of byte address into ZH
ldi ZL,low(2*resetmessage) ; Load low part of byte address into ZL
rcall sendstring
wdr
ldi temp,$08
out wdtcr,temp
wait_for_reset:
rjmp wait_for_reset
inctimebase: ;increase timebase
rjmp loop
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_rec:
rcall TrytoGetbytemessage
cpi header,defaultreceiveaddress ;Is this data addressed to device 1?
breq itsforme ;If it is addressed to device 1, its for me.
rjmp rs_rec
itsforme:
ret ;go back
rs_rec_echo: ;receive and echo char
rcall rs_rec
mov wirelessdat,inbyte
rcall Sendabyte ;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 wirelessdat,r0
rcall Sendabyte
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 wirelessdat,inbyteh
rcall Sendabyte
mov wirelessdat,inbytel
rcall Sendabyte
ret
sendbinarybyte:
ldi temp,$08
stillsendingbinary: ;rotate byte through carry; send 0 or 1 depending on carry bit
ldi wirelessdat,$30
rol inbytel
brcc dontsendone
ldi wirelessdat,$31
dontsendone:
rcall Sendabyte
dec temp
brne stillsendingbinary
ret
;*******************
;*******************
;******************* MEASURE FREQUENCY
;*******************
;*******************
measurefreq:
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
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
;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 wirelessdat,tBCD2
ldi temp,$30
add wirelessdat,temp
rcall Sendabyte
mov inbytel,tBCD1
rcall sendbyte
mov inbytel,tBCD0 ;since leading digit on high byte is always zero, dont' sent it.
rcall sendbyte
ldi wirelessdat,$20
rcall Sendabyte
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
hellomessage:
.db "2313 frequency meter 2002.06.11 A RF LINK Dick Cappels"
.db $0A,$0D
.db "A=10ms,B=100ms,C=1s,D=10s,E=100s. Return or R=meausre "
.db $0A,$0D
.db "Max count = 65535, Max frequency < 4 MHz. "
.db $0A,$0D
.db $00,$00
resetmessage:
.db "Hardware reset initiated. "
.db $00,$00
crlfmessage:
.db $0A,$0D
.db 00,00
tenmsmessage:
.db "Timebase set to 10 ms Frequency is Count X100."
.db $0A,$0D
.db 00,00
hundredmsmessage:
.db "Timebase set to 100 ms. Frequency is Count X 10 "
.db $0A,$0D
.db 00,00
onesmessage:
.db "Timebase set to 1 s. Frequency is Count X 1."
.db $0A,$0D
.db 00,00
tensmessage:
.db "Timebase set to 10 s. Frequency is Count X 1/10. "
.db $0A,$0D
.db 00,00
hundredsmessage:
.db "Timebase set to 100 s. Frequency is Count X 1/100. "
.db $0A,$0D
.db 00,00
;///////////////RADIO RECEIVE BYTE ROUTINES///////////////////
;Try and decode a valid header and data byte. In the case or error, return with header = $00
ErrorQuitReceiving:
ldi header,$00
ret
TrytoGetbytemessage:
ldi header,$00
ldi temp,$00
mov runincount,temp
GetAnotherZero: ;Start of loop to gather minzeros zeros in runin code.
rcall Getbit
brts ErrorQuitReceiving ;It Getbit comes back with T set, its an error; start over.
brcs ErrorQuitReceiving ;If Getbit comes back with carry set, its a one, not a zero -start over
inc runincount
cpi runincount,minzeros
brmi GetAnotherZero
WaitForStartbit: ;Start of loop waiting for a one, which will be the start bit.
rcall Getbit
brts ErrorQuitReceiving ;It Getbit comes back with T set, its an error; start over.
brcc WaitForStartbit ;If its a zero, keep looking
;OK, Now we have the start bit, so the next 8 bits will be the header. Shift them in.
ldi temp,$08
mov runincount,temp
Getheader:
rcall Getbit
brts ErrorQuitReceiving ;It Getbit comes back with T set, its an error; Stop receiveing.
rol header ;Shift new bit into header byte
dec runincount
brne Getheader ;If all bits aren't in yet, go get another.
;At this point, one could tell whether this data is addressed to this machine, but I am going to leave
;address qualification to another part of the code. If time were very precious, one could stop
;execution of this part of the receive routine right here.
rcall Getbit ;Throw this one away. Its the start bit for the next byte
;It must be here so two bytes of zeros can't look like
;the runing code.
brts ErrorQuitReceiving ;It Getbit comes back with T set, its an error; start over.
;Now we have the runinbits, the start bit, and the header. Next is the data. Shift it in.
ldi temp,$08
mov runincount,temp
GetInbyte:
rcall Getbit
brts ErrorQuitReceiving ;It Getbit comes back with T set, its an error; Stop receiveing.
rol inbyte ;Shift new bit into input data byte
dec runincount
brne GetInbyte ;If all bits aren't in yet, go get another
ret ;RETURN WITH header IN header AND DATA IN INBYTE
Getbit: ;Subroutine to get a received bit.
;It returns the value of the received bit in the carry bit
;If this routine does not receive a valid data bit IT WILL NOT RETURN.
;If it is necessary to recover from this situation, the watchdog timer can be used.
;It measures the time between rising edges on the input pin (B3 originally).
;Successfult bit decoding will return with T flag clear. T flag =1 means error.
ldi tcounter,$00 ;Zero the counter.
;Count, waiting for pin to go low.
WaitForLow:
ldi delaycounter,$16 ;load with $16, 1 cycle takes 17.5 us
delayloop1:
dec delaycounter
brne delayloop1
inc tcounter
breq nobit
sbic PIND,RFrcvdat
rjmp WaitForLow
;Continue to count, waiting for pin to go high.
WaitForHigh:
ldi delaycounter,$16
delayloop2:
dec delaycounter
brne delayloop2
inc tcounter
breq nobit
sbis PIND,RFrcvdat
rjmp WaitForHigh
;Determine if we measured a "legal" bit and if so wheter it as a one of a zero.
;pcode:
;If tcount < $42 start over
;If tcount <$6E its a one
;If tcount is < $84 start over
;If tcount is >$DC start over
;Else, its a zero
cpi tcounter,$42
brmi nobit
cpi tcounter,$6E
brmi ItsAOne
cpi tcounter,$84
brmi nobit
cpi tcounter,$DC
brpl nobit
;If it gets here, its a valid zero.;
clt ;Clear T flag (no error), set the carry bit to show that its a zero and return.
clc
ret
ItsAOne:clt ;Clear T flag (error) set carry flag (data is a one), and return.
sec
ret
nobit: ;Error receiving a bit, set T flag and leave
set
ret
;/////////////////////////RADIO SEND BYTE ROUTINES/////////////////////////
Sendabyte:
;SEND RUNIN CODE (16 ZEROS)
ldi outbyte,$00 ;get ready to send 16 zeros
ldi temp,$0F ;$0F databits plus a zero "start bit"
mov bitsout,temp
clc ;no start bit (it goes as another zero)
rcall LateTransmitAByte ;jump into byte sending routine
;Send the header byte. The first and last bit in the headder is always a "1". Its the law.
mov outbyte,sendheader
rcall TransmitAByte
;(Ok, now the 16 bits of runin, the start bit, and the 8 bits of header have been sent.
;Time to send the data byte.
mov outbyte,wirelessdat
rcall TransmitAByte
ldi outbyte,$FF ;get ready to send 1 one, channel idle, to finish the last data bit.
ldi temp,$02 ;$01 databits plus a start bit
mov bitsout,temp
sec ;no start bit
rcall LateTransmitAByte ;jump into byte sending routine
ret ;finished sending the one byte message
TransmitAByte: ;send a byte out the wireless port
ldi temp,$09
mov bitsout,temp
sec ;start bit is a "one"
LateTransmitAByte:
SendACycle: ;a one or a zero, depending on the state of carry bit
sbi PORTD,RFxmitdat
ldi tcounter,$00
ldi delaycounter,$04
brcs notazero1
ldi delaycounter,$08
notazero1:
Delay:
dec tcounter
brne Delay
dec delaycounter
brne delay
cbi PORTD,RFxmitdat
ldi tcounter,$00
ldi delaycounter,$04
brcs notazero2
ldi delaycounter,$08
notazero2:
Delay2:
dec tcounter
brne Delay2
dec delaycounter
brne delay2
rol outbyte ;sent start bit and all 8 data bits yet?
dec bitsout
brne SendACycle ;if not send another bit
ret
;//////////END OF CODE FOR SEND ROUTINE///////////////
;www.projects.cappels.org/
;HOME