embeddedruststm32spibluenrg

Unknown BlueNRG SPI response


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.


Solution

  • 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.