AnsweredAssumed Answered

SPI interrupt missing transmits. What can I do?

Question asked by Nathan M on Aug 13, 2014
Latest reply on Aug 13, 2014 by Edward Karpicz

I am using an IMx6 variant master to collect data from a MC9SC08PA32 slave over SPI. I have noticed many byte packets are being dropped. The symptom is more pronounced when the SPI frequency is increased relative to the slave's external crystal speed. The error only occurs on the the slave's write cycle. It is able to read each and every incoming byte.

 

The error sends the previous byte a second time. No information is lost but there are duplicate bytes. So if the slave was asked to send 10 bytes in increasing order from 1 to 10, an error will appear as 11 bytes with one of the bytes randomly duplicated.

Ex. 1,2,2,3,4,5,6,7,8,9,10.

 

Example:

When the slave is clocked with a 18.1 MHz crystal and the master sets a SPI speed of 60kHz, 84/5000 bytes are errors. At an SPI speed of 100kHz, 5000/5000 fail.

 

When the slave is over clocked with a 24 MHz crystal and the master sets a SPI speed of 60kHz, 0/5000 bytes are errors. At an SPI speed of 100kHz, 47/5000 fail.

 

Similar results were tested for an 11.05 MHz crystal at lower SPI speeds.

 

Data sheets tell me that a an SPI frequency between fBus/2048 and fBus/2 are acceptable. It also tells me that an external crystal must be between 4 and 20 MHz. It's nice to see that over clocking is possible but this isn't an acceptable solution to getting MHz speed SPI.

 

It's fairly clear to me that the slave (in slave mode) is unable to keep up with the master and is missing transmit opportunities. The master is simply reading whatever data already exists in the SPI buffer. I've tried disabling timer and ADC interrupts but that hasn't had any impact. What can I do? Where are my cycles going?

 

Code

volatile byte Flags;   /* Temporary variable for flags */

volatile byte Data;

ISR(SS1_Interrupt)

{

  SPITimeout = mScount + 2;

  if (SPITimeout > ROLLTIME) SPITimeout = -2;

  Flags = SPI0_S;

  Data = SPI0_D;

 

 

  switch (IOState) {

  case IOSTATE_RECV:

  if ((SPI0_S_SPRF_MASK & Flags) == SPI0_S_SPRF_MASK) {

  InpTransitBuf[receivepos] = Data;

  receivepos++;

  if (receivepos == (SPI_INP_BUF_SIZE - 1)) {

  IOState = IOSTATE_XMIT;

  transitpos = 0;

  }

  }

  break;

  case IOSTATE_XMIT:

  SPI0_C1_SPTIE = 0U;                  // Disable transmit interrupt

  if ((SPI0_S_SPTEF_MASK & Flags) == SPI0_S_SPTEF_MASK) { //is buffer empty? It better be!

  SPI0_D = OutTransitBuf[transitpos];

  transitpos++;

  if (transitpos == (SPI_OUT_BUF_SIZE - 1)) {

  IOState = IOSTATE_COMPLETE;

  }

  }

  IPC_SC_PULIPM = 1U;                  // Restore Interrupt Priority Mask

  break;

  case IOSTATE_COMPLETE:

  SS1_TransferComplete();

  Flags = SPI0_D;

  SPI0_D = 0xdd;

  IOState = IOSTATE_WAIT;

  break;

  case IOSTATE_WAIT:

  Flags = SPI0_D;

  SPI0_D = 0xcc;

  break;

  }

  //IPC_SC_PULIPM = 1U;                  // Restore Interrupt Priority Mask

}

 

Thanks for taking the time to read this.

Outcomes