Forum: Mikrocontroller und Digitale Elektronik Arduino und externer ADC über SPI


von name123 (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich hab einen Arduino Due und möchte damit einen AD7356 auslesen. Hält 
man den CS Pin länger auf low werden die Daten von beiden Kanälen des 
ADC auf einem SPI MISO ausgegeben, mit meinem Code versuche ich das so 
hinzubekommen aber es werden aber nur nullen zurückgegeben (oder gar 
nichts?). Liegt das Problem am Code oder ist da evtl. was an meiner 
Hardware falsch aufgebaut?

1
#include <SPI.h>
2
3
#define CS_ADC 4
4
5
void setup() {
6
  // put your setup code here, to run once:
7
  pinMode(CS_ADC, OUTPUT);
8
  
9
  Serial.begin(9600);
10
  
11
  SPI.begin(4);
12
  SPI.setClockDivider(4,1);
13
  SPI.setDataMode(4,SPI_MODE2);
14
  SPI.setBitOrder(MSBFIRST);
15
16
}
17
18
void loop() {
19
  // put your main code here, to run repeatedly:
20
  unsigned int val1 = 0;
21
  unsigned int val2 = 0;
22
  byte MSB1;
23
  byte LSB1;
24
  byte MSB2;
25
  byte LSB2;
26
  
27
  MSB1 = SPI.transfer(4, 0x00, SPI_CONTINUE);
28
  LSB1 = SPI.transfer(4, 0x00, SPI_CONTINUE);
29
  MSB2 = SPI.transfer(4, 0x00, SPI_CONTINUE);
30
  LSB2 = SPI.transfer(4, 0x00);
31
  
32
  val1 = MSB1<<8;
33
  val1 = val1 | LSB1;
34
  
35
  val2 = MSB2<<8;
36
  val2 = val2 | LSB2;
37
  
38
//  val1 = SPI.transfer16(4,0x00,SPI_CONTINUE);  //transfer16 nicht deklaiert???
39
//  val2 = SPI.transfer16(4,0x00);
40
  
41
  Serial.print("MSB1: ");
42
  Serial.println(MSB1);
43
  Serial.print("LSB1: ");
44
  Serial.println(LSB1);
45
  Serial.print("MSB2: ");
46
  Serial.println(MSB2);
47
  Serial.print("LSB2: ");
48
  Serial.println(LSB2);
49
  Serial.print("val1 original: ");
50
  Serial.println(val1);
51
  Serial.print("val1 in V : ");
52
  Serial.println((val1*2.048)/4096);
53
  Serial.print("val2 orignial: ");
54
  Serial.println(val2);
55
  Serial.print("val2 shifed: ");
56
  Serial.println((val2>>2));
57
  Serial.print(" \n \n \n");
58
  delay(10000);
59
  
60
}

von Werner (Gast)


Lesenswert?

So wie ich das sehe, setzt Du den CS Pin nur einmalig LOW.

Ich denke, da passiert Gar nichts, weil:

The CS signal initiates the data transfer
        !!!  and  !!!!
conversion process.
The falling edge of CS puts the track and hold into hold mode,
at which point the analog input is sampled and the bus is taken
out of three-state. The conversion is also initiated at this point
and requires a minimum of 14 SCLKs to complete.

Nach dem Powerup kommen also einmalig Müll-Daten. Theroetisch kommen 
erst beim 2. CS-Low die Daten der vorherigen Conversion.

Also: Mach das Auslesen mal zyklisch (Alle 1 Sek oder so) und lass dir 
die Werte ausgeben.

Dann wirst Du immernoch was an deinem Bit-geschiebe ändern müssen, denn:

The 12-bit result then follows with the final bit in the data transfer 
and is valid on the 14th falling edge (having been clocked out on the 
previous (13th) falling edge). In applications with a slower SCLK, it 
may be
possible to read in data on each SCLK rising edge depending on
the SCLK frequency. With a slower SCLK, the first rising edge,
of SCLK after the CS falling edge has the second leading zero
provided, and the 13th rising SCLK edge has DB0 provided.

Datenblatt lesen!

Werner

von name123 (Gast)


Lesenswert?

Werner schrieb:
> So wie ich das sehe, setzt Du den CS Pin nur einmalig LOW.

Durch das SPI_CONTINUE in den SPI.transfer() Funktionen wird der CS PIN 
weiterhin low gehalten, beim letzen .transfer sollte der Pin dann wieder 
auf high gehen. Oder muss ich den Pin auf low setzen, 14CLKs warten und 
dann anfagen auszulesen? Bei einem SAR-ADC sollten er doch dirket 
anfangen können Bits zu liefern oder?

Werner schrieb:
> Also: Mach das Auslesen mal zyklisch (Alle 1 Sek oder so) und lass dir
> die Werte ausgeben.

Das Problem besteht danach immer noch...nur nullen.
Kann es sein das ich den falschen SPI Mode benutze?

Werner schrieb:
> Dann wirst Du immernoch was an deinem Bit-geschiebe ändern müssen, denn:

stimmt, da schaue ich später nochmal nach...

von Nico W. (nico_w)


Lesenswert?

name123 schrieb:
> Das Problem besteht danach immer noch...nur nullen.
> Kann es sein das ich den falschen SPI Mode benutze?

Warum denkst du denn das Mode 2 richtig ist?

von name123 (Gast)


Lesenswert?

Nico W. schrieb:
> Warum denkst du denn das Mode 2 richtig ist?

Ich hab Fig. 31 im Datenblatt des ADC mit der Fig.32-4 auf S. 681 im 
Datenblatt des uC auf dem Due verglichen und habe daraus abgelesen, dass 
NCPHA = 0 und CPOL = 1 sein sollte.
In der Tabelle https://www.arduino.cc/en/Reference/SPI zu den SPI Modes 
komme ich dann auf Mode 2, ist das korrekt?

von Werner (Gast)


Lesenswert?

Ok, noch eine Idee
(ich mache nix mit den Arduio Libs, deswegen kenn ich mich da nicht aus. 
Ich bleibe lieber nahe am Datenblatt und schreib mir meine eigenen Libs. 
Deswegen weiß ich wenig über die Funktionen, und dwas die so tun).

Aber das hier scheint auch noch Wert zu sein drüber nachzudenken:

>Kann es sein das ich den falschen SPI Mode benutze?
Wenn ich das Datenblatt richtig interpretiere, dann brauchst du wohl

CPOL = 1
und
CPHA = 1

das entspricht aber nicht MODE2, sondern 3.
https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus


>Oder muss ich den Pin auf low setzen, 14CLKs warten und
>dann anfagen auszulesen?

Nein, das hab ich so nicht aus dem Datenblatt verstanden.

Werner

von Nico W. (nico_w)


Lesenswert?

Sieht für mich eher wie Mode 3 aus. Würde das eher mal testen. Afaik ist 
Mode 1 und 2 eher unüblich. Mag mich aber auch irren.

von name123 (Gast)


Lesenswert?

Werner schrieb:
> das entspricht aber nicht MODE2, sondern 3.

Mode3 nach Datenblatt oder nach der Arduino Doku, die sind 
komischerweise nicht gleich?


Ich konnte mir ein Oszi organisieren und das Problem ist, das der CS Pin 
konstant auf 0 liegt, kennt sich hier jemand mit der Extended SPI 
Library vom Due aus und weiß woran es liegt?
Der ADC kann theroetisch 5MS/s, ich würde das gerne so gut es geht 
ausnutzen und ich vermute das das über die .transfer Funktion etwas 
effektiver ist, als wenn ich den CS Pin vor einem lesevorgang auf low 
setze und danach wieder auf high, vorallem, da ich nicht weiß wann 14 
SCLK rum sind?!

von Werner (Gast)


Lesenswert?

Hi.
Ich kenne mich -wie gesagt- mit dem Arduino Zeug nicht aus.
Vielleicht solltest Du mal:

Die Zeile "pinMode(CS_ADC, OUTPUT);" wegnehmen. Kann ja sein, dass 
dieses ganze SPI.machirgendwas() den Pin und dessen Funktion 
selbstständig organisiert.

Wenn das nicht hilft, dann -wie Du selbst schon sagtest- ein "PinLow" 
vor dem ganzen SPI.transfer und ein "PinHigh" dahinter, wenn alles 
fertig ist.

Wahrscheinlich musst Du dann aber der SPI.transfer() Funktion einen 
anderen (unbenutzten) CS Pin mitteilen, damit die Funktion damit machen 
kann, was sie möchte und Du selbst dien CS Pin in der Hand hast.

Das ist dann Geschwindigkeitstechnisch vielleicht nicht der Hit, aber 
immerhin hast Du vielleicht ein Ergebnis.

>Mode3 nach Datenblatt oder nach der Arduino Doku, die sind
>komischerweise nicht gleich?

Ich hatte nur überflogen, aber sind die Modes in der Arduino Doku nicht 
an den Wiki-Artikel angelehnt? Für mich sah die Belegung gleich aus.

Werner

von name123 (Gast)


Lesenswert?

Werner schrieb:
> Die Zeile "pinMode(CS_ADC, OUTPUT);" wegnehmen

DANKE! ohne die Zeile Funktioniert es so wie es soll!

Werner schrieb:
> Ich hatte nur überflogen, aber sind die Modes in der Arduino Doku nicht
> an den Wiki-Artikel angelehnt? Für mich sah die Belegung gleich aus.

Ich hab es gerade nochmal verglichen, bin da wohl irgendwie in der 
Splate verrutscht, mit Mode 3 geht es.

von Werner (Gast)


Lesenswert?

Wow.

Ein ganzer Thread, der ein Problem gelöst hat,
ganz ohne irgendeinen Troll oder chronischer-Hater Post.

Das lässt neue Hoffnung in mir keimen, dass dieses Forum doch noch von 
vernünftigen Leuten Besucht wird.

Danke!


Werner


PS: Freut mich, dass es nun funktionert. Schönes Wochenende.

von name123 (Gast)


Lesenswert?

Ich benötige doch nochmal Hilfe bei der Umrechnung.

Ich nutze folgenden Code:
1
  byte MSB1;
2
  byte LSB1;
3
  byte MSB2;
4
  byte LSB2;
5
  unsigned int volt = 0;
6
7
  SPI.beginTransaction(ADC_settings);
8
  MSB1 = SPI.transfer(4, 0x00, SPI_CONTINUE);
9
  LSB1 = SPI.transfer(4, 0x00, SPI_CONTINUE);
10
  MSB2 = SPI.transfer(4, 0x00, SPI_CONTINUE);
11
  LSB2 = SPI.transfer(4, 0x00);
12
  
13
  volt = (MSB1 << 8);
14
  volt = (volt | LSB1);
15
  volt = (volt >> 2);

Laut DB kommen erst 2 nullen, dann 12 Daten und dann nochmal 2 nullen 
für MSB1 und LSB1 (Fig. 31 auf S. 19 im DB).
Speise ich mit einem Netzeil 0,6V an INA+ an und an INA+ liegt GND 
erhalte ich aber
MSB1: 0b00101001
LSB1: 0b00101000

lässt man die beiden Nullen am Anfang und am Ende weg kommt 2634 (dez) 
raus, da U=(ADC*Vref)/4096 ist müssen es 1,3V sein?! Habe ich irgendwo 
einen Umrechnungsfehler gemacht?

von Manfred (Gast)


Lesenswert?

name123 schrieb:
> lässt man die beiden Nullen am Anfang und am Ende weg kommt 2634 (dez)
> raus, da U=(ADC*Vref)/4096 ist müssen es 1,3V sein?! Habe ich irgendwo
> einen Umrechnungsfehler gemacht?
Warum nimmst Du nicht einfach ein Multimeter in die Hand, geht deutlich 
schneller als hier Fragen zu formulieren.

Spannungsquelle (2 Batterien) und Drehpoti oder ein Labornetzgerät. 
Spannung an den ADC, messen, gucken welchen Wert der ausspuckt und gut 
gewesen.

von name123 (Gast)


Lesenswert?

Manfred schrieb:
> Warum nimmst Du nicht einfach ein Multimeter in die Hand, geht deutlich
> schneller als hier Fragen zu formulieren.

Ich halte 0,6V vom Netzteil direkt an den ADC Eingang und das Ergebnis 
das ich ausrechne ist dann 1,3V. Wo soll ich da noch was mit einem 
Multimeter messen?


Kann es daran liegen, dass ich nicht 16bit einlesen sondern 2x8Bit?
Aber eigentlich müsste ich doch durch das low halten des CS Pin diese 
einfach aneinander hängen können oder?

von chris_ (Gast)


Lesenswert?

Das Problem ist: MSB war nur 8 bit lang. Deshalb geht das Schieben 
schief.

Anmerkung: Bei Microcontrollern immer stdint.h verwenden, nie die 
C-Typen.

<c>
  uint16_t MSB1;
  uint16_t LSB1;
  uint16_t MSB2;
  uint16_t LSB2;
  uint32_t = 0;

  SPI.beginTransaction(ADC_settings);
  MSB1 = SPI.transfer(4, 0x00, SPI_CONTINUE);
  LSB1 = SPI.transfer(4, 0x00, SPI_CONTINUE);
  MSB2 = SPI.transfer(4, 0x00, SPI_CONTINUE);
  LSB2 = SPI.transfer(4, 0x00);

  volt = MSB1 << 8;
  volt = volt | LSB1;
  volt = volt >> 2;
</c>

von Tany (Gast)


Lesenswert?

chris_ schrieb:
> Das Problem ist: MSB war nur 8 bit lang. Deshalb geht das Schieben
> schief

mann kann auch so lösen:
uint16_t val=0;
val = (uint16_t)SPI.transfer(4, 0x00, SPI_CONTINUE)<<8; //MSB1
val|= (uint16_t)SPI.transfer(4, 0x00, SPI_CONTINUE)<<0;

von name123 (Gast)


Lesenswert?

chris_ schrieb:
> Das Problem ist: MSB war nur 8 bit lang. Deshalb geht das Schieben
> schief.

danke, war mir nicht bewusst, dass erst in MSB geschoben wird und der 
Wert dann in die variable volt übernommen wird.


Das Problem besteht leider immernoch, schaue ich mir die Werte, die 
dirket nach dem lesen in MSB und LSB stehen und setzte diese zusammen 
dann kommt bei beiden Kanälen 1,3V raus. Ist das IC so im falschen 
operation mode?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.