SPI-Bus Kommunikation mit Sensor

Hallo Community,

ich versuche momentan einen 3D-Hall Sensor AS5410 an einem Arduino Uno Board in Betrieb zu nehmen.
Die Kommunikation zwischen Sensor und qC soll dabei über den SPI-Bus erfolgen.

Laut Sensor Datenblatt erwartet der Sensor zum Register lesen über die MOSI Leitung eine Bitfolge von 0 + 15 bits für die Adresse. Darauf folgen 16 bits mit den Daten aus dem Register.

Ich habe nun zunächst versucht, das Register 0110h mit der Sensortemperatur auszulesen.
Leider bekomme ich hier nur nullen über die MISO Leitung zurück.

Hier der von mir verwendete Code

#include <SPI.h>

#define DATAOUT 11           //MOSI
#define DATAIN  12           //MISO 
#define SPICLOCK  13         //sck
#define SLAVESELECT 10       //ss

void setup() {
  Serial.begin(9600);
  pinMode(DATAOUT, OUTPUT);                  // SPI Out/Inputs
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK, OUTPUT);
  pinMode(SLAVESELECT, OUTPUT);

  SPI.begin();               // SPI Bus aktivieren

  SPI.setClockDivider(SPI_CLOCK_DIV32);      // 16MHz/32=500kHz
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  digitalWrite(SLAVESELECT, LOW);
  delay(50);
}



void loop () {

  byte val[2];

  SPI.transfer(0x01);  // 16-9 Bit
  Serial.println ("...");
  SPI.transfer(0x10);  //  8-0 Bit
  Serial.println ("...");

  val[1] = SPI.transfer(0x00);
  val[2] = SPI.transfer(0x00);


  Serial.println (val[1], BIN);
  Serial.println (val[2], BIN);
  
}

Was ich sehr eigenartig finde, dass wenn ich statt der hier verwendeten SPI Initialisierung die neuere und empfohlene Initialisierung per SPI.beginTransaction() verwende

void setup() {
  Serial.begin(9600);
  pinMode(DATAOUT, OUTPUT);                  // SPI Out/Inputs
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK, OUTPUT);
  pinMode(SLAVESELECT, OUTPUT);

  digitalWrite(SLAVESELECT, LOW);
  SPI.beginTransaction (SPISettings (1000000, MSBFIRST, SPI_MODE0)); // 1 MHz clock, MSB first, mode 0
  delay(50);
}

das Programm im zweiten Aufruf von SPI transfer hängt und nicht mehr weiterläuft.
(Ausgabe über den Seriellen Monitor nur “…” und dann nichts mehr)

Hallo,

vielleicht hilft das schon.

Bei SPI üblich muß der Chip-Select Pin für den Sensor in Ruhe immer auf HIGH.
Wenn man einen IC ansprechen möchte, legt man diesen Pin auf LOW.
Wenn die Kommunikation beendet ist, legt man diesen wieder auf HIGH.

Im setup den Pin erst auf HIGH setzen und dann auf Ausgang.
Damit umgeht man ein kurzes Low Signal.

Doc_Arduino:
Bei SPI üblich muß der Chip-Select Pin für den Sensor in Ruhe immer auf HIGH.

Da ich nur den einen Sensor verwende ist laut Sensordatenblatt eigentlich gar kein Chip-Select notwendig.
"For a single unit, this connection is optional as the data transmission is synchronized automatically by the number of SCK cycles"
Ich habe auch schon probiert den CS nur vor SPI.transfer auf LOW zu setzen - ändert allerdings leider auch nichts.

Stimmt der eingestellte SPI Modus? Wenn nur Nullen ankommen, hat der Chip möglicherweise die Adresse nicht richtig erkannt.

Es schadet nicht, wenn man den CS zwischen den Übertragungen abschaltet, damit der Chip nicht mit den Bits aus dem Tritt kommt.

Bei Verwendung von beginTransaction könnte es sogar notwendig sein, den CS vor beginTransaction und vor endTransaction wegzunehmen. Sonst könnte die Bibliothek nachher vergeblich auf die Freigabe des SPI Bus durch andere Benutzer warten. Das erscheint mir als wahrscheinlichste Ursache für das Aufhängen des Programms.

Hallo,

gut möglich. Mit anderen Worten. Das CS Signal ist nicht nur allgemein dafür da, "he ich will mit dir reden", sondern für den IC selbst auch intern eine Art Startsignal das es von vorn los geht. Bleibt er dauerhaft auf LOW, weis er spätestens mit dem 2. Datensatz nichts mehr anzufangen.

Danke für eure Hilfe :slight_smile:
Die neuere Methode für die SPI initialisierung funktioniert jetzt durch das gezielte HIGH bzw LOW setzen des CS Signal und das Programm bleibt nicht mehr hängen.
Ich habe festgestellt, dass nachdem ich den Sequencer in Register 000Eh auf 1 Setze auch Werte aus dem Temperaturregister ausgelesen werden können.
Im Datenblatt steht beim Sequencer in der Default Spalte 1, was ich leicht verwirrend finde, da ich zunächst immer davon ausgegangen bin, dass dieser dann schon standardmäßig an ist…

Jetzt muss ich nur noch hinbekommen, die Positionsdaten auszulesen, will den Sensor ja schließlich nicht als Thermometer betreiben.
Im Statusregister ist aber immer das MagLost bit (Magnetic field values are too low for position measurement) gesetzt. Vielleicht muss ich mir hier einfach noch einen stärkeren Magneten besorgen.

So sieht mein Code jetzt aktuell aus

#include <SPI.h>

#define DATAOUT 11           //MOSI
#define DATAIN  12           //MISO 
#define SPICLOCK  13         //sck
#define SLAVESELECT 10       //ss


void setup() {
  Serial.begin(9600);

  pinMode(DATAOUT, OUTPUT);                  // SPI Out/Inputs
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK, OUTPUT);
  pinMode(SLAVESELECT, OUTPUT);

  digitalWrite(SLAVESELECT, HIGH);
  delay(500);
  SPI.beginTransaction (SPISettings (1000000, MSBFIRST, SPI_MODE0)); // 1 MHz clock, MSB first, mode 0
  delay(500);
  
  writeRegister(0x000E, 0x0002); //Sequencer enable
  writeRegister(0x000F, 0x0103); //set threshold magnitude value
}



void loop () {

  delay(1000);

  double temp = ((double)readRegister(0x0110) / 200.0) + 25.0;
  Serial.print("Temperatur: ");
  Serial.println(temp);

  uint16_t status = readRegister(0x0107);
  Serial.print("Status: ");
  Serial.println(status, BIN);

  uint16_t position = readRegister (0x0122);
  Serial.print("Position: ");
  Serial.println(position, BIN);
}

uint16_t readRegister(uint16_t registertoread) {
  digitalWrite(SLAVESELECT, LOW);
  SPI.transfer16(registertoread); // register
  uint16_t val = SPI.transfer16(0x0000); // read register
  digitalWrite(SLAVESELECT, HIGH);
  return val;
}

void writeRegister(uint16_t registertowrite, uint16_t writedata) {
  digitalWrite(SLAVESELECT, LOW);
  SPI.transfer16(registertowrite | 0x8000); // write command + register
  SPI.transfer16(writedata); // write data
  digitalWrite(SLAVESELECT, HIGH);
  return;
}

Wofür soll der Sensor überhaupt verwendet werden? Es gibt einfache dreibeinige IC mit analogem bzw. digitalem Ausgang, die funktioniren auch ohne komplizierte Programmierung.

DrDiettrich:
Wofür soll der Sensor überhaupt verwendet werden?

Es soll damit möglichst schnell und genau die Durchbiegung eines Biegebalkens gemessen werden.

Hallo,
Positionsdaten auslesen klappt nun auch, zumindest teilweise.
Ich habe allerdings noch ein recht großes Problem mit dem SPI timing.
Der aktuell verwendete Code

#include <SPI.h>

#define DATAOUT 11           //MOSI
#define DATAIN  12           //MISO 
#define SPICLOCK  13         //sck
#define SLAVESELECT 10       //ss


void setup() {
  Serial.begin(9600);

  pinMode(DATAOUT, OUTPUT);                  // SPI Out/Inputs
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK, OUTPUT);
  pinMode(SLAVESELECT, OUTPUT);

  digitalWrite(SLAVESELECT, HIGH);
  delay(500);
  SPI.beginTransaction (SPISettings (500000, MSBFIRST, SPI_MODE0)); // 1/2 MHz clock, MSB first, mode 0
  delay(500);

  writeRegister(0x000E, 0x0002); //Sequencer enable
  writeRegister(0x000F, 0x0103); //set threshold magnitude value
  writeRegister(0x000B, 0x0010); //differential measurement of hall cells
  uint16_t seq = readRegister (0x000E);
  Serial.print("Sequencer: ");
  Serial.println(seq, BIN);
}



void loop () {

  delay(10);


  uint16_t status = readRegister(0x0107);


  if(~status & 0x4000) { //print position if magnet is in range
  uint16_t position = readRegister (0x0122);
  Serial.print("Position: ");
  Serial.println(position);
  }

}

uint16_t readRegister(uint16_t registertoread) {
  digitalWrite(SLAVESELECT, LOW);
  delay(10);
  SPI.transfer16(registertoread); // register
  delay(10);
  uint16_t val = SPI.transfer16(0x0000); // read register
  delay(10);
  digitalWrite(SLAVESELECT, HIGH);
  return val;
}

void writeRegister(uint16_t registertowrite, uint16_t writedata) {
  digitalWrite(SLAVESELECT, LOW);
  delay(10);
  SPI.transfer16(registertowrite | 0x8000); // write command + register
  delay(10);
  SPI.transfer16(writedata); // write data
  delay(10);
  digitalWrite(SLAVESELECT, HIGH);
  return;
}

Zu Beginn wird im setup der Sequencer gestartet. Danach frage ich das Register ab, ob der Sequencer gestartet ist.
Und hier ist das Problem.
Das Register wird sehr oft falsch initialisiert und der Sensor macht dann natürlich nur noch Blödsinn. Nach mehrmaligen Reset des Arduino (meist über 10x) wird das Register richtig initialisiert und dann läuft das Programm auch richtig weiter, ohne wieder aus dem Tritt zu kommen.
Ich habe schon versucht den Clock auf die Hälfte der maximalen Sensorfrequenz zu halbieren und jeweils in der read bzw writeRegister Funktion nach jedem Schritt delays einzufügen.
Ändert allerdings nichts an dem Verhalten.
Im Anhang befindet sich die SPI Timing Tabelle aus dem Datenblatt.
Da werden doch eigentlich alle Zeiten eingehalten?

Könnte es vielleicht etwas mit den Kabellängen auf sich haben?
Zum Sensor verwende ich 25cm Jumper Kabel. Das USB Kabel vom PC zum Arduino ist mit 180cm recht lang.