ADC 18 Bit Probleme...

Hallo zusammen

Ich bin am testen von verschiedenen Pulsar PMOD Modulen (PulSAR ADC PMODs [Analog Devices Wiki]).
Das AD7980 (16 Bit) funktioniert, aber beim AD7982 (18 Bit) komme ich nicht weiter...
Beim 16 Bit ist das ja einfach, ein SPI.transfer16 und es funktioniert.
Aber beim 18 Bit habe ich diesen Code:

#include <SPI.h>
const byte slaveSelectPin = 10; //CS
float time_in;
float time_out;
unsigned long msb;
unsigned long lsb;
unsigned long result;
  
void setup() {
  Serial.begin(115200);
  pinMode(slaveSelectPin,OUTPUT);
  digitalWrite(slaveSelectPin,HIGH); //chip active LOW
  SPI.setSCK(27);
  SPI.begin();
  SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));
}

void loop() {
  time_in = micros();
  digitalWrite(slaveSelectPin,LOW);
  msb = SPI.transfer16(0xFF);
  lsb = SPI.transfer(0xFF);
  result = msb << 8 | lsb >> 6;
  digitalWrite(slaveSelectPin,HIGH);
  time_out = micros();
  delay(20);
  Serial.println(result);
  //Serial.println(time_out-time_in); 
}

Ich sende zuerst einen Transfer 16 Bit MSB, shifte den nach nach links und danach einen Transfer 8 Bit LSB und danach das Ganze nach rechts shiften.
Aber egal was ich shifte, egal welchen Transfer zuerst, da kommt nix gescheites raus...

Frage, bin ich auf dem komplett falschen Weg? Mache ich das Bit shiften falsch?

AD7982 Link:
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7982.pdf

Gruss
Franz

msb = SPI.transfer16(0xFF);
lsb = SPI.transfer(0xFF);
result = msb << 2 | ((lsb >> 6)&3);

würde ich mal so meinen....

Code korrigiert!

&3

Danke!
Mit &3 gehts auch nicht, ohne kommen schon mal einigermassen passende Werte, aber die unteren Bits kommen nicht richtig rein.
Noch eine Idee?

aber die unteren Bits kommen nicht richtig rein.

Was heißt das?

Idee?
Wie immer:
Die Werte von msb und lsb auf die serielle Schnittstelle schreiben und hier bekanntgeben.
Ggfs. etwas seltener als alle 20ms messen

Ggfs. etwas seltener als alle 20ms messen

Hmm...

Das verlinkte Board kann 1 Millon Samples pro Sekunde.
Das SPI Interface kann min. 30MHz

Zudem ist das 18Bit Messen eine Herausforderung, an der man fast nur scheitern kann.
Schau dir das FFT Bildchen an....
Dort ist ein lautes (ca. 20dB) weißes Rauschen zu sehen.
:o Wenn in 2km Entfernung eine Gießkanne umfällt, siehst du das als Peak an der Stelle. :o

Das wird erst ein Spaß, wenn die Tannenbäume alle aus den Fenstern fliegen :slight_smile:

Nee, im Ernst:
Die serielle Schnittstelle wird mit der Ausgabe nicht mehr hinterherkommen, wenn alle 20ms ein Wert ausgegeben werden soll. Deshalb zum Test etwas langsamer - damit man noch was sehen kann.

Hallo,

das &3 benötigt man nicht, wir wollen doch nichts filtern oder maskieren. :wink:

Gib uns mal ein Bsp. eines Datensatz der nicht stimmt. Also, msb, lsb und result.
Die Werte kannste serial binär ausgeben.

Hallo,

habe mich animiert gefühlt eine universell nutzbare Funktion zu bauen zur lesbaren Bitausgabe.

/*
  Doc_Arduino - german Arduino Forum
  IDE 1.8.13
  avr-gcc 10.2.0
  Arduino Mega2560
  02.01.2021
  License: GNU GPLv3
*/  
unsigned long msb {0b1110011100111100};
byte lsb {0b11000000};
unsigned long result;

void setup (void)
{
  Serial.begin(115200);
  Serial.println("\nStart");
  
  formatiereBytes (msb, 2);
  formatiereBytes (lsb, 1);
  result = (msb << 2) | (lsb >> 6);
  formatiereBytes (result, 4);

  Serial.println();
  
  formatiereBytes (msb, 3);
  formatiereBytes (lsb, 3);
  result = (msb << 2) | (lsb >> 6);
  formatiereBytes (result, 3);

}

void loop (void)
{

}


// ****** Funktionen ******
void formatiereBytes (const unsigned long data, const byte anzahlBytes)
{
  unsigned long mask {0};
  byte inkrementLimit {0};
  byte digit {0};
  
  switch (anzahlBytes)
  {
    case 1:  mask = 0x80;       inkrementLimit =  8; break;
    case 2:  mask = 0x8000;     inkrementLimit = 16; break;
    case 3:  mask = 0x800000;   inkrementLimit = 24; break;
    case 4:  mask = 0x80000000; inkrementLimit = 32; break;
    default: mask = 0x80000000; inkrementLimit = 32; break;
  }
  
  Serial.print(F("data: "));
  for (char i = 0; i < inkrementLimit; i++)
  {
    if (data & mask) digit = 1;
    else digit = 0;
    Serial.print(digit);
    // aller 4 Bits ein Hochkomma dazwischen schieben und das Letzte unterdrücken
    if (!((i+1)%4) && (i < inkrementLimit-1)) Serial.print("'");
    mask = mask >> 1;
  }
  Serial.println();
}

Tja...
Vielleicht sind wir es ja jetzt wert, Rohdaten zu sehen zu bekommen....

Hallo,

genau, ohne seine Rohdaten gehts für ihn hier nicht weiter. Habe es sicherlich wie du mehrfach durchgespielt und kann beim zusammensetzen der Bits erstmal keinen Fehler erkennen.

Habe derweile die Formatierungsfunktion nochmal umgekrempelt. Wer noch Optimierungspotential sieht darf sich gern melden. Was mich stutzig macht ist, dass er mit 1 am höchstwertigen Bit und signed Variable mit 'narrowing conversion' warnt. Ich meine ob ich der Variablen einen Wert dezimal oder binär zuweise sollte doch egal sein? Denkfehler? :o

unsigned long a {0b11100111001111001110011100111100};
unsigned int  b {0b1100110011001100};
byte c {0b11000000};

void setup (void)
{
  Serial.begin(115200);
  Serial.println("\nStart");
  
  formatiereBytes (a);
  formatiereBytes (b);
  formatiereBytes (c);
}

void loop (void)
{
}


// ****** Funktionen ******
template <typename T>
void formatiereBytes (const T data)
{
  // Anzahl der Bytes vom Datentyp T ermitteln und eins abziehen, //
  // damit wird die Bit Startposition der Maske erzeugt und       //
  // die for Schleife limitiert                                   //
  constexpr byte limit {(sizeof(T)*8)-1};
  unsigned long mask {1};   // wegen Overflow Compilerwarnung aufgetrennt
  mask = (mask << limit);
  byte digit {0};
  
  Serial.print(F("data: "));
  for (char i = 0; i <= limit; i++)
  {
    if (data & mask) digit = 1;
    else digit = 0;
    Serial.print(digit);
    // aller 4 Bits ein Hochkomma dazwischen schieben und das Letzte unterdrücken
    if (!((i+1)%4) && (i < limit)) Serial.print("'");
    mask = mask >> 1;
  }
  Serial.println();
}

Hallo zusammen

Zuerst danke an alle für den Input :slight_smile:
Ich werde am Sonntag mich noch mal daran machen und die Tipps umsetzen.
Zur Info, da hängt ein ein Teensy 4.1 dran, da geht sogar Realtime ohne Delay über USB...
Den 16 Bit habe ich ja schon soweit getestet, da komme ich auf echte 14 Bit ohne Rauschen. Evtl. geht ja beim 18er noch ein Bit mehr...

ein Teensy

DAS war klar!

SPI.setSCK(27);

Du bist wirklich sparsam mit verwertbaren Informationen.

Doc_Arduino:
Was mich stutzig macht ist, dass er mit 1 am höchstwertigen Bit und signed Variable mit 'narrowing conversion' warnt. Ich meine ob ich der Variablen einen Wert dezimal oder binär zuweise sollte doch egal sein? Denkfehler? :o

Okay okay okay. :slight_smile: Das hängt am Zweierkompliment. Man kann nicht so einfach eine negative binäre Zahl vorgeben. Denn das diese negativ sein soll weiß in dem Moment nur "ich". Der Compiler sagt sich, warum will der Idiot einen unsigned Wert in einen signed quetschen, ich warn ihn lieber einmal. :slight_smile:

Hallo zusammen

Wie gewünscht die Rohdaten. Habe jeweils von 0V bis 5V Eingang ADC in 0.1V Schritten geloggt.

Code:

void loop() {
  time_in = micros();
  digitalWrite(slaveSelectPin,LOW);
  msb = SPI.transfer16(0xFF);
  lsb = SPI.transfer(0xFF);
  result = (msb << 2) | (lsb >> 6);
  digitalWrite(slaveSelectPin,HIGH);
  time_out = micros();
  delay(100);
  Serial.println(result);
  //Serial.println(time_out-time_in); 
}

ADC hat ja differentielle Eingänge, Ref. ist 5V. - Eingang liegt auf GND.
Laut Datenblatt S. 15 sollte dann der Bereich nicht von 0V (0x00000) bis 5V (0x1FFFF) gehen?
Das stimmt aber im result nicht.

lsb.txt (3.87 KB)

msb.txt (4.69 KB)

result.txt (4.21 KB)

Hallo,

Du zeigst Einzelwerte ohne zeitlichen Zusammenhang. Nur ein gültiger Datensatz macht Sinn.
bspw.

void loop()
{
  time_in = micros();
  digitalWrite(slaveSelectPin,LOW);
  msb = SPI.transfer16(0xFF);
  lsb = SPI.transfer(0xFF);
  result = (msb << 2) | (lsb >> 6);
  digitalWrite(slaveSelectPin,HIGH);
  time_out = micros();
  Serial.print(msb, BIN); Serial.print('\t');
  Serial.print(lsb, BIN); Serial.print('\t');
  Serial.println(result, BIN);
  delay(100);
}

Lege einmal 0V und 1V an und gib uns diese Werte.
Was macht übrigens das 0xFF in SPI.transfer(0xFF)?
Ab jetzt wäre auch ein Schaltplan nicht verkehrt, um zu sehen wie du den IC angeklemmt hast.

Ok, anbei das neue Log.
SPI.transfer(0xFF) da kann auch 0 drinstehen, spielt keine Rolle.
ADC ist ein PMOD Modul (AD7982, Link im ersten Beitrag). Ein gleich angeschlossenes 16 Bit Modul (AD7980) funktioniert ohne Probleme.
Werte kommen ja rein, aber auch nicht linear.

capture.txt (12.6 KB)

Hallo,

wir reden etwas aneinander vorbei? Ich/wir können nur das lesen was du schreibst bzw. zeigst. Wir sehen nicht was du wie angeschlossen hast. Gleich angeschlossen wie ... hilft uns hier nicht. Logisch oder?
In deinem capture kann ich beim zusammensetzen erstmal keinen Fehler finden. Erscheint alles logisch richtig zu sein.
Kannst du 2 konkrete Datensätze für 0V (Masse) und 1V zeigen damit ich/wir eine klare Zuordnung haben?
Kannst auch 0V, 1V, 2V, 3V, 4V und 5V zeigen. Mehr aber auch nicht.

Ich habe mal versucht, die Werte auszurichten (anbei).
Was auffällt sind die auftretenden LSB-Werte. Wenn da nur zwei Bit signifikant sind (vermutlich die oberen Bits 6 & 7), sollte doch der Rest immer gleich sein. Ist aber nicht (Beispiele):

2021-01-03 12:54:31	 111111111111100	10000000	 11111111111110010
2021-01-03 12:54:31	 111111111111010	 1111111	 11111111111101001
2021-01-03 12:54:31	 111111111111010	       0	 11111111111101000
2021-01-03 12:54:37	1111101111101101	11111111	111110111110110111

Wenn mindestens eins der beiden High-Bits gesetzt ist, kommen die anderen auch, sonst nicht.

Also vermute ich, dass die 24 Bit Datenaufnahme über SPI so nicht korrekt funktioniert. Ich kann das auch anhand des Datenblatts nicht nachvollziehen: Da werden immer 18 Bit pro Kanal und Clock-Puls rausgetickert. Es wird nicht mit Nullen oder Einsen auf ganze Byte aufgefüllt.

capture-modified.txt (13.6 KB)