SPI

onSelect()

SPI.onSelect, onSelect

Since 0.5.0:

Registers a function to be called when the SPI master selects or deselects this slave device by pulling configured slave-select pin low (selected) or high (deselected). This function is called as an interrupt service routine (ISR) so you must be careful about what calls you make from it.

On Gen 3 devices (Argon, Boron, and Xenon), SPI slave can only be used on SPI1.

// SYNTAX
SPI.onSelect(myFunction);

void myFunction(uint8_t state) {
  // called when selected or deselected
}

Parameters: handler: the function to be called when the slave is selected or deselected; this should take a single uint8_t parameter (the current state: 1 - selected, 0 - deselected) and return nothing, e.g.: void myHandler(uint8_t state)

// SPI1 slave example
static uint8_t rx_buffer[64];
static uint8_t tx_buffer[64];
static uint32_t select_state = 0x00;
static uint32_t transfer_state = 0x00;

SerialLogHandler logHandler(LOG_LEVEL_TRACE);

void onTransferFinished() {
    transfer_state = 1;
}

void onSelect(uint8_t state) {
    if (state)
        select_state = state;
}

/* executes once at startup */
void setup() {
    for (int i = 0; i < sizeof(tx_buffer); i++)
      tx_buffer[i] = (uint8_t)i;
    SPI1.onSelect(onSelect);
    SPI1.begin(SPI_MODE_SLAVE, A5);
}

/* executes continuously after setup() runs */
void loop() {
    while (1) {
        while(select_state == 0);
        select_state = 0;

        transfer_state = 0;
        SPI1.transfer(tx_buffer, rx_buffer, sizeof(rx_buffer), onTransferFinished);
        while(transfer_state == 0);
        if (SPI1.available() > 0) {
            Log.dump(LOG_LEVEL_TRACE, rx_buffer, SPI1.available());
            Log.info("Received %d bytes", SPI1.available());
        }
    }
}

On Gen 2 devices, this example can use SPI instead of SPI1. Makes sure you change all of the calls!


Gen 3 Devices (B-Series SoM, Tracker SoM, Tracker One, Boron, Argon, and E404X):

You can call the DMA-based SPI.transfer from an onSelect handler. You should use a callback, because if you don't, it will busy wait.

You must, however:

  • Always pass real buffers in RAM for txBuf and rxBuf.
  • Never pass NULL for a buffer pointer.
  • Never pass a buffer located in flash memory, such as constant arrays of bytes embedded in code.

The problem is that in the prohibited cases, a temporary buffer is located in RAM, and heap allocation is now allowed from an ISR.