0
\$\begingroup\$

I'm trying to do a rather simple data transaction between two 8051 microcontrollers using a variant of the SPI protocol. The large one is a slave and the small one is a master.

The documentation for the slave (larger micro) states the clock output speed is 6x slower compared to its external crystal speed. Because of this, the master also runs 6x slower I added a bunch of nops (no-operation) commands in my code for the slave.

Hardware wise, the circuit is on a PCB and the track going to the clock is roughly 40mils wide and the other tracks are 12mils wide. Their lengths are less than one inch and the clearance for all tracks is at least 12mils.

After various tests, the SSEL (aka "slave select") line does go low when the master sets it low, but the slave does not make the SPICLK (aka "SPI clock") line high which is what it should be doing.

What am I doing wrong?

;Big micro (slave) code

SPICLK bit P3.6
DIN bit P2.5
DOUT bit P2.6
SSEL bit P2.7

org 0h
setb DIN
setb SSEL
clr SPICLK
debug:
    acall SPISS ;stall until remote does valid cmd
    jc debug ;carry = remote didn't set low yet
    jz debug ;zero = invalid command
    ;process code based on accumulator value
ret

;SPI slave routine
SPISS:
    setb SSEL ;Set high so master can set it low
    nop     ;wait 6 cycles because master is 6x slower
    nop
    nop
    nop
    nop
    nop
    jnb SSEL,nospiop ;if line isn't taken low
    setb C     ;then set carry
    ret        ;and return. This always happens which is a problem!
    nospiop:
    mov R7,#8h     ;Setup 8 bits
    s:
    setb SPICLK    ;tell other micro we are ready
    nop         ;let it catch up
    nop
    nop
    nop
    nop
    nop
    clr HWLED   ;turn our light on
    jb SPICLK,$ ;wait until master lowers clock line
    clr SPICLK  ;we keep it low to make master wait
    mov C,DIN   ;get bit
    setb HWLED  ;turn off light
    rrc A   ;shift into the byte
    mov DOUT,C  ;and take other bit as output
    djnz R7,s   ;continue for remaining 7 bits
    setb SPICLK ;tell micro we are ready again
    nop
    nop
    nop
    nop
    nop
    nop
    clr HWLED   ;turn on light
    jnb SSEL,$  ;wait until master turns slave select to high
    clr SPICLK  ;tell micro we are always busy
    setb DOUT   ;reset other values
    setb DIN
    setb HWLED
    clr C       ;C=0 for normal completion
ret








;Little master micro code


DOUT bit P1.1
DIN bit P1.2
SSEL bit P1.3
SPICLK bit P3.2

org 0h
mov A,#COMMAND ;Some value irrelevant to the problem
acall docpucmd
sjmp $

docpucmd:
    acall SPISM ;Send command out but we don't care what comes back
    clr A         ;Make command as zero..
    acall SPISM ;because now we want the result to the last command
    clr C
ret

;Master SPI routine
SPISM:
  setb SPICLK   ;Make clock line so slave can pull it low
  setb DIN  ;Make input high so slave can set it
  clr SSEL  ;Lower slave select to start transaction
  mov R7,#8h    ;8 bits to transfer
  m:
      nop       ;wait until remote RAISES clock line to indicate 
          ;slave is ready but slave likes to keep line low always
      jnb SPICLK,m
      mov C,DIN     ;Get bit
      rrc A ;Shift it in and shift old bit out
      mov DOUT,C    ;Send out old bit
      clr SPICLK    ;lower clock line to tell slave we have data
      nop       ;add small delay. this is executed on slow micro
      setb SPICLK   ;raise clock line
  djnz R7,m ;repeat for remaining bits
  jnb SPICLK,$  ;wait until remote is ready
  mov C,DIN     ;get official last bit
  rrc A     ;and make byte correct
  setb SSEL ;and reset lines
  setb DOUT
  setb DIN
ret

Circuit

\$\endgroup\$
  • \$\begingroup\$ Without looking in detail, I'd say that your clocks aren't exactly in phase and they are drifting relative to each other, as well. \$\endgroup\$ – jonk Mar 4 '18 at 1:30
  • \$\begingroup\$ You need a master clock not skewed by incidental instruction times or ISR's. Nops are only good for atomic read/writes or to wait for a ADC to finish sampling. \$\endgroup\$ – Sparky256 Mar 4 '18 at 2:11
  • \$\begingroup\$ "the slave does not make the SPICLK (aka "SPI clock") line high which is what it should be doing." - why should it be doing this? \$\endgroup\$ – Bruce Abbott Mar 4 '18 at 2:20
  • \$\begingroup\$ because when the master lowers the clock then raises it to indicate data processing, then the slave can make the clock low to tell the master the slave is too busy to handle the next bit of data. \$\endgroup\$ – Mike Mar 4 '18 at 2:31
  • 1
    \$\begingroup\$ I'm not going to check, Are you saying that the SSEL line is being driven as an output by both chips? I'm not sure looking at what little code I can see. If so, you need wire-or and an external pull-up present. Also, a LOT more code. I have an article on stuff like this, written decades ago, somewhere. I suppose I should go find it. Different clocks and sampling rates, execution variability with stuff like JNB are all problems related to timing this to work. The whole idea of "6 times slower so add some NOPs" is just crazy-making to me. It's just wrong. \$\endgroup\$ – jonk Mar 4 '18 at 4:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.