LPC5528 SPI FIFOWR controlbit issue

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

LPC5528 SPI FIFOWR controlbit issue

2,651 Views
fruitmans
Contributor II

For the project I am working on we are using multiple SPI peripherals. I am currently working on a driver that meets up to our abstraction layer written in C++ but I encounter an issue when trying to exchange data on the SPI bus. All measurements are done on the LPC5528 processor and I use FlexComm8 for my SPI interface.

For the input of the exchangeBytes function i use the following data:

uint8_t dataTx[10];
 uint8_t dataRx[10];

 for(uint32_t i = 0; i < 10; i++)
 {
 dataTx[i] = i;
 dataRx[i] = 0;
 }

 System::spiFPGA.open();
 System::spiFPGA.exchangeBytes(dataTx, dataRx, 10);
 System::spiFPGA.close();

When I read the user manual for the SPI interfaces the FIFO write data register documentation describes:

Byte, half-word or word writes to FIFOWR will push the data and control bits into the FIFO.
Word writes with the upper half-word of 0, byte writes or half-word writes to FIFOWR will
push the data and the current control bits, into the FIFO. Word writes with a non-zero
upper half-word will modify the control bits before pushing them onto the stack.

The issue I encounter is the unexpected behavior when not always writing the control bits to the FIFO.

The code below shows the blocking exchange function code that is use and which results in a valid SPI transfer:

SpiMasterBase::SpiResult SpiMasterDriver::exchangeBytes(uint8_t *in, uint8_t *out, uint32_t nrOfBytes)
{
 SpiMasterBase::SpiResult result = SpiMasterBase::SPI_SUCCESS;

 uint32_t rxCount = 0;
 uint32_t txCount = 0;

 while((rxCount < nrOfBytes) || (txCount < nrOfBytes))
 {
 if((rxCount < nrOfBytes) && (ports[PORT].peripheral->FIFOSTAT & SPI_FIFOSTAT_RXNOTEMPTY_MASK)) // Check if there is data in the RX FIFO
 {
 // There is data in the rx fifo, read the next byte.
 out[rxCount++] = ports[PORT].peripheral->FIFORD;
 }
 else if((txCount < nrOfBytes) && (ports[PORT].peripheral->FIFOSTAT & SPI_FIFOSTAT_TXNOTFULL_MASK)) // Check if there is space in the TX FIFO
 {
 if(0 < txCount)
 {
 ports[PORT].peripheral->FIFOWR = in[txCount++] + config;
 }
 else
 {
 ports[PORT].peripheral->FIFOWR = in[txCount++] + config;
 }
 }
 else
 {
 // Nothing to do now. Wait for data in the Rx FIFO or an empty space in the tx FIFO
 }
 }

 return result;
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

The code above works like a charm because it always writes the control bits. The result on the SPI output signals is

shown in the image blow.

ValidCommunication.png

When I change the code to:

SpiMasterBase::SpiResult SpiMasterDriver::exchangeBytes(uint8_t *in, uint8_t *out, uint32_t nrOfBytes)
{
 SpiMasterBase::SpiResult result = SpiMasterBase::SPI_SUCCESS;

 uint32_t rxCount = 0;
 uint32_t txCount = 0;

 while((rxCount < nrOfBytes) || (txCount < nrOfBytes))
 {
 if((rxCount < nrOfBytes) && (ports[PORT].peripheral->FIFOSTAT & SPI_FIFOSTAT_RXNOTEMPTY_MASK)) // Check if there is data in the RX FIFO
 {
 // There is data in the rx fifo, read the next byte.
 out[rxCount++] = ports[PORT].peripheral->FIFORD;
 }
 else if((txCount < nrOfBytes) && (ports[PORT].peripheral->FIFOSTAT & SPI_FIFOSTAT_TXNOTFULL_MASK)) // Check if there is space in the TX FIFO
 {
 if(0 < txCount)
 {
 ports[PORT].peripheral->FIFOWR = in[txCount++];// + config;
 }
 else
 {
 ports[PORT].peripheral->FIFOWR = in[txCount++] + config;
 }
 }
 else
 {
 // Nothing to do now. Wait for data in the Rx FIFO or an empty space in the tx FIFO
 }
 }

 return result;
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

The control bits should not be modified if I am not mistaking and thus the result should be the same. Unfortunately I get an unexpected output signal on the SPI output.

InvalidCommunication.png

The first byte is exchanged correctly but from the second byte the data is not correct anymore and even the clock speed seems to be changed which is totally unexpected to me because there is no option for this in the control bits.   

I know I could always add the control bits and forget about the issue but I do not like to keep unexpected behavior in my code and forget about it I would like to know what I am doing wrong.

14 Replies

2,410 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Rik,

I have tested the SPI8 module with SPI master plus DMA with Byte transfer on MY LPC55S69-EVK, it is okay.

i copy the result here.

BR

XiangJun Rong

pastedImage_1.png

0 Kudos

2,410 Views
fruitmans
Contributor II

Thanks for the test result, Can you easilly execute this test on SPI port 8, The high speed peripheral that i use?

In the mean time I will try to check if the normal flexcomm spi (0 to 7) peripherals do work as expected on our hardware board. 

0 Kudos

2,410 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Rik,

I just tested the lpcxpresso55s69_spi_dma_b2b_transfer_master project based on my lpc55s69-EVK board, I close the pin PIO0_19 to PIO0_20 with a resistor on the EVK, it works fine. As the following fig, the SPI transfer length is Byte.

Maybe our initialization code for SPI is incorrect somewhere.

BR

XiangJun Rong

pastedImage_1.png

2,410 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Rik,

sorry for the delay.

I have checked the SDK package, it has the example for both SPI plus DMA, it uses Byte transfer.

You can download SDK package from the link:

MCUXpresso SDK | Software Development for Kinetis, LPC, and i.MX MCUs | NXP 

After you download the SDK package based on the LPC55S28, you can try to test the code.

anyway, pls have a try.

BR

XiangJun Rong

pastedImage_1.png

0 Kudos

2,409 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

I tried to use the line simply, 
(uint16_t)ports[PORT].peripheral->FIFOWR = in[txCount++];// + config;
But above line can not pass the compilation.
So I tried to rewrite the structure, I replace the FIFOWR with union so that you can access both the data and control.
Pls have a try.
/** SPI - Register Layout Typedef */
typedef struct
{
    uint8_t RESERVED_0[1024];
    __IO uint32_t CFG; /**< SPI Configuration register, offset: 0x400 */
    __IO uint32_t DLY; /**< SPI Delay register, offset: 0x404 */
    __IO uint32_t
        STAT; /**< SPI Status. Some status flags can be cleared by writing a 1 to that bit position., offset: 0x408 */
    __IO uint32_t INTENSET; /**< SPI Interrupt Enable read and Set. A complete value may be read from this register.
                               Writing a 1 to any implemented bit position causes that bit to be set., offset: 0x40C */
    __O uint32_t INTENCLR;  /**< SPI Interrupt Enable Clear. Writing a 1 to any implemented bit position causes the
                               corresponding bit in INTENSET to be cleared., offset: 0x410 */
    uint8_t RESERVED_1[16];
    __IO uint32_t DIV;    /**< SPI clock Divider, offset: 0x424 */
    __I uint32_t INTSTAT; /**< SPI Interrupt Status, offset: 0x428 */
    uint8_t RESERVED_2[2516];
    __IO uint32_t FIFOCFG;  /**< FIFO configuration and enable register., offset: 0xE00 */
    __IO uint32_t FIFOSTAT; /**< FIFO status register., offset: 0xE04 */
    __IO uint32_t FIFOTRIG; /**< FIFO trigger settings for interrupt and DMA request., offset: 0xE08 */
    uint8_t RESERVED_3[4];
    __IO uint32_t FIFOINTENSET; /**< FIFO interrupt enable set (enable) and read register., offset: 0xE10 */
    __IO uint32_t FIFOINTENCLR; /**< FIFO interrupt enable clear (disable) and read register., offset: 0xE14 */
    __I uint32_t FIFOINTSTAT;   /**< FIFO interrupt status register., offset: 0xE18 */
    uint8_t RESERVED_4[4];


    union
    {
        struct
        {
            __IO uint16_t FIFOWRData;
            __IO uint16_t FIFOWRcontrol;

          } splitTwo;
        __O uint32_t FIFOWR;
    };

    uint8_t RESERVED_5[12];
    __I uint32_t FIFORD; /**< FIFO read data., offset: 0xE30 */
    uint8_t RESERVED_6[12];
    __I uint32_t FIFORDNOPOP; /**< FIFO data read with no FIFO pop., offset: 0xE40 */
    uint8_t RESERVED_7[440];
    __I uint32_t ID; /**< Peripheral identification register., offset: 0xFFC */
} SPI_Type;
BR
XiangJun Rong
0 Kudos

2,410 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Rik,

If you use the hlaf-word access for the SPI FIFOWR register with the code like , do you have any issue?

  //base->FIFOWR = data | control;

SPI_Type *base;
    base->FIFOWR = data; // access the 32 bits FIFOWR with control and data;
    base->splitTwo.FIFOWRData=0x1234; //access the 16 bits data
    base->splitTwo.FIFOWRcontrol=0xxxxx; //access the 16 bits control

BR

XiangJun Rong

0 Kudos

2,410 Views
fruitmans
Contributor II

Unfortunately is have negative results with the proposal for code change wich is unfortunate. Did you have positive results using the union? 

Personally i did expect this because is also have negative results when using an 8 bit transfer size with DMA. 

How to proceed from this point? Is it possible for you to provide me with a small software project/example that works well?

With kind regards,

Rik

0 Kudos

2,410 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Rik,

I have tested, but I also got negative result. All the SDK driver writes the base->FIFOWR with 32 bits instead of 16 bits or 8 bits. I suppose you have to write the register with 32 bits.

It is frustrating result. I will try to use assembly language to write the register, which can define the width of the register.

BR

XiangJun Rong

0 Kudos

2,410 Views
fruitmans
Contributor II

It is unfortunate to hear that you also encounter the same issue instead of just a simple coding error from my side. I don't think that assembly will give you the desired results. As said i encounter the same issue with the use of the DMA controller. Can you please also chack this behaviour? At the moment i have to copy the 8 bit data array to a 32 bit one so i can add the configuration bits. The performance of the high speed SPI peripherel (50MHz) is overall extremely decreased by this action. 

0 Kudos

2,410 Views
fruitmans
Contributor II

Ok i did some testing on the question "Is the same issue there with the use of a DMA transfer". I expected that the issue is also there when using a DMA transfer because it also uses a memory/register write which should result in the same faulty behaviour. But to be sure is tested it with a dummy data arrey and here are my results:

For the input data i used the same data array as in the first implementation but only now a 50 bytes array instead of the 10 bytes i used previously. The code in my exchangeBytes function is now modified to DMA usage and for the Tx transfer is have implemented 2 versions. The first is an byte oriented write action and the second implementation uses a word oriented data transfer. For the word oriented data transfer i copied the input data(uint8_t) to an internal uint32_t array and added the desired configuration bits to each word. 

SpiMasterBase::SpiResult SpiMasterDriverDMA::exchangeBytes(uint8_t *in, uint8_t *out, uint32_t nrOfBytes)
{
    SpiMasterBase::SpiResult result = SpiMasterBase::SPI_SUCCESS;

    // Enable the SPI DMA triggers.
    SPI_EnableTxDMA(ports[PORT].peripheral, true);
    SPI_EnableRxDMA(ports[PORT].peripheral, true);

    DEBUG_ASSERT(TRANSFER_SIZE >= nrOfBytes);

    // Copy the input data from the byte array to an internal word size tx buffer. This is needed to add the config
    // data to every single byte...
    for(uint32_t i = 0; i < nrOfBytes; i++)
    {
        masterTxData[i] = in[i] + config;
    }

    /******************************************************************************************************************
     * Use the DMA transfer in byte mode. This is totally unsuccessful because not all the control bits are written
     * every time. Probably a bug in the processor peripheral.
     ******************************************************************************************************************/
    // First write the first data byte + config bits.
//    ports[PORT].peripheral->FIFOWR = config + in[0];
//
//    uint32_t xferConfTx = DMA_CHANNEL_XFER(0, 1, 0, 0, kDMA_Transfer8BitWidth, kDMA_AddressInterleave1xWidth, kDMA_AddressInterleave0xWidth, nrOfBytes - 1);
//    DMA_SubmitChannelTransferParameter(&masterTxHandle, xferConfTx, &in[1], (uint32_t*)&ports[PORT].peripheral->FIFOWR, NULL);

    /******************************************************************************************************************
     * Use the DMA transfer in word mode. This results in a successful transfer but unfortunately all the data must
     * be copied from a byte array to a word array with added configuration data to work.
     ******************************************************************************************************************/
    // Functional configuration with 32 bits array (data + config).
    uint32_t xferConfTx = DMA_CHANNEL_XFER(0, 1, 0, 0, kDMA_Transfer32BitWidth, kDMA_AddressInterleave1xWidth, kDMA_AddressInterleave0xWidth, nrOfBytes * 4);
    DMA_SubmitChannelTransferParameter(&masterTxHandle, xferConfTx, &masterTxData[0], (uint32_t*)&ports[PORT].peripheral->FIFOWR, NULL);

    uint32_t xferConfRx = DMA_CHANNEL_XFER(0, 1, 0, 0, kDMA_Transfer32BitWidth, kDMA_AddressInterleave0xWidth, kDMA_AddressInterleave1xWidth, nrOfBytes * 4);
    DMA_SubmitChannelTransferParameter(&masterRxHandle, xferConfRx, (uint32_t*)&ports[PORT].peripheral->FIFORD, &masterRxData[0], NULL);

    // Start the transfer.
    DMA_StartTransfer(&masterTxHandle);
    DMA_StartTransfer(&masterRxHandle);

    // Wait for the transfer to finish.
    while(DMA_ChannelIsActive(masterTxHandle.base, masterTxHandle.channel));
    while(DMA_ChannelIsActive(masterRxHandle.base, masterRxHandle.channel));

    // Disable the SPI DMA triggers.
    SPI_EnableTxDMA(ports[PORT].peripheral, false);
    SPI_EnableRxDMA(ports[PORT].peripheral, false);

    return result;
}

Unfortunately the result with the uint8_t transfer was as expected: 

Failure.png

The word oriented transfer gave me the desired result:

Success.png

While i did manage to create a successful SPI transfer on the high speed SPI port I do have the issue that is only works with the uint32_t/word oriented operations. To do this i have to copy all data from an byte array to a word array and add the configuration data to each word. Although i can communicate on high speeds this way it does impact the performance and memory usage a lot and this is totally undesired...

0 Kudos

2,410 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Rik,

In the second figure, the SPI SCK clock changes it's period  during the transfer, I never see the phenomenon, it is abnormal.

For the writing  FIFOWR register code, I suggest you specify the data width.

For example



 ports[PORT].peripheral->FIFOWR = (uint16_t)in[txCount++];// + config;
Before the code, you have to set up the control halfword of FIFOWR with the data width, and TXSSELx.
Hope it can help you
BR
XiangJun Rong
0 Kudos

2,410 Views
fruitmans
Contributor II

Hello and thanks for the response, unfortunately i tried the casting already and without success. All data width 8, 16, and 32 bit writes give me the same result. I did try the casting to uint8 also but as expected the cast from the input parameter which is of the same type dit not result in a successful session. For the 32 bit word writ i changed the code to 

uint32_t value = in[txCount++];
ports[PORT].peripheral->FIFOWR = value;

I validated with the debugger that the upper half word contains zero's. It seems like the peripheral doesn't take the current configuration bits with the new data. 

0 Kudos

2,410 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Rik,

It appears that you have to write all 32 bits data(control bits and data bits) to the FIFOWR register.

BR

XiangJun Rong

0 Kudos

2,410 Views
fruitmans
Contributor II

Ok so this is an issue inside the MCU? I think it should be mentioned in the errata sheet if this is the case. 

How does this impact the functionality in combination with DMA?

With kind regards,

Rik

0 Kudos