MC9S12C128 SPI Interface 24-bit word transfers

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

MC9S12C128 SPI Interface 24-bit word transfers

Jump to solution
1,860 Views
tnriley
Contributor III

I am having trouble using the SPI interface to communicate with the MAX5481 digital pot. The 5481 requires a 24-bit word to set the pot value. To accomplish using one of the PTAD lines as the SS output and am controlling it manually while 

sending the 24-bit word out in 3 8-bit chunks.

 

I am attempting to do the transfer in the following manner:

1) set PTAD line low

2) do 1st 8-bit SPI transfer (i.e. load SPIDR and wait until SPISR == SPITEF)

3) do 2nd 8-bit SPI transfer (i.e. load SPIDR and wait until SPISR == SPITEF)

4) do 3rd 8-bit SPI transfer (i.e. load SPIDR and wait until SPISR == SPIF)

5) set PTAD line high

 

In theory I thought this would work, but since I am not transfering any data into the MC9s12C device, the code just hangs on step 4.

 

If I replace step 4 above with this line

4) do 2nd 8-bit SPI transfer (i.e. load SPIDR and wait until SPISR == SPITEF)

the SS line gets set high before the transfer is complete because SPITEF does not mean that the data transfer to the device has occured.

 

What is the best mechanism to solve this issue.

 

See example code below

 

void MAX5481(unsigned long mosi){

unsigned int tt;

 

mosi = mosi << 6;

//wiperPos = (unsigned long)wiperPos << 6;

PTAD = PTAD &~SSB; //Pull SSB line low

 

//Data is a 24 bit write

tt = (mosi >> 16) & 0x00FF;

SPIDR = tt; //write upper 8 bits to SPI data register

while((SPISR & SPITEF) == 0); //Wait here until transfer complete

 

tt = (mosi >> 8) & 0x00FF;

SPIDR = tt; //write middle 8 bits to SPI data register

while((SPISR & SPITEF) == 0); //Wait here until transfer complete

 

tt = mosi & 0x00FF;

SPIDR = tt; //write lower 8 bits to SPI data register

while((SPISR & SPIF) == 0); //Wait here until transfer complete

 

for(tt=0; tt<10; tt++){

asm("NOP");

}

PTAD = PTAD | SSB; //Set SSB line back high

}

Labels (1)
0 Kudos
1 Solution
1,022 Views
bigmac
Specialist III

Hello,

 

Your code does seem excessively complex for what you are attempting to achieve.  The following code snippet would be my take on the requirement.  Perhaps you could see whether this code hangs or not, and whether it provides the correct communication with the SPI slave device.  The SPI_trans() function is a general function that is capable of both sending and returning the received byte value.  Do not attempt to single step through this function so as to avoid spurious flag clearing.

 

void MAX5481( unsigned long mosi){   mosi = mosi << 6;   PTAD = PTAD & ~SSB; // Pull SSB line low   // Data is a 24 bit write   (void)SPI_trans( (mosi >> 16) & 0xFF); // transfer MSByte   (void)SPI_trans( (mosi >> 8) & 0xFF);  // Transfer midByte   (void)SPI_trans( mosi & 0xFF);         // transfer LSByte   PTAD = PTAD | SSB; // Set SSB line high}unsigned char SPI_trans( unsigned char val){   while ((SPISR & SPTEF) == 0);  // Wait until buffer empty   SPDR = val;                    // Send byte value   while ((SPISR & SPIF) == 0);   // Wait until transfer complete   return SPIDR;                  // Also clears flag}

Regards,

Mac

 

View solution in original post

0 Kudos
5 Replies
1,022 Views
bigmac
Specialist III

Hello,

 

Since you are not clearing the SPIF after step 2 or step 3, the flag should already be set when step 4 is executed, I might expect that there would be a premature exit from the function prior to the final byte transmission being completed.  This is problematic, but the code should not hang.

 

If the hang occurs during the debug process, it is most likely that the debug process itself is clearing the SPIF flag, prior to being tested within the loop.  This will occur if you are monitoring both SPIDR and SPISR registers, and are attempting to single step the code.

 

During normal operation, to avoid the premature exit problem and eliminate any timing issues, wait for the SPIF flag to become set, and then clear the flag, before sending the next byte of the sequence.  With this approach, SPITEF flag will already be set when each byte is sent, and need not be tested.  Note that the completion of a byte transfer is indicated by SPIFbecoming set, and not by SPTEF.

 

The "double buffering" feature is not particularly useful for SPI master operation due to the potential timing issues.  It is of more value for SPI slave operation, in conjunction with the use of SPI interrupt.

 

Regards,

Mac

 

0 Kudos
1,022 Views
tnriley
Contributor III

Thanks for the ideas.

 

Ok, so I rewrote the code to trigger off the SPIF flag. See code below. But the code hangs at the last flag check statement 

 

while((SPISR & SPIF) == 0); //Wait here until transfer complete

 

when I insert a statement to check the flag state directly above this, the routine works correctly.

 

temp = SPISR & SPIF;

while((SPISR & SPIF) == 0); //Wait here until transfer complete

 

So from this I guess I have a few additional questions.  Why am I able to get past the SPIF flag barrier when the status is read before I reach the barrier? Is this the proper barrier test? Why does it only hang up on the last one?

 

 

 

 ######24-BIT SPI CODE START###########

void MAX5481(unsigned long mosi){

unsigned int tt;

unsigned int temp = 0;

 

mosi = mosi << 6;

 

PTAD = PTAD &~SSB; //Pull SSB line low

 

//#Debug only flag ststus check

temp = SPISR & SPIF;

 

//Check for SPIF flag set and clear if set

if ((SPISR & SPIF) != 0){

temp = SPIDR; //Should Clear SPIF Implied read of SPISR when SPIF read

}

 

//#Debug only flag status check

temp = SPISR & SPIF;

temp = SPISR & SPITEF;

 

//Data is a 24 bit write

//transfer MSByte

tt = (mosi >> 16) & 0x00FF;

SPIDR = tt; //write upper 8 bits to SPI data register

while((SPISR & SPIF) == 0); //Wait here until transfer complete

 

//#Debug only flag status check

temp = SPISR & SPIF;

temp = SPIDR; //Should Clear SPIF Implied read of SPISR when SPIF read

temp = SPISR & SPIF;

 

//Transfer midByte

tt = (mosi >> 8) & 0x00FF;

SPIDR = tt; //write middle 8 bits to SPI data register

while((SPISR & SPIF) == 0); //Wait here until transfer complete

//#Debug only flag ststus check

temp = SPISR & SPIF;

temp = SPIDR; //Should Clear SPIF Implied read of SPISR when SPIF read

temp = SPISR & SPIF;

 

//transfer LSByte

tt = mosi & 0x00FF;

SPIDR = tt; //write lower 8 bits to SPI data register

 

//#Debug only flag ststus check

temp = SPISR & SPIF;

 

//???????Code Hangs here unless previous SPIF check statement is inserted

while((SPISR & SPIF) == 0); //Wait here until transfer complete

 

for(tt=0; tt<10; tt++){

asm("NOP");

}

PTAD = PTAD | SSB; //Set SSB line back high

}

###########CODE END###############

 

 

 

0 Kudos
1,023 Views
bigmac
Specialist III

Hello,

 

Your code does seem excessively complex for what you are attempting to achieve.  The following code snippet would be my take on the requirement.  Perhaps you could see whether this code hangs or not, and whether it provides the correct communication with the SPI slave device.  The SPI_trans() function is a general function that is capable of both sending and returning the received byte value.  Do not attempt to single step through this function so as to avoid spurious flag clearing.

 

void MAX5481( unsigned long mosi){   mosi = mosi << 6;   PTAD = PTAD & ~SSB; // Pull SSB line low   // Data is a 24 bit write   (void)SPI_trans( (mosi >> 16) & 0xFF); // transfer MSByte   (void)SPI_trans( (mosi >> 8) & 0xFF);  // Transfer midByte   (void)SPI_trans( mosi & 0xFF);         // transfer LSByte   PTAD = PTAD | SSB; // Set SSB line high}unsigned char SPI_trans( unsigned char val){   while ((SPISR & SPTEF) == 0);  // Wait until buffer empty   SPDR = val;                    // Send byte value   while ((SPISR & SPIF) == 0);   // Wait until transfer complete   return SPIDR;                  // Also clears flag}

Regards,

Mac

 

0 Kudos
1,022 Views
tnriley
Contributor III

Thanks Big Mac. That seems to work very well.

0 Kudos
1,022 Views
kef
Specialist I

You have to use SPIF flag to track the end of master SPI transfer. The problem is, SPIF is set after first byte is sent and will remain set infinitely. You need to service SPIF flag after each byte transfer is done. The easiest way is to 1) send one byte 2) wait for SPIF, 3) clear SPIF by reading SPI status register and then SPI data register.

 

Your code attempts to use double buffer feature of SPI. Look at picture spidblbufOK.png. Timescale is from left to the right. Rectangles are bytes being sent. What you do

 

  1. wait for SPITEF, then write 1st byte to SPIDR
  2. wait for SPITEF, then write 2nd byte to SPIDR
  3. wait for SPIF, then clear SPIF reading SPISR followed by rea from SPIDR. (waiting for SPIF assumes cyclic reading from SPISR, so when SPIF=1, just read SPIDR).
  4. wait for SPITEF, then write 3rd byte to SPIDR
  5. do again step 3
  6. do again step 3.
  7. Now you can negate chip select pin

 

Unfortunately this sequence is not reliable on many older S12 devices including S12C. What we have in S12C manual is this:

"Reading the data can occur anytime from after the SPIF is set to before the end of the next transfer. If the SPIF is not serviced by the end of the successive transfers, those data bytes are lost and the data within the SPIDR retains the first byte until SPIF is serviced."

In fact S12C SPI is not fully double buffered. In case long interrupt service routine (ISR) fires between steps 2 and 3 or between steps 4 and 5, you will be late servicing one of SPIF=1 events, SPIDR will be lost and you will loose track of bytes completely transferred. See picture spidblbufNOK.png. You can stuck in step 6 forever. This problem seems being fixed on  S12XD/S12XA and I hope on other newer devices to. On fully double buffered SPI devices you can delay so that both bytes are transferred, but you can still service SPIF flag twice, no SPIF events are lost.

 

0 Kudos