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:
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 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
Solved! Go to Solution.
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
Dear Jorge,
It works fine this way, thank you very much!
Kind regards
Daniel
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
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
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
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