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
}
Solved! Go to Solution.
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
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
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###############
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
Thanks Big Mac. That seems to work very well.
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
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.