SPI Communication Issues with Si4455

I am being blocked from uploading PDF files, so datasheets are attached as links instead of direct attachments. If I've omitted any useful information, let me know and I'll be happy to include it.
I am trying to communicate with an Si4455 RF transceiver over SPI with an Arduino Uno R3. I have a breakout board for the Si4455, not sure exactly which part # it is but it was purchased here. This chip can only take up 3.6V, so the Arduino is connected through a TXS0108 level shifter.
Si4455 Programming Guide
Si4455 Datasheet
TXS0108 Datasheet
Schematic of my setup:


I am starting simple, just trying to send the simple PART_INFO command so I can be sure everything is working. However, the only response I am getting from the chip is 0xFF, no matter what I do or what commands I send.
Here's my sketch:

#include <SPI.h>
#define SDN   8   // Shutdown pin
#define nSEL  10  // Chip Select (SPI CS)
#define nIRQ  9   // Interrupt pin
#define SCK   13  // SPI Clock
#define MISO  12  // SPI MISO
#define MOSI  11  // SPI MOSI

void setup() {
    Serial.begin(115200);
    Serial.println("Starting Si4455 SPI Test...");

    pinMode(SDN, OUTPUT);
    pinMode(nSEL, OUTPUT);
    pinMode(nIRQ, INPUT);
    pinMode(SCK, OUTPUT);
    pinMode(MISO, INPUT);
    pinMode(MOSI, OUTPUT);

    digitalWrite(nSEL, HIGH); // Ensure SPI is inactive before startup
    digitalWrite(SDN, HIGH);  
    delayMicroseconds(15); // 10microsecond minmum hold time in case SDN was previously high


    digitalWrite(SDN, LOW);   // Bring Si4455 out of shutdown
    delay(6);  // Ensure Si4455 fully wakes up (6ms minimum)

    SPI.begin();
    SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0));

    digitalWrite(nSEL, LOW);
    uint8_t powerUp[] = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    for(auto i:powerUp){
      SPI.transfer(i);
    }
    digitalWrite(nSEL, HIGH);
    wait_for_cts();
    // Verify SPI communication with PART_INFO
    get_part_info();
}

void loop() {

}


void wait_for_cts() {
    uint8_t CTS = 0x00;
    unsigned long startTime = millis();
    Serial.println("Waiting for CTS byte to be 0xFF...");

    while (CTS != 0xFF) {
        digitalWrite(nSEL, LOW);
        SPI.transfer(0x44);  // READ_CMD_BUFF
        delayMicroseconds(2);
        CTS = SPI.transfer(0xFF);
        digitalWrite(nSEL, HIGH);
        delay(1);

        // if (millis() - startTime > 100) { // Timeout after 10ms
        //     Serial.println("ERROR: CTS did not become 0xFF!");
        //     return;
        // }
    }
    Serial.println("Clear to send command.");
}


void get_int_status() {
    uint8_t response[8];

    digitalWrite(nSEL, LOW);
    SPI.transfer(0x20);  //  GET_INT_STATUS command
    SPI.transfer(0x00);  // Clear all flags (PH_CLR_PEND, MODEM_CLR_PEND, CHIP_CLR_PEND)
    SPI.transfer(0x00);
    SPI.transfer(0x00);
    digitalWrite(nSEL, HIGH);
    delayMicroseconds(5);  


    wait_for_cts();

    digitalWrite(nSEL, LOW);
    for (int i = 0; i < 8; i++) {
        response[i] = SPI.transfer(0xFF);
        delayMicroseconds(2);
    }
    digitalWrite(nSEL, HIGH);

    Serial.println("INT_STATUS Response:");
    for (int i = 0; i < 8; i++) {
        Serial.print("Byte ");
        Serial.print(i + 1);
        Serial.print(": 0x");
        Serial.println(response[i], HEX);
    }
}

void get_part_info() {
    uint8_t response[8];

    digitalWrite(nSEL, LOW);
    delayMicroseconds(2);
    SPI.transfer(0x01);  // PART_INFO command
    digitalWrite(nSEL, HIGH);
    delayMicroseconds(5);
    wait_for_cts();  // Ensure CTS is high before reading

    digitalWrite(nSEL, LOW);
    for (int i = 0; i < 8; i++) {
        response[i] = SPI.transfer(0x00);
        delayMicroseconds(2);
    }
    digitalWrite(nSEL, HIGH);

    Serial.println("PART_INFO Response:");
    for (int i = 0; i < 8; i++) {
        Serial.print("Byte ");
        Serial.print(i + 1);
        Serial.print(": 0x");
        Serial.println(response[i], HEX);
    }
}

The serial ouput:

09:44:12.342 -> Starting Si4455 SPI Test...
09:44:12.386 -> Waiting for CTS byte to be 0xFF...
09:44:12.386 -> Clear to send command.
09:44:12.386 -> Waiting for CTS byte to be 0xFF...
09:44:12.386 -> Clear to send command.
09:44:12.386 -> PART_INFO Response:
09:44:12.386 -> Byte 1: 0xFF
09:44:12.386 -> Byte 2: 0xFF
09:44:12.386 -> Byte 3: 0xFF
09:44:12.386 -> Byte 4: 0xFF
09:44:12.386 -> Byte 5: 0xFF
09:44:12.386 -> Byte 6: 0xFF
09:44:12.386 -> Byte 7: 0xFF
09:44:12.386 -> Byte 8: 0xFF

I've attached some logic analyzer (attached on the Si4455 side of the level shifter) screenshots in case they're helpful, I'm not sure what to make of them though:


Missing a SPI.endTransaction() ?

    SPI.begin();
    SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0));

    digitalWrite(nSEL, LOW);
    uint8_t powerUp[] = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    for(auto i:powerUp){
      SPI.transfer(i);
    }
    digitalWrite(nSEL, HIGH);
    SPI.endTransaction();  //  <<<<<<<<<<<

possibly in more places.

1 Like

Thanks for your reply. Adding SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0)); and SPI.endTransaction() before and after each command being sent respectively, produced the same output. Is there a particular reason SPI not being disabled in between commands would be causing issues?

SPI uses transactions, it is all explained in the link below.

https://docs.arduino.cc/tutorials/communication/SPITransaction/

I have to read the datasheet to see how to communicate and if the parameters / commands are correct.

@seanlake

rewrote your sketch a bit (I have no hardware to test)
Can you post the output?


#include <SPI.h>

#define SDN   8   // Shutdown pin
#define nSEL  10  // Chip Select (SPI CS)
#define nIRQ  9   // Interrupt pin
#define SCK   13  // SPI Clock
#define MISO  12  // SPI MISO
#define MOSI  11  // SPI MOSI


SPISettings settings(500000, MSBFIRST, SPI_MODE0);


void setup()
{
  Serial.begin(115200);
  Serial.println("Starting Si4455 SPI Test...");

  pinMode(SDN, OUTPUT);
  pinMode(nSEL, OUTPUT);
  pinMode(nIRQ, INPUT);
  //  pinMode(SCK, OUTPUT);  // SPI handles these 3 pins
  //  pinMode(MISO, INPUT);
  //  pinMode(MOSI, OUTPUT);

  digitalWrite(nSEL, HIGH); // Ensure SPI is inactive before startup
  digitalWrite(SDN, HIGH);
  delayMicroseconds(15);    // 10 microsecond minmum hold time in case SDN was previously high
  digitalWrite(SDN, LOW);   // Bring Si4455 out of shutdown
  delay(6);  //  Ensure Si4455 fully wakes up (6 ms minimum)

  SPI.begin();

  power_up();
  wait_for_cts();
  // Verify SPI communication with PART_INFO
  get_part_info();
}

void loop() {

}


void power_up()
{
  uint8_t powerUpCommand[] = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

  SPI.beginTransaction(settings);
  digitalWrite(nSEL, LOW);
  for (auto i : powerUpCommand) SPI.transfer(i);
  digitalWrite(nSEL, HIGH);
  SPI.endTransaction();
}

uint8_t wait_for_cts()
{
  uint8_t CTS = 0x00;
  unsigned long startTime = millis();

  Serial.println("Waiting for CTS byte to be 0xFF...");

  while (CTS != 0xFF)
  {
    SPI.beginTransaction(settings);
    digitalWrite(nSEL, LOW);
    SPI.transfer(0x44);  //  READ_CMD_BUFF
    delayMicroseconds(2);
    CTS = SPI.transfer(0xFF);
    digitalWrite(nSEL, HIGH);
    SPI.endTransaction();
    delay(1);

    if (millis() - startTime > 100) // Timeout after 100 ms
    {
      Serial.println("ERROR: CTS did not become 0xFF!");
      return CTS;
    }
  }
  Serial.println("Clear to send command.");
  return CTS;
}


void get_int_status()
{
  uint8_t response[8];

  SPI.beginTransaction(settings);
  digitalWrite(nSEL, LOW);
  SPI.transfer(0x20);  //  GET_INT_STATUS command
  SPI.transfer(0x00);  // Clear all flags (PH_CLR_PEND, MODEM_CLR_PEND, CHIP_CLR_PEND)
  SPI.transfer(0x00);
  SPI.transfer(0x00);
  digitalWrite(nSEL, HIGH);
  SPI.endTransaction();
  delayMicroseconds(5);

  wait_for_cts();

  SPI.beginTransaction(settings);
  digitalWrite(nSEL, LOW);
  for (int i = 0; i < 8; i++) {
    response[i] = SPI.transfer(0xFF);
    delayMicroseconds(2);
  }
  digitalWrite(nSEL, HIGH);
  SPI.endTransaction();

  Serial.println("INT_STATUS Response:");
  for (int i = 0; i < 8; i++) {
    Serial.print("Byte ");
    Serial.print(i + 1);
    Serial.print(": 0x");
    if (response[i] < 0x10) Serial.print(0);
    Serial.println(response[i], HEX);
  }
}


void get_part_info() {
  uint8_t response[8];

  SPI.beginTransaction(settings);
  digitalWrite(nSEL, LOW);
  delayMicroseconds(2);
  SPI.transfer(0x01);  //  PART_INFO command
  digitalWrite(nSEL, HIGH);
  SPI.endTransaction();

  delayMicroseconds(5);
  wait_for_cts();  // Ensure CTS is high before reading

  SPI.beginTransaction(settings);
  digitalWrite(nSEL, LOW);
  for (int i = 0; i < 8; i++) {
    response[i] = SPI.transfer(0x00);
    delayMicroseconds(2);
  }
  digitalWrite(nSEL, HIGH);
  SPI.endTransaction();

  Serial.println("PART_INFO Response:");
  for (int i = 0; i < 8; i++) {
    Serial.print("Byte ");
    Serial.print(i + 1);
    Serial.print(": 0x");
    Serial.println(response[i], HEX);
  }
}

I don't know if wait_for_cts(); line should be in the get_part_info() function.

TODO: check datasheet

After running your version:

11:38:47.559 -> Starting Si4455 SPI Test...
11:38:47.602 -> Waiting for CTS byte to be 0xFF...
11:38:47.602 -> Clear to send command.
11:38:47.602 -> Waiting for CTS byte to be 0xFF...
11:38:47.602 -> Clear to send command.
11:38:47.602 -> PART_INFO Response:
11:38:47.602 -> Byte 1: 0xFF
11:38:47.602 -> Byte 2: 0xFF
11:38:47.602 -> Byte 3: 0xFF
11:38:47.602 -> Byte 4: 0xFF
11:38:47.602 -> Byte 5: 0xFF
11:38:47.602 -> Byte 6: 0xFF
11:38:47.602 -> Byte 7: 0xFF
11:38:47.602 -> Byte 8: 0xFF

I also tried this version of the sketch with line 133 (the wait_for_cts() call in get_part_info) removed, with the same output

I noted the datasheet states "Not recommended for new designs"

Is it the latest version of the datasheet?

OK, equally bad.

Can you remove the

wait_for_cts(); line from the get_part_info() function? (line 134 approx)

It's the most recent(and only) one I could find- I'll do some more digging and see if there's a newer version

Yes, I forgot to include this originally but removing that wait_for_cts line produced the same output

OK, i'm reading through the datasheet, might take some time...

Thank you for your help!

There is an errata doc...

From the output it looks like the wait_for_cts() works.
Lets verify it is so

If you disconnect the SPI bus from the processor, and you run the sketch, what is the output?
(SPI allows such dry run).

Just to make sure, would that mean removing all the SPI.begin / SPI.beginTransaction call, or do I need to physically disconnect something on the board? Just commenting out all the SPI.begin/beginTransaction/endTransaction lines, the output hangs on
12:04:52.322 -> Starting Si4455 SPI Test...
for at least a few minutes, I can't imagine it will change after this point.

No no no
Just disconnect the wires.

Unfortunately, the same response:

12:40:43.401 -> Starting Si4455 SPI Test...
12:40:43.448 -> Waiting for CTS byte to be 0xFF...
12:40:43.448 -> Clear to send command.
12:40:43.448 -> Waiting for CTS byte to be 0xFF...
12:40:43.448 -> Clear to send command.
12:40:43.448 -> PART_INFO Response:
12:40:43.448 -> Byte 1: 0xFF
12:40:43.448 -> Byte 2: 0xFF
12:40:43.448 -> Byte 3: 0xFF
12:40:43.448 -> Byte 4: 0xFF
12:40:43.448 -> Byte 5: 0xFF
12:40:43.448 -> Byte 6: 0xFF
12:40:43.448 -> Byte 7: 0xFF
12:40:43.448 -> Byte 8: 0xFF

that implies that the wait_for_cts() effectively not get the 0xFF from the device...

(thinking sounds)

can you change this line CTS = SPI.transfer(0xFF);
to CTS = SPI.transfer(0x00);

to see if the 0xFF comes from a sort of "echo of the input"?

datasheet gave no clues, starting with the AN692 doc