Argon's SPI slave mode not working

Hello, can anyone verify that, as is in firmware release 1.1.0, the SPI::onSelect() interrupt callback is being triggered? I have tried the sample SPI slave code, with 2 argon’s. The first was acting as a slave
and another one as the master, but it seems that the SPI::onSelect is never triggered, despite the fact that I “change” the ss pin’s state. Used a photon and a peripheral device (3d accelerometer) for troubleshooting, that both work as expected. I also tried various pins(+ default ss pin).

Yup, I can confirm that.
I’ve been testing with this code between two Photons where it worked and between a Photon master and Argon slave were the onSelect function was never executed

//#define pinMASTER A2   // SS pin on the master
#define pinSLAVE D0    // SS pin on the slave (also tested with A2)

#if defined(pinMASTER)
char tx[64];
char rx[64];

void setup() {
  Serial.begin();
  SPI.begin(pinMASTER);
  digitalWrite(pinMASTER, HIGH);
}

void loop() {
  if (!digitalRead(BTN)) {                          // when SETUP/MODE button is pressed
    delay(50);                                      // debounce
    while(!digitalRead(BTN));                       // wait for button release
    digitalWrite(pinMASTER, LOW);                   // select slave 
    Time.format().toCharArray(tx, sizeof(tx));      // copy current time to transfer buffer
    Serial.printlnf("TX: %s", tx);
    SPI.transfer(tx, rx, sizeof(tx), NULL);         // transfer data to slave
    Serial.printlnf("RX: %s", rx);
    delay(50);                                      // debounce
    digitalWrite(pinMASTER, HIGH);                  // deselect slave
  }
}
#elif defined(pinSLAVE)
SYSTEM_MODE(MANUAL);

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

void onTransferFinished() {
  transfer_state = 1;
}

void onSelect(uint8_t state) {
  if (state) {
    select_state = state;
  }
  digitalWrite(D7, !digitalRead(D7));
}

/* executes once at startup */
void setup() {
  Serial.begin();
  pinMode(D7, OUTPUT);
  for (int i = 0; i < sizeof(tx_buffer); i++)
    tx_buffer[i] = (uint8_t)i;
  SPI.onSelect(onSelect);
  SPI.begin(SPI_MODE_SLAVE, pinSLAVE);
  //pinMode(pinSLAVE, INPUT);   // doesn't help either
}

/* executes continuously after setup() runs */
void loop() {
  while(select_state == 0);
  Serial.println("selected");
        
  select_state = 0;
  transfer_state = 0;
  SPI.transfer(tx_buffer, rx_buffer, sizeof(rx_buffer), NULL); //onTransferFinished);
  //while(transfer_state == 0);
  if (SPI.available() > 0) {
    Serial.printf("Received %d bytes", SPI.available());
    Serial.println();
    Serial.write(rx_buffer, strlen((char*)rx_buffer));
    Serial.println();
  }
}
#else
#error "no role selected"
#endif

Update:
@gerasimos, I have inquired about the matter and got told that on Gen3 devices Slave Mode is only supported on SPI1 (which also maxes out at 8MHz, while SPI can get up to 32MHz but can’t offer Slave Mode).

1 Like

Thanks for that info, are there any future plans to utilize SPI as well?

As it seems that is a hardware limitation of the nRF52840 controller and hence won’t be an option ever.


Update:
This code now works (Master SPI with CS on A2 & Slave SPI1 with CS on D5)

//#define pinMASTER A2   // SS pin on the master
#define pinSLAVE D5    // SS pin on the slave

#if defined(pinMASTER)
SPIClass &spi = SPI;

char tx[64];
char rx[64];

void setup() {
  Serial.begin();
  spi.setClockSpeed(8, MHZ);
  spi.begin(pinMASTER);
  digitalWrite(pinMASTER, HIGH);
}

void loop() {
  if (!digitalRead(BTN)) {                          // when SETUP/MODE button is pressed
    delay(50);                                      // debounce
    while(!digitalRead(BTN));                       // wait for button release
    digitalWrite(pinMASTER, LOW);                   // select slave 
    Time.format().toCharArray(tx, sizeof(tx));      // copy current time to transfer buffer
    Serial.printlnf("TX: %s", tx);
    spi.transfer(tx, rx, sizeof(tx), NULL);         // transfer data to slave
    Serial.printlnf("RX: %s", rx);
    delay(50);                                      // debounce
    digitalWrite(pinMASTER, HIGH);                  // deselect slave
  }
}
#elif defined(pinSLAVE)
SYSTEM_MODE(MANUAL);

// SPI slave example
SPIClass &spi = SPI1;

static   uint8_t  rx_buffer[64];
static   uint8_t  tx_buffer[64];
static   uint32_t transfer_state = 0x00;
volatile uint32_t select_state   = 0x00;

void onTransferFinished() {
  transfer_state = 1;
}

void onSelect(uint8_t state) {
  if (state) {
    select_state = state;
  }
  digitalWrite(D7, !digitalRead(D7));
}

/* executes once at startup */
void setup() {
  Serial.begin();
  pinMode(D7, OUTPUT);
  for (int i = 0; i < sizeof(tx_buffer); i++)
    tx_buffer[i] = (uint8_t)i;
  spi.onSelect(onSelect);
  spi.setClockSpeed(8, MHZ);
  spi.begin(SPI_MODE_SLAVE, pinSLAVE);
  pinMode(pinSLAVE, INPUT);
}

/* executes continuously after setup() runs */
void loop() {
  while(select_state == 0);
  Serial.println("selected");
        
  select_state = 0;
  transfer_state = 0;
  spi.transfer(tx_buffer, rx_buffer, sizeof(rx_buffer), NULL); //onTransferFinished);
  //while(transfer_state == 0);
  if (spi.available() > 0) {
    Serial.printf("Received %d bytes", spi.available());
    Serial.println();
    Serial.write(rx_buffer, strlen((char*)rx_buffer));
    Serial.println();
  }
}
#else
#error "no role selected"
#endif
2 Likes