SPI Slave Simple Echo

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
Anonymous
Not applicable

Hi,

I am trying to build a simple SPI slave communication protocol for an existing master. The master device is an Atmega microcontroller, with an SPI clock approximately 40Kbps.

 

There are two types of messages: first one is master write messages, and second one is master read messages. And I want to implement all protocol logic in interrupt handler...

The problem is; I cant simply use buffered mode, since master write packets should echo the incoming packet (with one or two bytes exception) immediately.

SCB is configured in SPI slave mode, with 0 software buffer size (RX and TX buffer sizes are 8). Interrupt is configured as Internal, and the only interrupt source selected is "RX FIFO Not Empty".

Code is written with following naming notations...

Every thing starting with cy are cypress conponents, and everything starting with comm are related with my communication routines.

the following code is my custom SPI ISR function.

//WRITE MESSAGE FORMAT:

// <WriteSlaveCommand><TotalSize><PayloadSize><...PAYLOAD...><MasterMagic><CRC>

// Response should be an echo (only change MasterMagic with SlaveMagic, and update CRC accordingly)

//READ MESSAGE FORMAT:

// <WriteSlaveCommand><TotalSize><PayloadSize><...0xFF's...><MasterMagic><CRC>

//Response should be populated accordingly

void _commParsePacket(void)

{

    //store received byte

    _commRxByte = cySPI_RX_FIFO_RD_REG;

    while(0u != cySPI_GET_RX_FIFO_ENTRIES);

    cySPI_ClearRxInterruptSource(cySPI_INTR_RX_NOT_EMPTY);

   

    //if software buffer enabled

    //cySPI_rxBufferHead = 0;

    //cySPI_rxBufferTail = 0;   

   

    switch(_parseIndex)

    {

        //wait for Write or Read command

        case 0:

            commRxCrcReset();

            commPacket.command = _commRxByte;

           //Clear/Invalidate TX buffer

            cySPI_TX_FIFO_CTRL_REG |= 0x00010000;

            cySPI_TX_FIFO_CTRL_REG &= 0xFFFEFFFF;

           

            if ((commPacket.command == WriteSlaveCommand) || (commPacket.command == ReadSlaveCommand))

            {

                commStartedCount++;

                commTxCrcReset();

                _commTxByte = commPacket.command;

                if (commPacket.command == WriteSlaveCommand)

                {

                    commPacket.payload = (unsigned char*)&commMosiPacket; //if command is a write command, fill this structure

                }

                else

                {

                    commPacket.payload = (unsigned char*)&commDummyPacket; //if command is a read command, fill a dummy/temporary structure

                                                                                                                           //note: already populates MisoPacket is going to be transferred

                }

               

                _parseIndex++;

            }

            commRxCrcUpdate(_commRxByte);

            break;

        // wait for total size

        case 1:

            commPacket.totalSize = _commRxByte;

            _commTxByte = commPacket.totalSize;

           

            commRxCrcUpdate(commPacket.totalSize);

           

            _parseIndex++;

            break;

        //wait for payload size           

        case 2:

            commPacket.payloadSize = _commRxByte;

            commRxCrcUpdate(commPacket.payloadSize);

            //check for payload sizes... return to parseIndex=0 to avoid a possible data overflow

            if( ((commPacket.command == ReadSlaveCommand) && (commPacket.payloadSize ==  5)) ||

                ((commPacket.command == WriteSlaveCommand) && (commPacket.payloadSize == 17)) )

            {

                _commTxByte = commPacket.payloadSize;

               

                _parsePacketIndex = 0;

                _parseIndex++;

            }

            else

            {

                _parseIndex = 0;

            }

            break;

        //fill payloads

        case 3:

            if (_parsePacketIndex < commPacket.payloadSize)

            {

                commPacket.payload[_parsePacketIndex] = _commRxByte;

                if (commPacket.command == ReadSlaveCommand)

                {

                    _commTxByte = ((unsigned char*)(&commMisoPacket))[_parsePacketIndex];

                }

                else

                {

                    _commTxByte = ((unsigned char*)(&commMosiPacket))[_parsePacketIndex];

                }

                commRxCrcUpdate(_commRxByte);           

                _parsePacketIndex++;

            }

            if (_parsePacketIndex == commPacket.payloadSize)

            {

                _parseIndex++;

            }

            break;

        //replace magic numbers

        case 4:

            commPacket.magicNumber = _commRxByte;

            _commTxByte = SlaveMagic;

           

            commRxCrcUpdate(commPacket.magicNumber);

            _parseIndex++;

            break;

        //wait and check for CRC's

        case 5:

            commPacket.crc = _commRxByte;

            _commTxByte = commTxCrcGet();

            _parseIndex++;

            if (commPacket.crc == commRxCrcGet())

            {

                if (commPacket.command == WriteSlaveCommand)

                    _commSuccess = 0x00;

            }

            break;

        //wait for my CRC to be send...

        default:

            _parseIndex = 0;

            //_commResetTXBuff();

            //return;

            break;

    }  

   

    //transmit

    cySPI_TX_FIFO_WR_REG = _commTxByte;

    commTxCrcUpdate(_commTxByte);

       

    //if software buffer enabled

    //cySPI_txBufferHead = 1;

    //cySPI_txBufferTail = 0;

   

    return;

}

I am using Psoc Creator 4.1, and my device is CY8C4124...

Also note that "Slave Select" is always selected.

Problem is;

1 - It seems nearly 1/2 of the time, they just communicate ok.

2 - If I read failed packets, I see data is late 1 or 2 bytes...

Thanks...

0 Likes
1 Solution
SergiiV_71
Employee
Employee
First like received Welcome!

If the CapSense is interrupt processing is the source of issue,  you can set SPI interrupt priority higher than CapSense, so CapSense would wait until SPI is done.

View solution in original post

3 Replies
EmHo_296241
Level 5
Level 5
10 solutions authored 50 replies posted 25 replies posted

Hey,

Will you be able to share your project so that we can have a look into it?

0 Likes
Anonymous
Not applicable

Actually when I try to strip away the application specific "unnecessary"  sections from the project, the above code started to work correctly.

It seems, the algorithm is correct but Capsense 4.0's ScanAllWidgets() function causes this problem.

Actually the function only initialize scanning process, but the capsense IRQ takes too long to switch from one widget to another.

And of course this causes a delay in SPI interrupt service call.

Thanks

SergiiV_71
Employee
Employee
First like received Welcome!

If the CapSense is interrupt processing is the source of issue,  you can set SPI interrupt priority higher than CapSense, so CapSense would wait until SPI is done.