Unknown BlueNRG SPI response

2.1k views Asked by At

I'm trying to communicate with BlueNRG chip (on X-NUCLEO-IDB04A1 extension board) connected to STM32L1 (on NUCLEO-L152RE board) over SPI protocol.

According to BlueNRG manual, I can send an empty SPI packet of 5 bytes: (0x0B, 0, 0, 0, 0) to get the read/write buffer sizes as well as the device status. The status is supposed to be 0x02 (ready), or 0x00 or 0xFF if the device was sleeping and is waking up.

Here is the communication I'm getting:

Send (0x0B, 0, 0, 0, 0)
Receive (0x00, 0x00)    // why 2 bytes?
Send (0x0B, 0, 0, 0, 0) // assuming device is waking up, re-trying
Receive (0x06, 0x00)    // what is code 6?

My test app is written in Rust using Zinc. The MCU is running the default clock (2048 MHz from MSI). Here is the code responsible for SPI initialization:

// PB.3 = CLCK
let _spi_clock = pin::Pin::new(pin::PortB, 3,
  pin::AltFunction(pin::AfSpi1_Spi2, pin::OutPushPull, pin::Medium),
  pin::PullDown);

// PA.6 = MISO
let _spi_in = pin::Pin::new(pin::PortA, 6,
  pin::AltFunction(pin::AfSpi1_Spi2, pin::OutPushPull, pin::Medium),
  pin::PullNone);

// PA.7 = MOSI
let _spi_out = pin::Pin::new(pin::PortA, 7,
  pin::AltFunction(pin::AfSpi1_Spi2, pin::OutPushPull, pin::Medium),
  pin::PullNone);

// PA.1 = CS
let spi_csn = pin::Pin::new(pin::PortA, 1,
  pin::GpioOut(pin::OutPushPull, pin::Medium),
  pin::PullUp);
spi_csn.set_high();

let spi = spi::Spi::new(spi::Spi1, spi::DirFullDuplex, spi::RoleMaster,
  spi::Data8b, spi::DataMsbFirst, 1); // baud pre-scaler = 2

let bnrg_reset = pin::Pin::new(pin::PortA, 8,
  pin::GpioOut(pin::OutPushPull, pin::VeryLow),
  pin::PullUp);

bnrg_reset.set_low();
// do something
bnrg_reset.set_high();

SPI internals are slightly modified from zinc master. The send/receive code is organized as follows:

loop {
  spi_csn.set_low();
  // send a dummy read request
  spi.write(SpiRead as u8); // = 0x0B
  spi.write(0);
  spi.write(0);
  spi.write(0);
  spi.write(0);

  let status = spi.read();
  // debug print status
  while spi.has_more_data() {
    let data = spi.read();
    // debug print data
  }

  spi_csn.set_high();
}

The question is - how to explain or treat these BlueNRG responses? I wasn't able to find any formal description of the low-level protocol it uses (not talking about HCI/ACI here, but about the status codes other than 0x02 = ready). It is very possible that I'm just initializing the hardware incorrectly, or even missing something obvious here. Would appreciate any guidance.

2

There are 2 answers

0
Lundin On BEST ANSWER

Something seems to be fundamentally wrong with the SPI driver. Does it have a buffer in hardware or software where the incoming bytes are stored, or how does it work? Generally you do not send or receive with SPI, you transceive in full duplex. It will be impossible for the SPI driver itself to tell whether the incoming data is valid or garbage, so there is no such thing as "I only receive 2 bytes when sending 5". You always receive 5 bytes when sending 5 bytes.

The MCU in this application will be the SPI master and the external chip will be the slave. For each byte sent, you will receive one as well. Whether or not that byte makes sense depends on how the external chip works. There is unfortunately no SPI standard, so the external chip can state various delay requirements, such as for example "slave select must be pulled low for x time units before SCK & MOSI go live". You'll have to read the manual of the external chip in detail.

And then when all that confusion is sorted out, there's of course the usual suspects when SPI isn't working properly: clock skew because of incorrect clock polarity or clock phase settings.

0
Winfred On

Please reference ST UM1865, Chapter 5: SPI interface. It is for BlueNRG-MS (not BlueNRG) but the HCI/ACI interface is the same.

As you've noticed, the 1st byte is the SPI READY indication, and 0x02 indicates that the slave SPI interface is ready. If it is any value other than 0x02, the master must ignore the following 4 bytes and abort the SPI transaction.

If the BlueNRG (or -MS) SPI is ready (0x02), the following 4 bytes give 2 buffer sizes for write and read:

  • 2nd byte contains write buffer size (maximum value is 127)
  • 4th byte contains read buffer size

In your example, {0xb, 0, 0, 0, 0} is the SPI header only to check BlueNRG for the size of read buffers. Similarly, {0xa, 0, 0, 0, 0} is to check the size of write buffers.

If the returning size is other than 0, like in your example {0x6, 0}, i supposed it should be {0x6, 0, 0, 0} instead, that means BlueNRG is ready to be read for 6 (0 << 8 | 0x6) bytes of data. Then, you shall read 6 bytes from BlueNRG as soon as you can.

Please reference C:\Program Files (x86)\STMicroelectronics\BlueNRG DK 2.0.2\Projects\Drivers\BSP\STM32L1xx_BlueNRG\SDK_EVAL_Spi_Driver.c for the function BlueNRG_SPI_Read_All().