Generating MCP2515 SPI ‘READ’ Operation Request Using PSoC 42xx

PSoC 42xx provides SPI component that supports up to 4MHz clock speed. Communicating with MCP2515 via this component, however, is not straightforward when you try the highest clock speed.

The problem is concept of ‘operation’ of MCP2515. An operation consists of multiple SPI bytes bundled by ‘enable’ signal on the CS pin. Lowering the CS pin initiates an operation and it must stay low during the data transmission. See following timing chart quoted from the MCP2515 datasheet.

datasheet_read_instruction

The PSoC SPI component lacks direct control on the CS pin signal. The component automatically lowers the CS level when the write API puts a byte to Tx FIFO and the component’s internal logic raises the CS level when all FIFO values are consumed. But auto-generated API functions are not fast enough due to overhead to make the functions generic. Then, the CS signal may split during an operation when Tx FIFO becomes empty due to the API failing to catch up with the desired speed.

I wrote a function that generates a valid READ operation frame and retrieves returned bytes. The strategy is:

  • Use lower-level component interfaces to avoid overhead.
  • The function pushes sending bytes to Tx FIFO as fast as possible to keep it non-empty during the operation.
  • Concurrently read data from Rx FIFO as fast as possible to avoid FIFO overflow.

There are limitations to use this functions:

  • Both Rx and Tx buffer sizes of the SPI component must be 4. PSoC Creator generates software buffer when the sizes are larger than 4. That makes source code management too complicated.
  • The first two bytes in output array are dummy that do not mean anything. Actual retrieve data starts from the third element.
  • The function may need to disable interrupts during the operation, though it is still missing in the implementation.

Here is the source code. In this example code, the SPI component name is ‘SPIM_CAN’ which is a master SPI component (non SCB).

#define CAN_CTL_READ 0x03

void mcp2515_read(uint8_t address, uint8_t data[], uint8_t length)
{
    /* initialization */
    uint8_t to_write = length;
    length += 2;

    /* flush rx buffer */
    while (SPIM_CAN_GetRxBufferSize())
        SPIM_CAN_ReadRxData();

    /* wait until Tx FIFO becomes empty */    
    while (0u == (SPIM_CAN_TX_STATUS_REG & SPIM_CAN_STS_TX_FIFO_EMPTY)) {}

    CY_SET_REG8(SPIM_CAN_TXDATA_PTR, CAN_CTL_READ); // push instruction
    CY_SET_REG8(SPIM_CAN_TXDATA_PTR, address);      // push address

    // loop until all bytes are retrieved
    while (length > 0) {
        // transmit 0 to receive a byte
        if (to_write > 0 && (SPIM_CAN_TX_STATUS_REG & SPIM_CAN_STS_TX_FIFO_NOT_FULL)) {
            CY_SET_REG8(SPIM_CAN_TXDATA_PTR, 0);
            --to_write;
        }
        // retrieve a byte if there is any in Rx FIFO
        if (SPIM_CAN_RX_STATUS_REG & SPIM_CAN_STS_RX_FIFO_NOT_EMPTY) {
            *data++ = CY_GET_REG8(SPIM_CAN_RXDATA_PTR);
            --length;
        }
    }
}

Tried reading 16 bytes from MCP2515 using this read function from a 4MHz-clock SPI of a PSoC Pionner Kit. The CS (Enable) signal stays low during the operation, as expected.

read_instruction

read_instruction2

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.