Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Communication options between 3 PICs

Status
Not open for further replies.

Andy_123

Member
I have USB PIC device based on 18F2450 or 18F2550

It controls only one external device that is taking almost all processing power using interrupt routines.
USB reads from this device 4 times a second mostly for display purposes.

Now I want to service two more external devices, each will be using own PIC18F2420 or similar.

So I need to transfer data from these two additional PICs to USB PIC so it
can be read by USB - complete device must have only one USB interface for all 3 PICs.

It will be up to 16 bytes from each device about 4-5 times per sec.
Also in time to time PC will have to write some data to all 3 PICs.

So what will be easiest way to do it? SPI, I2C?

Any ideas?
 
Last edited:
without checking the datasheets for your chips, id assume it has hardware usart, i2c, and spi so would really be down to personal preference.

i pertsonally prefer spi, but that just because ive used it more thsn i2c.
 
USB PIC 2450 does not have SPI/I2C, but I beleive 2455 and 2550 have it. Currently 2450 selected just for lowest possible cost.

Non-USB PICs of my choice 2220 and 2420 both have MSSP.
I need to stay with 5V PICs because I need 5V outputs
 
Haven't used the master slave SPI, but the master slave I2C works alright after you get it set up. There can be a whole slug of errata on both of these modes, so be careful and check your devices for rev no.

Setting up the slave Pic for the I2C interrupt routine seemed a real hassle for me. Hopefully your software supplies the libraries so you don't have to worry about it. A couple things of note would be to clock stretch the slave receive, and the slew rate adjustment in and around the 400kps mark. Should have no problem setting a 1000kbs clock rate (not exactly throughput).
 
I would use the EUSARTs.

There are three ways that I can think of for connecting them.

1) In a ring. Connect the transmit of one to the receive of the next. Your software in each PIC has to pass on any data that it hasn't originated.

2) Master - Slaves. Connect the transmit of the master to the receivers of the slave, and the transmits of the slaves to the receiver of the master. Your software in the slaves has to disable the transmitter when it is not transmitting. Fit a pull-up to the slave transmit line so that it idles high.

3) 1 node. Connect all the transmits and receives together, and add a pull-up. The software in everything has to disable the transmitters when not transmitting. As far as the software is concerned, this behaves like an RS485 connection.

The timing is probably easier with EUSART communication, as you can have pauses after each byte, and the EUSARTS do everything to receive or transmit a byte.
 
Thank you for quick responses:
I will be going USART route - looks like it's much easier and I have USART pins available. SPI pins are on port B and I am using all pins on port B and chnaging it will be much bigger problem.
 
1) In a ring. Connect the transmit of one to the receive of the next. Your software in each PIC has to pass on any data that it hasn't originated
This sounds like a good idea - any sample rotines in C for PIC18F for this ring configuration?
 
I thought I could pass on some routines I've made. It's a simple one-wire-bus, hence the name SWB. It supports upto 256 devices on one bus, all the hardware that is required is a pullup resistor to Vdd, and all the SWB-pins tied together. It's a bit slow, 2500Baud, but it is fast enough for many purposes. Sure it could be sped up, but when I wrote these routines I had simplicity and reability in mind. It employs a simple xor checksum too. I've used these routines to communicate with temperature probes around the house (~25m wire in total) and it works very realiably.

There are notes in routines what to change for 4, 8 and 20Mhz, though the Delay routines will have to be changed for different clocks. Essential is that one bit delay is 400µS and and the half bit 200µS.

The routines are non-stalling, that is to say if send or receive is called the routines will not lock up waiting for some event, but will timeout. Receive routine will wait about 50mS if no data is received before timing out.

Enjoy


Code:
			#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 ===
 
That looks like a nice bit of code - im gonna see if i can convert it to 12F629/675 as that would be very handy having a 1-wire bus on these little 8 pin devices.

:thumbsup:
 
Yes, it should convert just fine, as that is one of the main chips I have used it with, just modify the receive routine (comment and uncomment the needed lines) and here are the delays for 4Mhz

Code:
SWB_Delay1.0	movlw	0x83				;One bit delay 400µS 4Mhz
				movwf	delaya
SWB_Delay1.0_0	decfsz	delaya,	f
				goto	SWB_Delay1.0_0
				goto	$+1
				return

SWB_Delay0.5	movlw	0x41				;Half bit delay 200µS 4Mhz
				movwf	delaya
SWB_Delay0.5_0	decfsz	delaya,	f
				goto	SWB_Delay0.5_0
				return
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top