Problems with SPI (Nucleo L476RG)

Post here first, or if you can't find a relevant section!
jh2399
Posts: 6
Joined: Wed Aug 03, 2022 8:34 pm

Problems with SPI (Nucleo L476RG)

Post by jh2399 »

Hi,

I'm quite new to STM32 boards and figured I would start with STM32duino and get a good grasp of things before moving on to STM32Cube.

I have been struggling off and on now for the past few days to get SPI to work with my board and a magnetometer sensor (PNI RM3100). I found a sample sketch on GitHub that worked on my Arduino Nano with the following output:

Code: Select all

REVID ID = 0x22
Cycle Counts = 200
Gain = 74.92
Data in counts:   X:1662   Y:750   Z:2520
Data in microTesla(uT):   X:22.18   Y:10.01   Z:33.64
Magnitude(uT):41.52
However, when connecting my Nucleo L476RG and uploading the same code I get this output (it looks like Rev ID and cycle count are reading max values for their respective value types of uint8_t and uint16_t):

Code: Select all

REVID ID = 0xFF
Cycle Counts = 65535
Gain = 24059.40
Data in counts:   X:-1   Y:-1   Z:-1
Data in microTesla(uT):   X:-0.00   Y:-0.00   Z:-0.00
Magnitude(uT):0.00
The pins I am using for my sensor are:
D9 - DRDY (DATA READY)
D10 - CS
D11 - MOSI
D12 - MISO
D13 - SCK

The rest being either GND, supply voltage, or not connected.

This is the sketch I am using:

Code: Select all

#include "SPI.h"
#include "Arduino.h"

//pin definitions
#define PIN_DRDY 9 //Set pin D9 to be the Data Ready Pin
#define PIN_CS 10 //Chip Select (SS) is set to Pin 10

//internal register values without the R/W bit
const int RM3100_REVID_REG = 0x36; // Hexadecimal address for the Revid internal register
const int RM3100_POLL_REG = 0x00; // Hexadecimal address for the Poll internal register
const int RM3100_CMM_REG = 0x01; // Hexadecimal address for the Continuous Measurement Mode internal register
const int RM3100_STATUS_REG = 0x34; // Hexadecimal address for the Status internal register
const int RM3100_CCX1_REG = 0x04; // Hexadecimal address for the Cycle Count X1 internal register
const int RM3100_CCX0_REG = 0x05; // Hexadecimal address for the Cycle Count X0 internal register

//options
#define initialCC 200 // Set the cycle count
#define singleMode 0 //0 = use continuous measurement mode; 1 = use single measurement mode
#define useDRDYPin 1 //0 = not using DRDYPin ; 1 = using DRDYPin to wait for data

uint8_t revid;
uint16_t cycleCount;
float gain;

void setup() {
  pinMode(PIN_DRDY, INPUT);
  pinMode(PIN_CS, OUTPUT);
  digitalWrite(PIN_CS, HIGH);
  SPI.begin(); // Initiate the SPI library
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
  Serial.begin(9600); //set baud rate to 9600
  delay(1000);

  revid = readReg(RM3100_REVID_REG);

  delay(1000);
  Serial.print("REVID ID = 0x"); //REVID ID should be 0x22
  Serial.println(revid, HEX);

  changeCycleCount(initialCC); //change the cycle count; default = 200 (lower cycle count = higher data rates but lower resolution)

  cycleCount = readReg(RM3100_CCX1_REG);
  cycleCount = (cycleCount << 8) | readReg(RM3100_CCX0_REG);

  Serial.print("Cycle Counts = "); //display cycle count
  Serial.println(cycleCount);

  gain = (0.3671 * (float)cycleCount) + 1.5; //linear equation to calculate the gain from cycle count

  Serial.print("Gain = "); //display gain; default gain should be around 75 for the default cycle count of 200
  Serial.println(gain);

  if (singleMode) {
    //set up single measurement mode
    writeReg(RM3100_CMM_REG, 0);
    writeReg(RM3100_POLL_REG, 0x70);
  }
  else {
    // Enable transmission to take continuous measurement with Alarm functions off
    writeReg(RM3100_CMM_REG, 0x79);
  }
}

void loop() {
  long x = 0;
  long y = 0;
  long z = 0;
  uint8_t x2, x1, x0, y2, y1, y0, z2, z1, z0;

  //wait until data is ready using 1 of two methods (chosen in options at top of code)
  if (useDRDYPin) {
    while (digitalRead(PIN_DRDY) == LOW); //check RDRY pin
  }
  else {
    while ((readReg(RM3100_STATUS_REG) & 0x80) != 0x80); //read internal status register
  }

  //read measurements
  digitalWrite(PIN_CS, LOW);
  delay(100);
  SPI.transfer(0xA4);
  x2 = SPI.transfer(0xA5);
  x1 = SPI.transfer(0xA6);
  x0 = SPI.transfer(0xA7);

  y2 = SPI.transfer(0xA8);
  y1 = SPI.transfer(0xA9);
  y0 = SPI.transfer(0xAA);

  z2 = SPI.transfer(0xAB);
  z1 = SPI.transfer(0xAC);
  z0 = SPI.transfer(0);

  digitalWrite(PIN_CS, HIGH);

  //special bit manipulation since there is not a 24 bit signed int data type
  if (x2 & 0x80) {
    x = 0xFF;
  }
  if (y2 & 0x80) {
    y = 0xFF;
  }
  if (z2 & 0x80) {
    z = 0xFF;
  }

  //format results into single 32 bit signed value
  x = (x * 256 * 256 * 256) | (int32_t)(x2) * 256 * 256 | (uint16_t)(x1) * 256 | x0;
  y = (y * 256 * 256 * 256) | (int32_t)(y2) * 256 * 256 | (uint16_t)(y1) * 256 | y0;
  z = (z * 256 * 256 * 256) | (int32_t)(z2) * 256 * 256 | (uint16_t)(z1) * 256 | z0;

  //calculate magnitude of results
  double uT = sqrt(pow(((float)(x) / gain), 2) + pow(((float)(y) / gain), 2) + pow(((float)(z) / gain), 2));

  //display results
  Serial.print("Data in counts:");
  Serial.print("   X:");
  Serial.print(x);
  Serial.print("   Y:");
  Serial.print(y);
  Serial.print("   Z:");
  Serial.println(z);

  Serial.print("Data in microTesla(uT):");
  Serial.print("   X:");
  Serial.print((float)(x) / gain);
  Serial.print("   Y:");
  Serial.print((float)(y) / gain);
  Serial.print("   Z:");
  Serial.println((float)(z) / gain);

  //Magnitude should be around 45 uT (+/- 15 uT)
  Serial.print("Magnitude(uT):");
  Serial.println(uT);
  Serial.println();
  delay(1000);
}

//addr is the 7 bit value of the register's address (without the R/W bit)
uint8_t readReg(uint8_t addr) {
  uint8_t data = 0;
  digitalWrite(PIN_CS, LOW);
  delay(100);
  SPI.transfer(addr | 0x80); //OR with 0x80 to make first bit(read/write bit) high for read
  data = SPI.transfer(0);
  digitalWrite(PIN_CS, HIGH);
  return data;
}

//addr is the 7 bit (No r/w bit) value of the internal register's address, data is 8 bit data being written
void writeReg(uint8_t addr, uint8_t data) {
  digitalWrite(PIN_CS, LOW);
  delay(100);
  SPI.transfer(addr & 0x7F); //AND with 0x7F to make first bit(read/write bit) low for write
  SPI.transfer(data);
  digitalWrite(PIN_CS, HIGH);
}

//newCC is the new cycle count value (16 bits) to change the data acquisition
void changeCycleCount(uint16_t newCC) {
  uint8_t CCMSB = (newCC & 0xFF00) >> 8; //get the most significant byte
  uint8_t CCLSB = newCC & 0xFF; //get the least significant byte

  digitalWrite(PIN_CS, LOW);
  delay(100);
  SPI.transfer(RM3100_CCX1_REG & 0x7F); //AND with 0x7F to make first bit(read/write bit) low for write
  SPI.transfer(CCMSB);  //write new cycle count to ccx1
  SPI.transfer(CCLSB);  //write new cycle count to ccx0
  SPI.transfer(CCMSB);  //write new cycle count to ccy1
  SPI.transfer(CCLSB);  //write new cycle count to ccy0
  SPI.transfer(CCMSB);  //write new cycle count to ccz1
  SPI.transfer(CCLSB);  //write new cycle count to ccz0
  digitalWrite(PIN_CS, HIGH);
}
For what it's worth I also tried using SPI with an ADXL345 (accelerometer). Again with a working sketch for an Arduino but getting no usable results on the STM32 board, so I imagine I'm missing something simple, but I've looked at a lot of different examples, looked over the datasheet for the sensor and I'm just coming up with nothing. I would appreciate any help..
ABOSTM
Posts: 60
Joined: Wed Jan 08, 2020 8:40 am
Answers: 7

Re: Problems with SPI (Nucleo L476RG)

Post by ABOSTM »

Hi @jh2399,
I don't have sensor you mentioned,
nevertheless, I tried your sketch on my nucleo_L476RG, and everything look fine:
I put an logical analyser on the 4 SPI pin and get the expected signal.
Image
On SCLK, I got 8 pulses, sending REVID address to read On MOSI I can decode 0xB6 = 0x80 for read + 0x36 (=RM3100_REVID_REG)
Then 8 new pulses on SCLK for read value but of course nothing on MISO as I have no sensor connected.
CS is driven low during the whole operation.
So everything looks good.
jh2399
Posts: 6
Joined: Wed Aug 03, 2022 8:34 pm

Re: Problems with SPI (Nucleo L476RG)

Post by jh2399 »

I don't have a logic analyser but if I remove the sensor from the equation and probe the outputs from the board I'm getting nothing on SCLK and MISO, MOSI is constantly HIGH, and the CS pin is behaving as expected. Any idea as to why it's behaving in this way?

Edit: By "getting nothing on SCLK and MISO" what I meant was it was constantly LOW.
GonzoG
Posts: 403
Joined: Wed Jan 15, 2020 11:30 am
Answers: 27
Location: Prudnik, Poland

Re: Problems with SPI (Nucleo L476RG)

Post by GonzoG »

@jh2399 , if you're using multimeter, then you won't get any useful data. You need something that can detect high frequency signals.
As you can see on graph from data analyzer, there was only about 25us (microseconds, not milli) burst on SCLK.
hobbya
Posts: 49
Joined: Thu Dec 19, 2019 3:27 pm
Answers: 1

Re: Problems with SPI (Nucleo L476RG)

Post by hobbya »

It looking interesting that the SPI clock is that slow. Usually when I run Arduino code on STM32 I need to review if the device can cope with the SP clock rate as STM32 is faster than Arduino.
ABOSTM
Posts: 60
Joined: Wed Jan 08, 2020 8:40 am
Answers: 7

Re: Problems with SPI (Nucleo L476RG)

Post by ABOSTM »

It looking interesting that the SPI clock is that slow
It is as close as possible to the frequency requested by the sketch:

Code: Select all

  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
1MHz requested, with an input clock of 80MHz, and a prescaler of 128 (power of 2), we achieve 625KHz.
jh2399
Posts: 6
Joined: Wed Aug 03, 2022 8:34 pm

Re: Problems with SPI (Nucleo L476RG)

Post by jh2399 »

GonzoG wrote: Thu Aug 04, 2022 10:05 pm @jh2399 , if you're using multimeter, then you won't get any useful data. You need something that can detect high frequency signals.
As you can see on graph from data analyzer, there was only about 25us (microseconds, not milli) burst on SCLK.
Yeah sorry, that's my mistake, I wasn't thinking clearly!

Hooking it up to a oscilloscope (again without the sensor connected) I see similar results to the picture @ABOSTM posted. I'm getting similar results for the CS, MISO, and SCLK, but the logic looks inverted on the MOSI line? Meaning it's sitting HIGH at idle and pulses LOW when attempting to read from the register, basically mirroring the picture posted above.

Edit: Nevermind... the MOSI line is behaving similar as well. I apologize I'm a software person, not a hardware person :oops: . Posting pictures to show what I'm getting. Any suggestions?

Image
^SCLK

Image
^MOSI
ABOSTM
Posts: 60
Joined: Wed Jan 08, 2020 8:40 am
Answers: 7

Re: Problems with SPI (Nucleo L476RG)

Post by ABOSTM »

Didi you noticed that PNI RM3100 has an I2CEN pin that should be set to GND in order to select SPI communication ?
Also you can try to play with SPI settings in the sketch: try other mode, or slow down the requested frequency.
jh2399
Posts: 6
Joined: Wed Aug 03, 2022 8:34 pm

Re: Problems with SPI (Nucleo L476RG)

Post by jh2399 »

Yes, I have the I2CEN pin grounded.

I have tried changing the speed to 300 KHz as with a prescaler of 256, would result in a speed of 312.5 KHz. I didn't try higher as the datasheet for the sensor says it needs 1MHz or lower on SCLK. I also tried using SPIMODE3 (CPOL=1, CPHA=1), as the sensor supports CPOL=0 CPHA=0 and CPOL=1 CPHA=1.

I have tried the following with no success:
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE3));
SPI.beginTransaction(SPISettings(300000, MSBFIRST, SPI_MODE0));
SPI.beginTransaction(SPISettings(300000, MSBFIRST, SPI_MODE3));

I'd like to thank you for your responses, you could be doing anything with your time and you're taking the time to respond to a stranger on the internet. I appreciate you :) .
GonzoG
Posts: 403
Joined: Wed Jan 15, 2020 11:30 am
Answers: 27
Location: Prudnik, Poland

Re: Problems with SPI (Nucleo L476RG)

Post by GonzoG »

I've looked at RM3100 datasheet and it's a 3.3V chip (3.7V absolute maximum). It's not compatible with 5V, even on data I/O pins.
If your board works with 5V Arduino that means that it has circuitry to make it compatible with 5V. This might be the problem. It will work with 5V MCUs, but not with 3.3V.
Post Reply

Return to “General discussion”