...my colleague Brian wants all of his house rewired. Also, we have been toying around with placing temperature sensors around our office (which we need to prove that our airconditioning doesn't work).
After some thought about power-line RF drivers and such, we decided on RS485-type network at a low data rate (16kbps approx). When used with appropriate slew-rate limited drivers (MAX487) you can have relatively long stubs on your network. The network is terminated on both ends with 100R, with two 1k resistors biasing the network into a '0' state.
The temperature sensors I've built use just four active components: a DS1621 I2C temperature sensor, a MAX487 line driver, a PIC 12C509 and a 78L05. We're planning to use four-wire cable at home (+12V, GND and the two differential data lines) and cat-5 network cabling in the office.
Below is the code for the RS485 protocol that we use. This code is heavily macro'd, but should be readable. If enough people show interest then I'll put the include files needed and the rest of the (not quite working) temperature sensor application up on my or Brian's home page.
Frank
Note: This source requires PC graphics to show the diagrams.
Note 2: JBS - BTFSC+GOTO JBC - BTFSS+GOTO LOOP - DECFSZ+GOTO LOOPI - INCFSZ+GOTO STR - MOVWF LD - MOVF,W JZ - SKPNZ+GOTO FRAME..FEND sets up a set of local registers etc. ; IMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM; ; : : ; : 485.I : ; : : ; : RS-485 home automation bus interface. : ; : : ; : Copyright 1997, Frank A. Vorstenbosch. : ; : : ; HMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM< ; ; Uses: FLAG_IDSTRING - a flag, somewhere in a register ; FLAG_BROADCAST - a flag, somewhere in a register (can be same as FLAG_IDSTRING) ; RxTxD - a port pin for received and transmitted data ; TxEN - another port pin set high to enable transmitter frame 485 byte Shifter485 byte Counter485 byte TempA485 byte CRCLow485 byte CRCHigh485 byte Timer485 static OurTC fend 485 #define Timer485 TempC485 ; ZDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD? ; 3 3 ; 3 Description. 3 ; 3 3 ; @DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDY ; Two twisted pairs in cable, one for data and one for power/ground. Power is ; supplied as 9..12V as measured on the peripheral -- a transmitter may take ; approx. 120mA when using 100j terminators, or 104mA using 120j terminators ; in addition to any local power needs. ; ; The network protocol is designed to run on 4MHz 12-bit PICs at ~16kb/s in ; software. The longest packet takes less than 23ms to send, the shortest ; packet less than 2.5ms. CSMA is used to prevent two devices transmitting ; at the same time. ; Synchronous message format: ; ; - Start bit (100-250us duration) ; Peripherals try sending a start bit of 100-200us, then float the ; line, see if the line goes back to 0, and if it does, then drive ; it low for the remainder of the 20us, then start sending data. ; If the line is still driven to 1, then the peripheral will back ; off until the bus is idle for >100us, then try sending another ; start bit. The peripheral will try progressively longer start ; bits up to the limit of 200us. The host uses a 250us start bit ; to give it priority over all peripherals (this also means that ; most peripherals can ignore start bits <250us long). ; - Flags byte ; 7..6 - transfer direction: ; 00 - peripheral to host ; 01 - peer to peer (UNIM) ; 10 - host to peripheral ; 11 - reserved ; 5 - reserved ; 4..0 - data length ; - Peripheral or destination address byte or 255 for broadcast (Flags<7..6>=10) ; - Optional source address byte (Flags<7..6>=01) (UNIM) ; - Command byte (Flags<7..6>=10) or status byte (Flags<7..6>=00) ; Command ; 0 - Are you there? (Returns up to 31 byte ID string) ; 1 - Data poll ; 2..255 - User defined commands ; Status ; 0 - OK ; 1 - CRC error ; 2 - Command error ; 3 - Parameter error ; 4 - No (more) data ; 5..254 - User defined errors ; 255 - Data broadcast ; - 0..31 data bytes ; - CRC-16 over Flags, Address and Data bytes ; ZDDDDDDDDDDDD//DDDDDDDDDDD? Z ; Start line idle 3 100-250us 325us3 ; DDDDDDDDDDDDDDDDDY @DDDDY ; ZDDDD? Z ; 0-bit 325us325us3 ; Y @DDDDY ; ZDDDDDDDDDD? Z ; 1-bit 3 50us 325us3 ; Y @DDDDY ; ; The low period at the end of each byte is 50us rather than 25us, giving ; devices time to process the byte. Thus, the byte 0x54 would be transmitted as: ; ; ZDDDD? ZDDDDDDDDDD? ZDDDD? ZDDDDDDDDDD? ZDDDD? ZDDDDDDDDDD? ZDDDD? ZDDDD? Z ; 325us325us3 50us 325us325us325us3 50us 325us325us325us3 50us 325us325us325us325us3 50us 3 ; Y @DDDDY @DDDDY @DDDDY @DDDDY @DDDDY @DDDDY @DDDDY @DDDDDDDDDDY ; ZDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD? ; 3 3 ; 3 Local definitions. 3 ; 3 3 ; @DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDY ifndef FLAG_IDSTRING ifndef FLAG_BROADCAST error "FLAG_IDSTRING and/or FLAG_BROADCAST must be defined" endif #define FLAG_IDSTRING FLAG_BROADCAST endif ifndef FLAG_BROADCAST #define FLAG_BROADCAST FLAG_IDSTRING endif ; ZDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD? ; 3 3 ; 3 Receive a message from the RS485 bus. 3 ; 3 3 ; @DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDY ; ; In: W - Size of receive buffer ; fsr - pointer to first byte of receive buffer ; Act: Receive a message from the RS485 bus. ; Out: Receive buffer filled in with command and data bytes ; Counter485 - Number of data bytes ; W - error code ; 0 - no error ; 1 - message was not addressed to us ; 2 - message was too long for our buffer ; 3 - CRC error ; Regs: Shifter485, Counter485, fsr, tmp, TempA485, TempB485, TempC485 ; Note: Assumes that the start bit has already been detected. ; Uses one stack level internally, can only be called from top level ReceiveMessage str Counter485 TRACE 1,"ReceiveMessage" clr CRCHigh485 clr CRCLow485 jbs RxTxD,$ ; wait for start bit to end call RM_Byte ; receive flags & length byte retbc Shifter485,7,1 ; if peripheral-to-host then ignore it ld Shifter485 str TempA485 subwf Counter485,w retc 2 movlw 31 andwf Shifter485,w str Counter485 inc Counter485 ; count command as a data byte bcf FLAG_BROADCAST ; assume is's not a broadcast call RM_Byte ; receive device ID call DeviceID xorwf Shifter485,w ; is the message addressed to us personally? jz RM_Loop incf Shifter485,w ; or is it a broadcast? retnz 1 bsf FLAG_BROADCAST RM_Loop call RM_Byte ; receive command/data byte ld Shifter485 inc fsr str ind loop Counter485,RM_Loop call RM_Byte ; receive first CRC byte call RM_Byte ; receive second CRC byte ld TempA485 str Counter485 ld CRCHigh485 xorwf CRCLow485,w retnz 3 ; CRC error movlw 31 andwf Counter485 ; clear unwanted bits in the length byte retlw 0 ; zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz RM_Byte ldk Timer485,8<<<4 RMB_Loop jbc RxTxD,$ ; wait for bit to start clc RMB_Inner incfsz Timer485 ; time the length of the bit jbs RxTxD,RMB_Inner ; here the low nibble of Timer485 should be 6 or 13 btfsc Timer485,3 ; detect 1 bit if 9 counts or more stc ld status ; carry is LSB of status reg ister rl Shifter485 ; shift bit into register xorwf CRCLow485,w clrc rr CRCHigh485 rr CRCLow485 andlw 1 xorwf CRCLow485 movlw 0xa0 skipz xorwf CRCHigh485 movlw 0x0f iorwf Timer485 loopi Timer485,RMB_Loop retlw 0 ; ZDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD? ; 3 3 ; 3 Entry point to subroutines for SendMessage. 3 ; 3 3 ; @DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDY SM_Byte_tmp ld tmp ; [1] SM_Byte str Shifter485 ; [1] SM_Byte_Sh ldk Timer485,8<<4 ; [2] jp SMB_Loop ; [2] ; ZDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD? ; 3 3 ; 3 Send a message on the RS485 bus. 3 ; 3 3 ; @DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDY ; ; In: W - Number of data bytes in message ; tmp - Status byte ; fsr - pointer to first byte to transmit ; Act: Send a message on the RS485 bus. ; Out: W - error code ; 0 - no error ; 1 - failed to send message after 8 attempts ; Regs: Shifter485, Counter485, CRCHigh485, CRCLow485, Timer485, fsr, tmp, (OurTC) ; Note: Uses one stack level internally, can only be called from top level SendMessage str Shifter485 TRACE 1,"SendMessage" ldk Counter485,8 clr CRCHigh485 clr CRCLow485 clr gpio ; set TxEN low movlw NORMAL_TRIS ; float RxTxD (with pull-up) tris gpio SM_WaitIdle movlw 30 subwf OurTC,w ; shorter delay if longer start bit used str Timer485 SMWI_Loop jbs RxTxD,SM_WaitIdle ; [2] wait for bus to be idle for 100..150us loopi Timer485,SMWI_Loop ; [3] movlw TxEN_MASK|RxTxD_MASK btfss RxTxD ; last chance: if activity on bus then don't set TxEN high str gpio ; drive TxEN high jbc TxEN,SM_WaitIdle ; wait some more if the STR wasn't executed movlw NORMAL_TRIS&~RxTxD_MASK ; quickly drive RxTxD to high level tris gpio movlw NORMAL_TRIS ; and then float it again tris gpio ifdef HOST movlw 36 ; host uses 250us timeout else movlw 13 addwf OurTC,w addwf OurTC,w endif str Timer485 ; wait for 100..200us SM_SendStartH call Delay4Ticks ; [4] loop Timer485,SM_SendStartH ; [3] clr gpio ; disable transmitter brake ; it takes ~2.5us for the MAX487 to go into read mode movlw TxEN_MASK ; moved here to prevent back-to-back I/O accesses jbs RxTxD,SM_BackOff ; if RxTxD still high, then back off str gpio ; enable transmitter again movlw NORMAL_TRIS&~RxTxD_MASK ; start driving RxTxD pin (to low state) tris gpio call Delay9Ticks ld Shifter485 andlw 31 str Counter485 call SM_Byte_Sh ; send flags & length byte call Delay13Ticks call DeviceID call SM_Byte ; send our ID call Delay16Ticks call SM_Byte_tmp ; send status byte jbs FLAG_IDSTRING,SM_LoopCode test Counter485 jz SM_CRCNoData ; [2] call Delay8Ticks ; [8] jp SMLD_Enter ; [2] SM_LoopData call Delay12Ticks ; [12] SMLD_Enter ld ind inc fsr call SM_Byte ; send data byte loop Counter485,SM_LoopData jp SM_SendCRC SM_LoopCode brake movlw 31 andwf fsr,w str tmp movlw IDString addwf tmp,w call PCW inc fsr call SM_Byte ; send ID string byte loop Counter485,SM_LoopCode brake SM_SendCRC brake nop SM_CRCNoData call Delay8Ticks ld CRCHigh485 str tmp ld CRCLow485 call SM_Byte ; send first CRC byte call Delay16Ticks call SM_Byte_tmp ; send second CRC byte movlw NORMAL_TRIS clr gpio ; disable transmitter tris gpio ; float RxTxD retlw 0 ; return OK ; zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz SM_BackOff incf OurTC,w ; increment our timer andlw 7 ; only 3 LSBs used str OurTC decfsz Counter485,f ; after 8 attempts, return error to caller retlw 1 jp SM_WaitIdle ; DDDDD Subroutine for SendMessage DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD SMB_Loop bsf RxTxD ld CRCLow485 xorwf Shifter485,w clc rr CRCHigh485 rr CRCLow485 andlw 1 xorwf CRCLow485 ; if W=0, then do nothing, skipz ; otherwise CRC^=0xa001 movlw 0xa0 xorwf CRCHigh485 SMB_Wait1 inc Timer485 ; [1] 25us high bit for a '0' movlw 0xf0 ; [1] jbc Timer485,1,SMB_Wait1 ; [3/2] rl Shifter485 jnc SMB_ShortBit SMB_Wait2 inc Timer485 ; [1] 50us high bit for a '1' jbc Timer485,3,SMB_Wait2 ; [3/2] brake nop SMB_ShortBit andwf Timer485 ; W=0xf0 here bcf RxTxD SMB_Wait3 inc Timer485 ; [1] 25us low bit nop ; [1] jbc Timer485,2,SMB_Wait3 ; [3/2] movlw 0x0f iorwf Timer485 loopi Timer485,SMB_Loop retlw 0 ; DDDDD EOF 485.I DDDDD
GEORGE R. FORSE Says:
Hello,James Newton replies: See: http://web.archive.org/web/20030212211144/www.falstaff.demon.co.uk/picres.html Franks site is no more, but thanks to the wayback...
I would like to use the program, but don't have the macros. When I try
to download them from sites that indicate where to download them (Google),
I am not able to access them I have spent many hours trying to find them.
Please advise. TIA George
Interested:
Comments:
Code: