Why does SPI work one way but not the other?

Status
Not open for further replies.

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
mov A,B ;load data in
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
 
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.
 
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
 
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.
 
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
 
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.
 
ok we might as well mark this thread as closed because I decided to make the SPI routine one-direction only.
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…