# Why does SPI work one way but not the other?

#### mik3ca

##### Member
I'm trying to make a two way master and slave system for my microcontrollers.
The small microcontroller runs at about 3.6Mhz is the master and the big slave microcontroller runs at 22.1184Mhz which is a 6x speed difference.

The small micro (at89c2051) has its clock driven from the ALE of the at89S52. The ALE is configured to always run and neither microcontroller performs any external memory operations.

On initialization the small micro is the master and the large one is slave. The small master calls its docpucmd function to send test data to the slave and the slave correctly responds by constantly calling SPISS function until it finds valid data which it then processes. Then later the master calls do1cpucmd with a command for the micros to switch roles.

Now the faster microcontroller is the master and the small one is slave. So far everything goes well except for the fact that the "slow" small slave isn't recognizing the data the master sends to it yet I made very small modifications to the code.

The big micro calls its own docpucmd (shown halfway down in code). and the slow small slave constantly calls SPIGS for valid data but I learn that I always get zeros coming from SPIGS.

Is my timing that terrible or am I doing something else wrong?

Code:
;********************************************************
;code for mini micro that runs at 3.686400Mhz (6x slower)
;********************************************************

;This function runs when master and slave switch roles
do1cpucmd:
acall SPISM
ret

;This function runs when master wants to send a 1-byte
;command from accumulator and wants a 1-byte response right away
docpucmd:
acall SPISM
clr A
acall SPISM
ret

;slow master (mini uC=master) A=new input > A=old output
SPISM:
clr SSEL ;lower slave-select pin
mov R7,#8h ;8 bits to transfer
setb SPICLK ;raise clock
setb DOUT ;Make output high for now
setb DIN ;Make input high so micro accepts input
nop ;Stall a bit
nop
nop
m:
jnb SPICLK,$;Stall until remote micro raises clock line mov C,DIN ;Read one bit of input rrc A ;shove it into accumulator mov DOUT,C ;send oldest bit back out to slave clr SPICLK ;lower clock line nop ;wait setb SPICLK ;raise clock line nop ;wait djnz R7,m ;repeat for remaining 7 bits jnb SPICLK,$ ;wait until remote micro raises clock line
setb DOUT ;set lines high
setb DIN
nop
setb SSEL ;including slave select
ret

;slow micro as slave
SPIGS:
setb SSEL ;make slave select ready
jnb SSEL,nospiop ;if slave isnt selected by master
setb DIN
setb DOUT
clr A
setb C ;reset lines and exit
ret
nospiop:
mov R7,#8h ;setup 8 bits
rrc A
mov DOUT,C
s: ;process bits. Eliminated nops because slave is slow
setb SPICLK
jnb SPICLK,$jb SPICLK,$
clr SPICLK
mov C,DIN
rrc A
mov DOUT,C
djnz R7,s
setb SPICLK
jnb SSEL,$clr SPICLK setb DIN setb DOUT clr C ret ;******************************************************** ;code for large micro that runs at 22.1184Mhz (faster) ;******************************************************** ;This is executed on big micro when big micro is slave and has nothing to do. SPISS: setb SSEL ;tell master (mini micro) we are ready jnb SSEL,nospiop ;see if they lowered slave select setb DIN ;they didnt setb DOUT ;so make lines high for sanity nop ;and... clr A ;return nothing for accumulator ret nospiop: mov R7,#8h ;setup 8 bit transfer mov A,B ;load last command result into accumulator rrc A ;get bit mov DOUT,C ;and send it out s: setb SPICLK ;tell master we are ready jnb SPICLK,$ ;wait until master makes line high
jb SPICLK,$;wait until master makes line low clr SPICLK ;we make line low to indicate busy mov C,DIN ;get bit and... rrc A ;move it into accumulator and old bit... mov DOUT,C ;back to the master. djnz R7,s ;repeat for 7 more bits setb SPICLK ;tell master we are ready again jnb SSEL,$ ;wait until master raises slave select
clr SPICLK ;tell master we are busy
setb DIN ;reset other lines
setb DOUT
acall SPIdelay ;add delay to let master keep up
ret

;execute small CPU command from big CPU.
;C=1=small CPU too busy
; *** FUNCTION CURRENTLY NOT WORKING CORRECTLY ***
docpucmd:
setb C
jb SPIPROC,nolcpuproc
mov A,R5 ;import parameter
acall SPIGM ;send it out to little cpu slave
mov A,R6 ;import next parameter
acall SPIGM ;send it out to little cpu slave
clr A ;Send nothing. we only want response
acall SPIGM ;get 1st parameter response
mov R5,A
clr A
acall SPIGM ;get 2nd parameter response
mov R6,A
clr C
nolcpuproc:
ret

;Big micro as master
SPIGM:
clr SSEL ;tell slave were ready
mov R7,#8h ;8 bits
setb SPICLK ;setup everything
setb DOUT
setb DIN
acall SPIdelay ;delay so slave (small micro) can catch up
m:
jnb SPICLK,$;wait until slave raises clock acall SPIdelay ;delay so small micro can process stuff mov C,DIN ;get bit rrc A ;put in accumulator mov DOUT,C ;send old bit out clr SPICLK ;lower clock acall SPIdelay ;and stall so slave can keep up setb SPICLK ;raise clock acall SPIdelay ;stall again so slave can keep up djnz R7,m ;repeat for 7 more bits jnb SPICLK,$ ;wait for slave to be ready
setb DOUT
setb DIN
setb SSEL ;reset slave select
acall SPIdelay ;and stall more
ret

;This function applies to both micros. It creates about a 27 clock cycle delay

SPIdelay:
push B
mov B,#10h
djnz B,\$
pop B
ret

#### Cicero

##### Active Member
Am I understanding you correctly, you're switching from master to slave mid operation? And you're bitbanging your SPI yourself.

I don't see it in the code, so are you sure you're changing your inputs to outputs when you switch master/slave config? As in re-configuring your MISO as a MOSI, and vice versa?

On a side note, when you do this make do you have series resistors mid line, because there could easily be a time when you have both configured as outputs and they could be at differing levels.

#### mik3ca

##### Member
im going to look into swapping the roles of slave select and spi clock line when reassigning the master and slave roles. both the 8051s have internal resistors

#### Cicero

##### Active Member
im going to look into swapping the roles of slave select and spi clock line when reassigning the master and slave roles. both the 8051s have internal resistors
Just to make sure you fully understand me.

Slave would have the following connections:
• SS - input
• SCK - input
• MOSI - input
• MISO - output

Master needs the following:
• SS - output
• SCK - output
• MOSI - output
• MISO - input

So when you switch roles, you need to switch every line.

On the resistors part, I'm not talking about internal pullups. I mean series resistors between the connections.
The reason why is because at some point in your code you're going to decide to switch roles, and have to change all the inputs to outputs, and outputs to inputs as above etc. Now lets say your start by switching your slave SS line to an output, and it immediately goes low before your data register is 0x00. Now, imagine your old master device's hasn't swapped just yet, and its SS is also an output and is set high. This wasn't a problem before when your slave SS was an input, but now you've got a short! (even if just for a split second) A small series resistor will protect against this situation. The other method if you're dead set against resistors is having to be really careful to change to high impedance states first, like change all your outputs to inputs first, then once you're sure everything is complete, change your inputs to outputs in a very controlled fashion.

#### mik3ca

##### Member
i already assessed the series resistors situation and i find i dont need them becauae the 8051's gpio lines are always high impedance or low. never a solid vcc so short circuit is impossible in my situation

#### mik3ca

##### Member
Im still have no luck. Is my timing off? or maybe theres a simpler way out?

#### Cicero

##### Active Member
I see, forgot 8051's don't have specific DDR's.

In your code, I'm confused as to why when in slave mode you're setting and clearing the clock line? Surely that should be left to the master? You seem to be using it as a secondary handshaking routine, but it seems confusing.

#### mik3ca

##### Member
ok we might as well mark this thread as closed because I decided to make the SPI routine one-direction only.