;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

;******************************************************
;Send and receive FM data, pass through UART at 9600 baud
;******************************************************
   
.include "2313def.inc"
 ; 	memory index	=r0	; used for fetching strings from memory
.def	bitsout		= r02	; counter for bits sent (couple also use a delay counter)
.def	sendheader	= r03	;headerr to place on outgoing RF byte
.def	receiveaddress	= r04	; Address mask for RF received data header 
   
.def	temp 		= r16	; Scratch register.
.def	tcounter	= r17   ; 8 Bit loop timing counter.
.def	delaycounter	= r18	; Delay to increase time measureble with 8 bit tcounter.
   
.def	runincount	= r19	; Used to keep track of consecutive zeros detected in runin code.
.def	wirelessdat 	= r19	; data to be sent via wireless port
			;REGISTER 19 USED TWICE ON PURPOSE
   
.def	inbyte		= r20	; Decoded data byte could be same register as runincoung
   
.def	header		= r21
.def	outbyte		= r21	;Byte value to be sent (input to send routine)
			;REGISTER 21 USED TWICE ON PURPOSE
   
.equ	minzeros	= 9	;Minimum number of consecutive zeros to qualify as run-in code
   
.equ	defaultsendheader = $10 ;Default value of RF sendingheaderr.
.equ	defaultreceiveaddress =$02 ;Default value for RF receiveing address in header
   
   
.equ	RFxmitdat	= 5	Port bit used to send transmit data to RF transmitter
.equ	RFrcvdat	= 6	Port bit used to receive data from RF receiver
   
				;SETTING UP THE UART
.equ	clock		= 4000000		;clock frequency
.equ	baudrate	= 9600		;choose a baud rate
.equ	baudkonstant 	= (clock/(16*baudrate))-1
   
; Used for memory index	= ZL,ZH
   
;PORT D
;	bit 0	this pin used for UART RX pin
;	bit 1	this pinused for UART TX pin
;	bit 5	Send data to RF transmitter
;	bit 6	receive data from RF receiver
   
;EEPROM
;0 not used
;1 sendheader
;2 receiveaddress
   
   
.cseg
.ORG $0000
		;START INITIALIZATION
	ldi     temp,low(ramend)
	out     spl,temp     	;Set the 8 bit stack pointer to the top of ram.
	
	ldi	temp,baudkonstant;load baud rate into the UART 
	out	ubrr,temp	
	
	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
	
;	ldi	temp,defaultsendheader	;Establish the default RF sendingheaderr
;	mov	sendheader,temp				;88888TEMPRORAILLY COMMENTED OUT!!!!!!!!!!!!
	
				;Fetch default transmit address from EEPROM
	ldi	temp,$01
	out	eear,temp		;Set up the read address
	sbi	eecr,eere		;Trigger the read
	in	temp,eedr		;Get the data into temp
	mov	sendheader,temp 
	
					;Fetch default receive address from EEPROM
	ldi	temp,$02
	out	eear,temp		;Set up the read address
	sbi	eecr,eere		;Trigger the read
	in	temp,eedr		;Get the data into temp
	mov	receiveaddress,temp 
	
;	mov	receiveaddress,temp		;Put it into receiveaddress register.
	
	sbi	ucr,rxen	;enable UART receive function	
	
		;FINISHED WITH THE INITIALIZATION
		;SEND GREETING
	rcall	greeting
   
	
main:	;MAIN FOREGROUND LOOP FOR THIS MACHINE
   
rcall rsreceiveit	;Check for RS-232 data coming in and send via RF link if anything's ready
rcall rssendit		;Check for incoming RF data. If coming in, decode and send to RS-232 device.
   
rjmp main
   
   
   
rsreceiveit:	;CHECK FOR DATA FROM TERMINAL. IF PRESENT, RESEND IT. Trap and intrepret Escape sequences.
	sbis	usr,rxc			;wait for data
	rjmp    norsdata
	in	wirelessdat,udr		;read data
	cpi	wirelessdat,$1B		;See if this is an escape character.
	brne	NotEscapeCommand	;If it keep going as if nothing happened -skip to bottom
	
receivecommand:				;If that was an Escape, then wait for command.
	sbis	usr,rxc			;wait for data
	rjmp    receivecommand
	in	wirelessdat,udr		;read command
	andi	wirelessdat,$DF		;And with $DF to make ASCII upper-casce.
	
receiveparameter:			;Receive the parameter
	sbis	usr,rxc			;wait for data
	rjmp    receiveparameter
	in	temp,udr		;Read parameter (interpreded as in the range of 0..7).
	subi	temp,$30		;Then make into address to insert into the header.
	andi	temp,$07
	lsl	temp
	lsl	temp
	lsl	temp
	mov	bitsout,temp		;Store results in bitsout for now.
	
			;Now interpret the command
   
	cpi	wirelessdat,$54		;"T" set transmit address
	brne	NotTransmit
	mov	temp,bitsout
	mov	sendheader,temp		;Store the address
	
wrat:	sbic	eecr,eewe		;Wait for EEPROM write to not be busy
	rjmp	wrat
	ldi	temp,$01
	out	eear,temp		;Set up the write address
	mov	temp,sendheader		
	out	eedr,temp		;Set up the  write data
	sbi	eecr,eemwe
	sbi	eecr,eewe		;Trigger the write
	
		
NotTransmit:
   
	cpi	wirelessdat,$52		;"R" set receive  address
	brne	NotReceive
	mov	receiveaddress,bitsout	;Put the number in the address receive register
wrar:	sbic	eecr,eewe		;Wait for EEPROM write to not be busy
	rjmp	wrar
	ldi	temp,$02
	out	eear,temp		;Set up the write address
	mov	temp,receiveaddress		
	out	eedr,temp		;Set up the  write data
	sbi	eecr,eemwe
	sbi	eecr,eewe		;Trigger the write
	
	NotReceive:	
   
	
	ret				;End of processing escape.
	
	
NotEscapeCommand:
;	com	wirelessdat		;///////////////////TEST ONLY INVERTED DATA if not commented out///////////
	rcall 	sendabyte
norsdata:	
ret
     
   
   
rs_send:		;SEND CONTENTS OF TEMP VIA SERIAL PORT
     sbi 	ucr,txen     ;set sender bit
     sbis  	usr,udre     ;wait till register is cleared
     rjmp 	rs_send     
     out	udr,temp     ;send the contents of temp.
     cbi  	ucr,txen     ;clear sender bit
     ret               ;go back
   
   
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 	temp,r0
     rcall	rs_send
     adiw 	ZL,1               ; Increment Z registers
     rjmp 	sendstring
finishsendstering:
     ret
   
greeting:
     ldi 	ZH,high(2*gteetingmessage)     ; Load high part of byte address into ZH
     ldi	ZL,low(2*gteetingmessage)     ; Load low part of byte address into ZL
     rcall	sendstring          ; sent it.
   
     mov	temp,sendheader		;display defualt transmit address
     andi	temp,$78	;mask off all but the address
     lsr	temp	;shift to right to make address into a binary number
     lsr     	temp
     lsr	temp
     ori	temp,$30	;make into an ascii number
     rcall	rs_send
     
   
    ldi 	ZH,high(2*greetingsecondpart)     ; Load high part of byte address into ZH
    ldi		ZL,low(2*greetingsecondpart)     ; Load low part of byte address into ZL
    rcall	sendstring          ; sent it.
  
   
     mov	temp,receiveaddress		;display defualt receive address
     andi	temp,$78	;mask off all but the address
     lsr	temp	;shift to right to make address into a binary number
     lsr     	temp
     lsr	temp
     ori	temp,$30	;make into an ascii number
     rcall	rs_send  
     
     			;print some blank lines
     ldi 	ZH,high(2*crlfs)     ; Load high part of byte address into ZH
     ldi	ZL,low(2*crlfs)     ; Load low part of byte address into ZL
     rcall	sendstring  
     
     
     ret
   
   
   
   
gteetingmessage:
.db	$0A,$0D
.db	"RF interface.              02JUN2002 Dick Cappels."
.db	$0A,$0D
.db	"To change receive address, type <ESC>, R, <Address> "
.db	$0A,$0D
.db	"To change transmit address, type <ESC>, T, <Address>"
.db	$0A,$0D
.db	"Where the valid range of address is 0..7"
.db	$0A,$0D
.db	"Default transmit address is set to  = $0"
.db	$00,00
   
greetingsecondpart:
.db	$0A,$0D
.db	"Default receive address is set to = $0"
.db	$00,00
   
crlfs:
.db	$0A,$0D
.db	$0A,$0D
.db	$0A,$0D
.db	$00,00
   
;/////////////////////////////////////////////////////////////////////////////////////////////////////
rssendit:	;CHECK FOR DATA FROM RADIO RECEIVER AND IF PRESENT, SEND TO TERMINAL
	
	rcall	TryToGetbytemessage ; returns with header in header and data in inbyte 
	andi	header,$78		;mask off all but the address part of the header
	cp	header,receiveaddress		;check the header to see if header matches address
	brne	notforme		;if not for address $01 then don't send it on.
	mov	temp,inbyte		;if it is addressed tho this machine then send it on to terminal
   
   
   
waittosend:
	sbi	ucr,txen	;set sender bit
	sbis	usr,udre	;wait till register is cleared
	rjmp	waittosend	
	out	udr,temp	;send the recived data
	cbi	ucr,txen	;clear sender bit
		
notforme:
ret
   
   
;///////////////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///////////////
;http://projects.cappels.org/
   
   ;HOME