3
\$\begingroup\$

I'm hooking up an STM32F4 (discovery) to an LSM6DS3 IMU (or rather the breakout board from sparkfun) via SPI and I'm not getting a response. I have a logic analyzer hooked up to the IMU and I'm seeing this:

saleae output

The only thing I could see being wrong with this picture is that the clock line begins too late after the start of my data. But I have no idea of how to fix that.

My freq is below the 10MHz limit and my Vddio is also below the max spec. I have two chips with the same set up so the bug is probably my own.

Here is my code. According to the datasheet 0xf is the who_am_I prompt.

#include "imu_control.h"
#include <spi.h>

void init_SPI1();
uint8_t SPI1_send(uint8_t data);

void imu_task(void* pvParameters){
  uint8_t rec = 0;
  init_SPI1();
  while(1){     
    GPIOE->BSRRH |= GPIO_Pin_7; // set PE7 (CS) low
    SPI1_send(0x0f);  // transmit data
    rec = SPI1_send(0x00); // transmit dummy byte and receive data
    GPIOE->BSRRL |= GPIO_Pin_7; // set PE7 (CS) high
    printf("%d\n", rec);
    vTaskDelay(1000);

  }
}

void init_SPI1(){

  GPIO_InitTypeDef GPIO_InitStruct;
  SPI_InitTypeDef SPI_InitStruct;

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOA, &GPIO_InitStruct);

  // connect SPI1 pins to SPI alternate function
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);


  // enable clock for used IO pins
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);

  /* Configure the chip select pin
     in this case we will use PE7 */
  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_Init(GPIOE, &GPIO_InitStruct);

  GPIOE->BSRRL |= GPIO_Pin_7; // set PE7 high

  // enable peripheral clock
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

  /* configure SPI1 in Mode 0 
   * CPOL = 0 --> clock is low when idle
   * CPHA = 0 --> data is sampled at the first edge
   */
  SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // set to full duplex mode, seperate MOSI and MISO lines
  SPI_InitStruct.SPI_Mode = SPI_Mode_Master;     // transmit in master mode, NSS pin has to be always high
  SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // one packet of data is 8 bits wide
  SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;        // clock is high when idle
  SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;      // data sampled at first edge
  SPI_InitStruct.SPI_NSS = SPI_NSS_Soft | SPI_NSSInternalSoft_Set; // set the NSS management to internal and pull internal NSS high
  SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32; // SPI frequency is APB2 frequency / 4
  SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;// data is transmitted MSB first
  SPI_Init(SPI1, &SPI_InitStruct); 

  SPI_Cmd(SPI1, ENABLE); // enable SPI1
}

/* This funtion is used to transmit and receive data 
 * with SPI1
 *          data --> data to be transmitted
 *          returns received value
 */
uint8_t SPI1_send(uint8_t data){

    SPI1->DR = data; // write data to be transmitted to the SPI data register
    while( !(SPI1->SR & SPI_I2S_FLAG_TXE) ); // wait until transmit complete
    while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); // wait until receive complete
    while( SPI1->SR & SPI_I2S_FLAG_BSY ); // wait until SPI is not busy anymore
    return SPI1->DR; // return received data from SPI data register
}
\$\endgroup\$
  • 1
    \$\begingroup\$ If you're still having trouble I suggest you edit your question to add a new 'scope capture showing how you've changed your SPI settings/data in response to the answers. \$\endgroup\$ – brhans Oct 20 '16 at 11:24
  • 1
    \$\begingroup\$ Brandon - Since you are using the SparkFun LSM6DS3 breakout, did you see in their "hookup guide" that you must unsolder the "ADDR" jumper when using SPI? Have you done that? Otherwise the SPI MISO pin is connected directly to Vcc. That won't be good in SPI mode (the pin is used to set I2C address in I2C mode). If that is the fix, I'll convert this comment to an answer :-) However if you have already unsoldered that jumper, please update your question at the bottom, to confirm this has been done \$\endgroup\$ – SamGibson Oct 20 '16 at 13:14
  • \$\begingroup\$ @BrandonSolis - Glad that worked. :-) I've added that fix as an answer. \$\endgroup\$ – SamGibson Oct 20 '16 at 19:11
2
\$\begingroup\$

[Since this was confirmed as the solution from my comment, here it is as an answer:]

The SparkFun LSM6DS3 breakout allows either the device's SPI or I2C interface to be used. However, the breakout PCB is supplied by default with a soldered jumper in-place, labelled "ADDR", which selects the I2C address from a choice of two. However that soldered jumper prevents SPI communication on the MISO connection, and must be removed if the breakout module is used with the SPI interface.

This requirement is briefly mentioned in the SparkFun LSM6DS3 Breakout Hookup Guide:

Excerpt from SparkFun LSM6DS3 Breakout Hookup Guide webpage showing ADDR jumper

\$\endgroup\$
1
\$\begingroup\$

You set bit 0 is low = write operation, during write operation target device is not expected to answer anything. To get data reply, set bit 0 to 1 to start read operation.

\$\endgroup\$
  • \$\begingroup\$ This was a solution I had explored before, but I had discarded because it wasn \$\endgroup\$ – Brandon Solis Oct 20 '16 at 18:43
  • \$\begingroup\$ t working. This plus the jumper solder did the trick! \$\endgroup\$ – Brandon Solis Oct 20 '16 at 18:43
0
\$\begingroup\$

Without having checked your SPI initialization: According to the datasheet, the LSM6DS3 data is sampled at rising edge and changes at falling edge. Both CPOL and CPHA have to be 1, to have Clock High when idle and sample data at rising edge (see here).
So, if Clock is idle High, and data has to be sampled at rising edge, this rising edge of course is the second edge of the clock signal.
You should check your CPOL and CPHA settings.

EDIT:
Another point: To read a register, the first transmitted bit has to be 1 (see chapter 6.2.1 SPI read). It isn't in your screenshot. Please check this also.

\$\endgroup\$
  • \$\begingroup\$ Changing CPHA to second edge didn't get a response either. \$\endgroup\$ – Brandon Solis Oct 20 '16 at 7:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.