| CODE |
;********************************* ;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/////////////// |