#define SWB_Pin PortE, 0
#define SWB_Error Flags_Reg, 0 ;Flags used by SWB-routines
#define SWB_Last_Byte Flags_Reg, 1
#define SWB_First_Byte Flags_Reg, 2
#define SWB_New_Data Flags_Reg, 3
#define SWB_Adr_Done Flags_Reg, 4
SWB_Address1 Equ d'70' ;Own adress
SWB_Address2 Equ d'0' ;Secondary address, can be zero on all devices then when sent to zero, all will receive data
cblock 0x20 ;76
SWB_Adr ;Single wire bus SFRs
SWB_Bytes
SWB_ChkSum
SWB_Temp
SWB_Timer
endc
; ================================
; Single Wire Bus Routines
; ================================
;Version 1.4
;Last modified: 28.8.2008
;To use receive, device address has to be set in SWB_Adress1 (and SWB_Adress2).
;The payload will be located in GPRs starting from address given in W. Received
;number of databyes (i.e. not checksum or address) will be returned in SWB_Bytes
;Routine automatically checks the checksum, and if uncorrupted data is received
;SWB_New_Data is set. Transmitter's address is saved in SWB_Adr. Routine will not
;receive next packet if SWB_New_Data is set (has to be cleared manually). If an
;error occurs SWB_Error will be set (checksum or synk). Because the error flag
;is cleared when receive routine is called, the flag will be lost if not checked
;before.
SWB_Receive btfsc SWB_New_Data ;If old data not processed, do nothing
return
movwf FSR ;Save address for data to be transmitted
clrf SWB_Timer ;Init timer
clrf SWB_ChkSum ;Init cheksum
clrf SWB_Bytes ;Init byte counter
bsf SWB_First_Byte ;Set first byte flag
bcf SWB_Adr_Done ;Clear address received bit
bcf SWB_Last_Byte ;Clear last byte flag
bcf SWB_Error ;Clear error flag
bsf Status, rp0 ;Bank0
bsf SWB_Pin ;Make pin input if not already
bcf Status, rp0 ;Bank1
SWB_WaitFoStart btfss SWB_Pin ;Test if low
goto SWB_CapturStart ;Yes
call SWB_Delay0.5 ;Else increment timeout
incfsz SWB_Timer, f ;Timeout ~= 256*200µS = 51.2mS
goto SWB_WaitFoStart
return ;If timeout, return
SWB_CapturStart clrf SWB_Timer ;Reset timer
goto $+1 ;Uncomment all for 20Mhz
goto $+1 ;
goto $+1 ;
goto $+1 ;
goto $+1 ;
goto $+1 ;Comment to here for 8Mhz
goto $+1 ;
nop ;Comment allfor 4Mhz
btfsc SWB_Pin ;Wait for high
goto SWB_RecSy_Byte-1 ;If goes high before timeout, synchronize
incfsz SWB_Timer, f
goto SWB_CapturStart+1 ;Loop till captured, one increment must be ~= 3,90625mS
return ;If too long, exit
call SWB_Delay0.5 ;Wait a half bit of the first synk bit
SWB_RecSy_Byte btfss SWB_Pin ;Test if synk byte is low?
goto SWB_Recv_Stop ;Last byte is to be received now if low.
; The synk bit is theoretically max 200µS long (beacuse half is lost in the receive loop)
movlw (d'255' - d'250') ;20Mhz So let's add some offset in the synk byte
; movlw (d'255' - d'100') ;8Mhz timeout. Let's say, almost double the theoretical
; movlw (d'255' - d'50') ;4Mhz max, about 350µS to give it some headroom
movwf SWB_Timer ;If pin high, syncronize
SWB_Synk_Wait btfss SWB_Pin
goto SWB_Recv_Data ;When pin goes low, receive is syncronised
goto $+1
incfsz SWB_Timer, f
goto SWB_Synk_Wait ;One loop takes 7 cykles
btfss SWB_First_Byte ;Synk error on first byte doesn't count as an error, might have been false trigger
bsf SWB_Error ;If no falling edge comes within timeout => Error
return
SWB_Recv_Data call SWB_Delay1.0 ;Skip over low part in synk
call SWB_Delay0.5 ;Skip to middle of bit
movlw d'8' ;Use timer as counter
movwf SWB_Timer ;Eight bits to receive...
SWB_Recv_Data_L bsf Status, c ;Set C
btfss SWB_Pin ;Is pin low?
bcf Status, c ;Yes? set C low
rlf SWB_Temp, f ;Save bit
call SWB_Delay1.0 ;Wait to next bit
decfsz SWB_Timer, f ;If counter zero, all bits received.
goto SWB_Recv_Data_L ;If not, get next
; Do checksum directly after
movf SWB_Temp, w ;Data to w
xorwf SWB_ChkSum, f ;Xor received byte with other received
SWB_Decode_Adre btfss SWB_First_Byte ;Test if first received byte
goto SWB_Senders_Adr ;If not continue
bcf SWB_First_Byte ;First byte received
movf SWB_Temp, w ;Compare data with adress
xorlw SWB_Address1
btfsc Status, z
goto SWB_RecSy_Byte ;Adress match, receive data
movf SWB_Temp, w ;Compare data secondary adress
xorlw SWB_Address2
btfsc Status, z
goto SWB_RecSy_Byte ;Adress match, receive data
return ;If no match do nothing
SWB_Senders_Adr btfsc SWB_Adr_Done ;Has transmitters address been received? (second byte)
goto SWB_Save_Data ;Yes?
bsf SWB_Adr_Done ;No? Set flag that now it has been
movf SWB_Temp, w
movwf SWB_Adr ;Save it in SWB_Adr
goto SWB_RecSy_Byte
SWB_Save_Data btfsc SWB_Last_Byte ;Was it the last byte to be received?
goto SWB_Chk_ChkSum ;Then do not save, but test checksum
movf SWB_Temp, w ;Data to w
movwf Indf ;Save data
incf FSR, f ;Pointer to next register
incf SWB_Bytes, f ;Increment received bytes counter
goto SWB_RecSy_Byte
SWB_Chk_ChkSum movf SWB_ChkSum, f ;Match?
btfss Status, z ;ChkSum = 0 if match
goto SWB_ChkSumError
bsf SWB_New_Data
return
SWB_ChkSumError bsf SWB_Error
return
SWB_Recv_Stop bsf SWB_Last_Byte
call SWB_Delay1.0 ;Skip the stopbit
goto SWB_Synk_Wait-2 ;Continue
;To send something, SWB_Bytes has to be loaded with the number of data bytes to be
;sent (i.e. not checksum or address). SWB_Adr has to be loaded with the recipient's
;address. The payload must be loaded into GPRs in order, starting from address
;passed in W.
SWB_Send movwf FSR ;Save address where to save data
decf FSR, f ;Decrement by one, loading routine increments by one
incf SWB_Bytes, f ;Loading routine decrements by one before it sends
clrf SWB_ChkSum ;Init checksum
bcf SWB_Adr_Done ;Clear address sent bit
bcf SWB_Last_Byte ;Clear last byte flag
bsf Status, rp0 ;Bank0
bcf SWB_Pin ;Make pin output
bcf Status, rp0 ;Bank1
bcf SWB_Pin ;Send start bit
call SWB_Delay1.0
call SWB_Delay1.0
call SWB_Delay0.5
movf SWB_Adr, w ;Send receiver address first
movwf SWB_Temp
SWB_Send_SyByte bsf SWB_Pin ;Send synk pulse
call SWB_Delay1.0
bcf SWB_Pin
call SWB_Delay1.0
movlw d'8' ;Shift out 8 bits per byte...
movwf SWB_Timer ;Timer functions as counter
SWB_Send_Byte_L rlf SWB_Temp, f ;Shift data out
btfss Status, c ;Set pin accordingly
goto $+3
bsf SWB_Pin
goto $+2
bcf SWB_Pin
call SWB_Delay1.0 ;Wait one bit
decfsz SWB_Timer, f ;Send all bits
goto SWB_Send_Byte_L
rlf SWB_Temp, f ;Shift once more to make a complete round
movf SWB_Temp, w ;Do checksum directly after
xorwf SWB_ChkSum, f
SWB_OwnAdr_Sent btfsc SWB_Adr_Done ;Has own address been sent?
goto SWB_Next_Byte
bsf SWB_Adr_Done ;No?
movlw SWB_Address1 ;Load own address to be sent
movwf SWB_Temp
goto SWB_Send_SyByte
SWB_Next_Byte btfsc SWB_Last_Byte ;Was this the last byte to be sent? (ie. the checksum)
goto SWB_Send_Done ;If was, set pin to input again
incf FSR, f ;Else, pointer to next byte
movf Indf, w ;Load next byte to be sent
movwf SWB_Temp
decfsz SWB_Bytes, f ;Was it the last byte?
goto SWB_Send_SyByte ;No, send next
bsf SWB_Last_Byte ;Yes, set indicator
movf SWB_ChkSum, w ;Send checksum
movwf SWB_Temp
bcf SWB_Pin ;Send stop bit
call SWB_Delay1.0
goto SWB_Send_SyByte
SWB_Send_Done bsf Status, rp0 ;Bank0
bsf SWB_Pin ;Make pin input
bcf Status, rp0 ;Bank1
return
SWB_Delay1.0 movlw 0x8E ;One bit delay 400µS
movwf delaya
movlw 0x02
movwf delayb
SWB_Delay1.0_0 decfsz delaya, f
goto $+2
decfsz delayb, f
goto SWB_Delay1.0_0
goto $+1
nop
return
SWB_Delay0.5 movlw 0xC6 ;Half bit delay 200µS
movwf delaya
movlw 0x01
movwf delayb
SWB_Delay0.5_0 decfsz delaya, f
goto $+2
decfsz delayb, f
goto SWB_Delay0.5_0
goto $+1
nop
return
; === SWB End ===