Trouble using SPI GetBlockSentStatus()

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

Trouble using SPI GetBlockSentStatus()

Jump to solution
1,511 Views
danielnagy
Contributor II

Dear Support and Forum Members,

 

I am working on a project using a Kinetis K64F microcontroller and am having some trouble detecting the end of an SPI transmission. I use Kinetis Design Studio 3.2 and Processor Expert. The microcontroller, an MK64FN1M0VLL12 is on a custom PCB. What I am experiencing is the following:

 

I would like send 3 bytes on SPI, where the master is the microcontroller. I set up an output buffer and call the PEx function SPI_SendBlock(). Then I would like to block the program until the transmission of all 3 bytes is done. For this purpose, I call the PEx function SPI_GetBlockSentStatus() in a loop and exit it when the function returns true. However, the function seems to return true well before the transmission completes. In fact, it returns true right after the first byte has been shifted out.

 

The main() function looks like this:

#define BUFLEN 3

int main(void)
{
// SPI output buffer
uint8 outbuf[BUFLEN];

/*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/
PE_low_level_init();
/*** End of Processor Expert internal initialization.                    ***/

// Fill in SPI output buffer
outbuf[0]=0xAA;
outbuf[1]=0x00;
outbuf[2]=0xFF;

// Initialize TestOut pin to 0
TestOut_PutVal(NULL, 1);
TestOut_PutVal(NULL, 0);

// Start SPI transmission
SPI_SendBlock(SPI_DeviceData, outbuf, BUFLEN);

// Wait until all data is sent
while (!SPI_GetBlockSentStatus(SPI_DeviceData));

// Assert TestOut pin
TestOut_PutVal(NULL, 1);

// Wait forever
for(;;);

/*** Don't write any code pass this line, or it will be deleted during code generation. ***/
  /*** RTOS startup code. Macro PEX_RTOS_START is defined by the RTOS component. DON'T MODIFY THIS CODE!!! ***/
  #ifdef PEX_RTOS_START
    PEX_RTOS_START();                  /* Startup of the selected RTOS. Macro is defined by the RTOS component. */
  #endif
  /*** End of RTOS startup code.  ***/
  /*** Processor Expert end of main routine. DON'T MODIFY THIS CODE!!! ***/
  for(;;){}
  /*** Processor Expert end of main routine. DON'T WRITE CODE BELOW!!! ***/
} /*** End of main routine. DO NOT MODIFY THIS TEXT!!! ***/

 

I am using a BitIO component (named TestOut) to determine when the program passes the line "while (!SPI_GetBlockSentStatus(SPI_DeviceData));". Capturing the signals with an oscilloscope, I see this:

148476_148476.pngSPI.png

It can be seen that data transmission works fine, but the TestOut pin (blue channel on the oscilloscope) is asserted after the first byte has been transmitted on the SPI bus.

 

My question would be: Am I misunderstanding the aim of the function SPI_GetBlockSentStatus()? Shouldn't it indicate the very end of the SPI transmission? Could you help me finding out what I am doing wrong?

 

Some additional information:

  • I have reproduced this phenomenon on an FRDM-K22F board with the very same code. The issue there is similar, although TestOut is asserted a little later (during the transmission of the second SPI byte).
  • I have been using SPI_GetBlockReceivedStatus() successfully. There, logical true is only returned, once all of the bytes have been received.
  • Instead of using SPI_GetBlockSentStatus(), I have tried setting a flag in the event function SPI_OnBlockSent(), but the results were exactly the same as with the original method.
  • Here is a similar thread, but no solution was found there, unfortunately: K70 SPI transmission end detection?
  • I am attaching the project for both the custom board with the K64F and the one for the FRDM-K22F board.

 

I really appreciate your help. Thanks and regards

Daniel

Original Attachment has been moved to: K64F_custom_board_SPIStatusTest.zip

Original Attachment has been moved to: FRDM_K22F_SPIStatusTest.zip

0 Kudos
1 Solution
1,105 Views
jorge_a_vazquez
NXP Employee
NXP Employee

Hi Daniel Nagy

There is a bug in the SPI PE driver. It actually set the SerFlag when you push all the bytes in the TxFIFO and it doesn´t wait for the data to be shifted, hence, the Serflag is up before the last 2 bytes are shifted.

You can fix the problem if you use the EOQ (End Of Queue) flag, it tell you when the end of the transfer is reached. To make this you have to mask your Txcommand with the EOQ bit to indicate that what the last byte is. This has to be done before it push the tx data in the interrupt. So in your SPI interrupt:

if (DeviceDataPrv->OutSentDataNum == DeviceDataPrv->OutDataNumReq){/* check if it’s the last byte */

TxCommand|= SPI_PUSHR_EOQ_MASK;

}

SPI_PDD_WriteMasterPushTxFIFOReg(SPI0_BASE_PTR, TxCommand); /* Put a character with command to the transmit register */

And in your main.c you just have to use the EOQF flag instead of the GetBlockSentStatus.

//while (!SPI_GetBlockSentStatus(SPI_DeviceData));

while (!(SPI_PDD_GetInterruptFlags(SPI0_BASE_PTR) & SPI_SR_EOQF_MASK));

And everything should works fine.

Hope it helps!

Jorge Alcala

View solution in original post

0 Kudos
5 Replies
1,105 Views
danielnagy
Contributor II

Dear Jorge,

It works fine this way, thank you very much!

Kind regards

Daniel

0 Kudos
1,105 Views
danielnagy
Contributor II

Dear Jorge,

Thank you very much for your response. I have carried out the change to the SPI driver you suggested, and it did work, however, I am now facing another issue. As you suggested, I took the SPI driver source (SPI.c) and in the function "PE_ISR(SPI_Interrupt)" replaced this code:

if (DeviceDataPrv->OutSentDataNum == DeviceDataPrv->OutDataNumReq) {

        TxCommand &= 0x7FFFFFFFU;

}

SPI_PDD_WriteMasterPushTxFIFOReg(SPI1_BASE_PTR, TxCommand); /* Put a character with command to the transmit register */

with this:

if (DeviceDataPrv->OutSentDataNum == DeviceDataPrv->OutDataNumReq) {

        TxCommand|= SPI_PUSHR_EOQ_MASK;

}

SPI_PDD_WriteMasterPushTxFIFOReg(SPI1_BASE_PTR, TxCommand); /* Put a character with command to the transmit register */

Then, I initated one 3-byte long SPI transmission and verifed that the expression "SPI_PDD_GetInterruptFlags(SPI1_BASE_PTR) & SPI_SR_EOQF_MASK" really returned logical true if the whole SPI transmission was completed. However, having made the suggested change to the driver, now I seem not to be able to carry out any further SPI transmissions. My main() funcion is as simple as:

int main(void) /*lint -restore Enable MISRA rule (6.3) checking. */ { /* Write your local variable definition here */ // SPI output buffer uint8 outbuf[BUFLEN];

/*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/ PE_low_level_init(); /*** End of Processor Expert internal initialization.                    ***/

/* Write your code here */ /* For example: for(;;) { } */ // Fill in SPI output buffer outbuf[0]=0xAA; outbuf[1]=0x00; outbuf[2]=0xFF; for (uint8 i=0; i<5; i++) {   // Start SPI transmission   SPI_SendBlock(SPI_DeviceData, outbuf, BUFLEN);

  // Wait until all data is sent   while (!(SPI_PDD_GetInterruptFlags(SPI1_BASE_PTR) & SPI_SR_EOQF_MASK)); }

// Wait forever for(;;);

/*** Don't write any code pass this line, or it will be deleted during code generation. ***/   /*** RTOS startup code. Macro PEX_RTOS_START is defined by the RTOS component. DON'T MODIFY THIS CODE!!! ***/   #ifdef PEX_RTOS_START     PEX_RTOS_START();                  /* Startup of the selected RTOS. Macro is defined by the RTOS component. */   #endif   /*** End of RTOS startup code.  ***/   /*** Processor Expert end of main routine. DON'T MODIFY THIS CODE!!! ***/   for(;;){}   /*** Processor Expert end of main routine. DON'T WRITE CODE BELOW!!! ***/ } /*** End of main routine. DO NOT MODIFY THIS TEXT!!! ***/

The first SPI tranfer in the for loop starts and finishes fine. The rest of them just don't happen, however and the for loop finishes without them being executed. If I leave the SPI driver untouched (and include a fixed, manual delay in my for loop instead of "while (!(SPI_PDD_GetInterruptFlags(SPI1_BASE_PTR) & SPI_SR_EOQF_MASK));"), then all 5 SPI transmissions are executed, as expected.

Do you have any suggestions to this phenomenon? I highly appreciate your help.

Kind regards

Daniel

0 Kudos
1,105 Views
jorge_a_vazquez
NXP Employee
NXP Employee

Hi Daniel Nagy

This issue is because you the EOQ flag is not cleared, so, in the second the  SPI transfer you jump the while instruction and a third transfer is executed. so, after you pass you while, you should clear the flag:

     for (uint8 i=0; i<5; i++)  {

    // Start SPI transmission

    SPI_SendBlock(SPI_DeviceData, outbuf, BUFLEN);

    // Wait until all data is sent

     while (!(SPI_PDD_GetInterruptFlags(SPI1_BASE_PTR) & SPI_SR_EOQF_MASK));

     // clear EOQ flag

    SPI_PDD_ClearInterruptFlags(SPI1_BASE_PTR, SPI_SR_EOQF_MASK);

}

Hope it helps!

Jorge Alcala

0 Kudos
1,106 Views
jorge_a_vazquez
NXP Employee
NXP Employee

Hi Daniel Nagy

There is a bug in the SPI PE driver. It actually set the SerFlag when you push all the bytes in the TxFIFO and it doesn´t wait for the data to be shifted, hence, the Serflag is up before the last 2 bytes are shifted.

You can fix the problem if you use the EOQ (End Of Queue) flag, it tell you when the end of the transfer is reached. To make this you have to mask your Txcommand with the EOQ bit to indicate that what the last byte is. This has to be done before it push the tx data in the interrupt. So in your SPI interrupt:

if (DeviceDataPrv->OutSentDataNum == DeviceDataPrv->OutDataNumReq){/* check if it’s the last byte */

TxCommand|= SPI_PUSHR_EOQ_MASK;

}

SPI_PDD_WriteMasterPushTxFIFOReg(SPI0_BASE_PTR, TxCommand); /* Put a character with command to the transmit register */

And in your main.c you just have to use the EOQF flag instead of the GetBlockSentStatus.

//while (!SPI_GetBlockSentStatus(SPI_DeviceData));

while (!(SPI_PDD_GetInterruptFlags(SPI0_BASE_PTR) & SPI_SR_EOQF_MASK));

And everything should works fine.

Hope it helps!

Jorge Alcala

0 Kudos
1,105 Views
sruthyuk
Contributor I

Dear Jorge,

i saw your suggested modifications for SPI transmission. I am trying to interface MK60FX512 master with AD5421 DAC. Can u suggest the code for receiving the transmitted bytes back to master using SPI_ReceiveBlock?

Kind regards

Sruthy UK

0 Kudos