Forum: Mikrocontroller und Digitale Elektronik AD-Wandler mittels SPI auslesen


von AD-Wandler mittels SPI auslesen (Gast)


Lesenswert?

Guten Abend liebe Forumsmitglieder,

ich bin noch ein Anfänger und meine Erfahrungen in Sachen 
Mikrocontrollerprogrammierung sind noch nicht so gut, von daher möchte 
ich gerne eine Frage in dieses Forum hineinstellen - genaugenommen sind 
es mehrere Fragen. Einiges habe ich schon anhand des Datenblatts 
verstanden, aber an anderen Stellen bin ich mir noch nicht so sicher.

Ich habe einen AD-Wandler mit der Bezeichnung Max186. Dabei handelt es 
sich um einen 12bit-AD-Wandler, der acht Kanäle hat. Die Kommunikation 
findet über den SPI-Bus statt - und genau an der Stelle harpert es.

Ich möchte den Max186 mit meinem Raspberry Pi verbinden. Der Raspberry 
Pi verfügt schon über einen SPI-Bus und über Python kann ich die 
SPI-Funktionen einbinden.

Hier ist mal ein Link zum Datenblatt von dem ADC:
http://datasheets.maximintegrated.com/en/ds/MAX186-MAX188.pdf

Der SPI-Bus besitzt also 3 Verbindungsleitungen - MISO, MOSI, SCLK/CLK. 
Diese drei Leitungen verbinde ich mit dem Raspberry.

Ein Problem ist - der ADC arbeitet mit 5 Volt, der Raspi mit 3,3 Volt. 
Die Pegel an den GPIO-Ports am Raspi betragen etwa 3 Volt. Die 
Mindestspannung an den Kontakten vom ADC muss 2,4 Volt betragen, damit 
der ADC an dem jeweiligen Kontakt auf HIGH schaltet. Also reicht das aus 
vom Raspi.

Der ADC liefert einen Pegel von 4 Volt (Output Voltage High). Da baue 
ich mir dann einfach einen kleinen Spannungsteiler, damit die Spannung, 
die ich an den Pin des Raspis sende, max. 3 Volt beträgt und ich den 
Raspi nicht kaputt mache.

Dann muss ich noch CS mit einem freien GPIO-Port des Raspis verbinden 
und da fangen meine Schwierigkeiten an. Im Datenblatt steht über dem 
Anschluss CS (Chipselect) ein Balkten. Dass heisst für mich, dass ich 
alles negiert zu betrachten habe. Ich habe es jetzt so verstanden - ist 
der Pegel von CS_quer = HIGH, dann befindet sich der ADC im Ruhezustand 
- also muss ich den Pin vom Raspi auf HIGH setzen, wenn der ADC aus sein 
soll. Möchte ich nun mit dem ADC kommunizieren, dann muss ich den 
GPIO-Port vom Raspi auf LOW setzen, damit der ADC "eingeschaltet" wird. 
Habe ich das so richtig kapiert?

Und jetzt kommt mein nächstes Problem - die Sache mit dem Clock. Am 
SPI-Bus ist ja ein Anschluss mit der Bezeichnung Clock vorhanden (SCLK).

Ich habe es jetzt so verstanden - immer, wenn ein Clock-Signal gesendet 
wird, dann wird ein Bit vom ADC gelesen und auch gesendet (auch auf dem 
Raspi).

Wenn ich nun mit meinem ADC kommunizieren will, dann würde ich es 
folgendermassen machen:

Init-Zustand:
GPIO = HIGH (damit ADC ausgeschaltet ist).

Kommunikationszustand:
GPIO = LOW (ADC einschalten)
Sende/Empfange über SPI, bis Vorgang abgeschlossen
GPIO = HIGH (ADC ausschalten)

Ich bin mir jetzt leider noch nicht sicher, ob ich es so richtig 
verstanden habe. Ich würde mich sehr freuen, wenn jemand von Euch mir 
etwas weiterhelfen könnte.

von Sven B. (scummos)


Lesenswert?

Das Raspberry Pi hat Harware-CS-Pins für SPI, das sind die CE0 und CE1 
hier: http://elinux.org/images/2/2a/GPIOs.png

Nimm einfach die, dann sollte der SPI-Controller dir diesen 
Problembereich schonmal abnehmen.

von c-hater (Gast)


Lesenswert?

AD-Wandler mittels SPI auslesen schrieb:

> Wenn ich nun mit meinem ADC kommunizieren will, dann würde ich es
> folgendermassen machen:
>
> Init-Zustand:
> GPIO = HIGH (damit ADC ausgeschaltet ist).
>
> Kommunikationszustand:
> GPIO = LOW (ADC einschalten)
> Sende/Empfange über SPI, bis Vorgang abgeschlossen
> GPIO = HIGH (ADC ausschalten)

Richtig, in etwa so läuft das im Allgemeinen, auch wenn du vergessen 
hast zu erwähnen, ob die auch den richtigen SPI-Mode eingestellt hast, 
es gibt nämlich derer vier. Allerdings beschreibt SPI auch bloß die 
gundsätzliche Funktionsweise der Kommunikation.

Der Teufel steckt aber oft im Detail, nämlich in deiner Zeile

> Sende/Empfange über SPI, bis Vorgang abgeschlossen

hast du gleich zwei völlig nichtssagende Allgemeinplätze verwendet, 
nämlich "Vorgang" und "abgeschlossen". Das ist beides nicht von SPI 
abgedeckt, sondern fast bei jedem Scheiß-Chip ein wenig anders 
umgesetzt.

Und die Beschreibung dieser wichtigen Details ist leider nur in wenigen 
Datenblättern wirklich eindeutig und völlig unmißverständlich. Manchmal 
hat man sogar fast den Eindruck, daß ausgerechnet bei diesem Kapitel der 
Schreiberling absichtlich vorher abgefüllt wurde...

Wie auch immer. Was ich eigentlich sagen wollte: SPI kann schon für sich 
selber genug Probleme schaffen. Zusätzliche Probleme durch improvisierte 
oder gleich ganz abwesende Pegelwandler will man da echt nicht auch noch 
haben. Du solltest also deine Bastellösungen besser über Bord werfen, 
wenn du nicht tagelang darüber rätseln willst, ob nun deine Software 
noch unfertig ist oder ob es wegen der improvisierten Hardware nicht 
funktioniert.

Solche Bastellösungen kann man mal ausprobieren, wenn man sich mit dem 
speziellen Target bereits auskennt und es in einer ordentlichen 
Hardwareanbindung stabil am Laufen hat. Wenn es nämlich dann ohne 
Pegelwandler nicht mehr funktioniert, weiß man wenigstens sofort, woran 
es liegt...

von AD-Wandler mittels SPI auslesen (Gast)


Lesenswert?

c-hater schrieb:
> Du solltest also deine Bastellösungen besser über Bord werfen,

ok, und was soll ich dann stattdessen machen?

von Noch einer (Gast)


Lesenswert?

Schade, für den MCP3008 gäbe es fertigen Code und ausführliche 
Anleitungen.

https://learn.adafruit.com/reading-a-analog-in-and-controlling-audio-volume-with-the-raspberry-pi

Viellecht findet sich da ein Hinweis auf das übersehene Detail.

von M. K. (sylaina)


Lesenswert?

AD-Wandler mittels SPI auslesen schrieb:
> Dann muss ich noch CS mit einem freien GPIO-Port des Raspis verbinden
> und da fangen meine Schwierigkeiten an. Im Datenblatt steht über dem
> Anschluss CS (Chipselect) ein Balkten. Dass heisst für mich, dass ich
> alles negiert zu betrachten habe. Ich habe es jetzt so verstanden - ist
> der Pegel von CS_quer = HIGH, dann befindet sich der ADC im Ruhezustand
> - also muss ich den Pin vom Raspi auf HIGH setzen, wenn der ADC aus sein
> soll. Möchte ich nun mit dem ADC kommunizieren, dann muss ich den
> GPIO-Port vom Raspi auf LOW setzen, damit der ADC "eingeschaltet" wird.
> Habe ich das so richtig kapiert?

Hast du. Der Raspberry ist der Master und der kann mit den Slaves 
sprechen. Damit die Slaves auch wissen wer genau gemeint ist bekommen 
sie das durch ChipSelect mitgeteilt. Und den Querstrich hast du richtig 
interpretiert ;)

AD-Wandler mittels SPI auslesen schrieb:
> Ich habe es jetzt so verstanden - immer, wenn ein Clock-Signal gesendet
> wird, dann wird ein Bit vom ADC gelesen und auch gesendet (auch auf dem
> Raspi).

Auch das ist richtig.
Doch vorsicht. Wichtig ist hier auch in welchem Clock-Mode du dich 
befindest. Bist du im external Mode dann gibst du an der SCKL-Leitung 
die Abtastrate vor mit der die Analogsignale gesampelt werden. Das ist 
ein wenig tricky. Schau dir auch mal die Timing-Diagramme an.

von AD-Wandler mittels SPI auslesen (Gast)


Lesenswert?

Michael Köhler schrieb:
> Doch vorsicht. Wichtig ist hier auch in welchem Clock-Mode du dich
> befindest. Bist du im external Mode dann gibst du an der SCKL-Leitung
> die Abtastrate vor mit der die Analogsignale gesampelt werden. Das ist
> ein wenig tricky. Schau dir auch mal die Timing-Diagramme an.

Ich möchte gerne den internen Clock-Mode benutzen, aber das kann ich ja 
über das Datenbit einstellen, dass ich dann über den SPI-Bus versenden 
möchte. In der Tabelle 2 in dem Datasheet (Control-Byte-Format) kann ich 
ja einstellen, welchen Clock-Modus ich verwenden will, welcher der 8 
Kanäle abgetastet werden soll, ob die Messung uni- oder bipolar und 
sigle-ended oder differentiell ist.

Wenn ich es also erfolgreich angeschlossen habe und ich am Kanal 1 mit 
Internal-Clock, Single-Ended, unipolar messen möchte, dann bastele ich 
mir ein Byte:

SendByte = 10001110

Also würde mein Programm im Pseudocode etwa so aussehen:

# init
GPIO(CE0) = HIGH
SendByte = 10001110

# LeseWert:
CE0 = LOW
Sende SendByte
CE0 = HIGH
warte 1,5 Mikrosekunden (acquisition time)
CE0 = LOW
Lese LeseBytes
CE0 = HIGH

Ich muss ja einen Moment warten, bis die Messung am ADC fertig ist. Ich 
habe es jetzt so verstanden, dass ich nach dem Senden von SendByte CE0 
erstmal wieder auf HIGH setzen muss (Figure 9 in dem Datasheet). Nach 
der Acquisition-Time von 1,5 Mikrosenkunden setze ich CS = LOW und bei 
der fallenden Flanke von SCLK wird das erste Bit empfangen.

Kann ich eigentlich auch länger als die 1,5 Mikrosekunden warten? In dem 
Datenblatt habe ich gelesen, dass man ein ExternalClock nur benutzen 
soll, wenn es schnell genug geht. Wenn man länger als 10 Mikrosekunden 
wartet, dann entläd sich der Kondensator am Hold-Glied und die Messung 
wird verfälscht. Aber um den erzeugten Wert zu lesen, müsste ich doch 
theoretisch auch eine Sekunde warten können - oder geht der binäre Wert, 
der dann seriell über den SPI-Bus versendet wird, auch nach einiger Zeit 
"verloren", wenn er nicht schnell genug gelesen wird (also meine 
Wartezeit zu lange ist)?

von Sven B. (scummos)


Lesenswert?

Ich glaube, das ist oben untergegangen: Der SPI-Controller hat ein 
Hardware-CE, was automatisch immer so geschaltet wird, wie es sein soll 
(CE0/CE1 auf dem Pin-Header). Bei mir hat das immer funktioniert. Willst 
du es nicht erstmal damit versuchen?

von AD-Wandler mittels SPI auslesen (Gast)


Lesenswert?

Sven B. schrieb:
> Willst du es nicht erstmal damit versuchen?

Doch will ich - ich habe es erst durch Deinen Beitrag verstanden, dass 
die beiden CE0/1 auch zum SPI-Bus gehören, aber ich wollte es gerne vom 
Grundprinzip her verstanden haben. CE0/1 wird  ja automatisch durch das 
SPI-dev-Modul von Python gesetzt, weil ich dort ja anwählen muss, 
welcher device benutzt wird.

von Sven B. (scummos)


Lesenswert?

Ja, genau. Gut, zu Lehrzwecken ist das sinnvoll das so zu machen :-)

von AD-Wandler mittels SPI auslesen (Gast)


Lesenswert?

Ich hab jetzt noch ein Problem mit der maximalen Strombelastbarkeit des 
D_out-PINS an dem ADC. Ich muss mir ja einen Spannungsteiler bauen und 
der Gesamtwiderstand des Spannungsteilers muss so gewählt werden, dass 
der Strom nicht zu groß (und ggf. auch nicht zu klein) sein darf. Ich 
finde dazu keine Angabe in dem Datenblatt. Ich habe in der Tabelle bei 
"Electrical Characteristics" im Datasheet geschaut (Seiten 3-5) und 
lediglich auf Seite 4 unter "DIGITAL OUTPUTS DOUT, SSTRB) gefunden, dass

Output Voltage Low = max. 0,4 Volt ist
Output Voltage High = min 4 Volt
Leeakage Current = +- 10 uA
Output Capacity = 15pF.

Ist das die Angabe Leakage Current, die ich zur Bestimmung des 
Gesamtwiderstands verwenden muss?

von Sven B. (scummos)


Lesenswert?

In Abs. Maximum Ratings steht "Digital Output Sink Current 25mA". 1 bis 
2mA pro Pin ist auf jeden Fall ok würde ich sagen.

: Bearbeitet durch User
von AD-Wandler mittels SPI auslesen (Gast)


Lesenswert?

Ist mit "Sink Current" der Gesamtstrom gemeint, mit dem der ADC belastet 
werden darf?

Dort steht auch bei Absolute Maximum Ratings: Vdd to AGND -0,3V to 6 V.

Warum ist dort von -0,3 Volt die Rede? Heisst das "nur", dass der ADC 
bis -0,3 Volt nicht kaputt geht? Könnte ich den ADC auch mit 3,3 Volt 
betreiben? So würde ich das jetzt verstehen. Aber das geht ja nicht, da 
die Vergleichsspannung vom ADC 4,096 Volt betragen muss. Dat der ADC 
dazu einen internen Spannungsregler, der dann aus den 5 Volt exakte 
4,096 Volt macht?

von Max H. (hartl192)


Lesenswert?

Die Absolute Maximum Ratings geben an, bei welchen Werten der IC knapp 
noch nicht kaputt wird. Das heißt aber nicht dass er im gesamten Bereich 
auch zuverlässig funktioniert. An diesen sollte man sich im Normalfall 
auch nicht orientieren.

In den ELECTRICAL CHARACTERISTICS steht was von
>Positive Supply Voltage      VDD      5 ±5%      V

: Bearbeitet durch User
von Sven B. (scummos)


Lesenswert?

AD-Wandler mittels SPI auslesen schrieb:
> Ist mit "Sink Current" der Gesamtstrom gemeint, mit dem der ADC belastet
> werden darf?

Ich weiß leider nicht, ob sich das auf einzelne Outputs oder auf die 
Summe bezieht. 25mA pro Output klingt eigentlich zu viel. Aber bestimmt 
weiß das jemand anders hier.

von AD-Wandler mittels SPI auslesen (Gast)


Lesenswert?

Ok, ich belaste den D_Out vom ADC mit 1mA.

Ich hab jetzt ein kleines Problem. Ich habe jetzt versucht, mit dem Oszi 
den SPI-Bus zu checken. Wenn ich MISO und MOSI brücke und mit dem Oszi 
messe, dann sehe ich ein Binärsignal im Oszi und empfange am SPI-Bus 
etwas.

Wenn ich jetzt nur alleine den MOSI-Anschluss checke, dann sehe ich am 
Oszi garnichts. Ich habe auch schon versucht, den MISO-Anschluss auf 
Masse und einmal auf Vcc zu legen - da misst das Oszi auch nichts.

CS und SCLK funktionieren beide einwandfrei. Ich habe auch es dierekt am 
ADC ausprobiert, MOSI an D_In am ADC und MISO mit D_out verbunden. Dort 
genauso - ist das verbunden, dann messe ich keine Pegel im Oszi.  Woran 
könnte das liegen?

von Purzel H. (hacky)


Lesenswert?

Wenn der  CS falsch ist sendet der ADC nicht, sondern hat tristate and 
Dataout.
Ich implementiere eine SPI Schnittstelle jeweils per software genau nach 
datenblatt. Manche Datenblaetter haben diesbezueglich keine Redundanz, 
dh man muss jeden Satz genau lesen und verstanden haben, nicht einfach 
Annahmen treffen.

von Sven B. (scummos)


Lesenswert?

Meine Erfahrung mit SPI ist dass man beim Empfänger (also beim Slave bei 
MOSI und CS und beim Master bei MISO) einen kleinen Kondensator (100pF) 
nach GND braucht, weil sonst wegen kleiner Störungen oft komisches Zeug 
passiert. Kannst du ja mal versuchen.

Grüße,
Sven

von AD-Wandler mittels SPI auslesen (Gast)


Lesenswert?

Vielen Dank fuer die Vielzahl Eurer Tipps.

Irgendwie hats auf einmal funktioniert. Ich hoffe, dass es jetzt aber 
nur nicht auf dem Steckbrett so ist, sondern auch noch, wenn ich später 
den Kram auf eine Platine schweiße.

von Sven B. (scummos)


Lesenswert?

AD-Wandler mittels SPI auslesen schrieb:
> Irgendwie hats auf einmal funktioniert.
Passt auch zu meiner Aussage mit dem Kondensator.

von Max H. (hartl192)


Lesenswert?

AD-Wandler mittels SPI auslesen schrieb:
> wenn ich später
> den Kram auf eine Platine schweiße.
Ich empfehle löten, beim Schweißen wirst du die Bauteile wahrscheinlich 
töten.

von AD-Wandler mittels SPI auslesen (Gast)


Lesenswert?

Ich habe nun ein Softwareproblem beim Auslesen des AD-Wandlers. Ich 
empfange etwas und kann auch was senden.

Ich habe ein kleines Python-Programm für den Raspberry geschrieben, dass 
den AD-Wandler auslesen soll:
1
Import spidev
2
Import time
3
4
spi = spidev.SpiDev()
5
spi.open(0,0)
6
7
sendbit = []
8
sendbit.append(0b10000010)  # bipolarer Modus
9
acquisition_time = 12./1000000
10
11
spi.writebytes(sendbit)
12
time.sleep(acquisition_time)
13
result=spi.readbytes(2)

Bei der Variable result hab ich so meine Probleme. Es sind dort zwei 
Werte enthalten, einmal für das bit0 und dann für das bit1. Das 
AD-Wandlerergebnis ist 12bit breit. Ich messe bipolar, also von -2 bis 
+2 Volt. Eines der 12 Bits muss also für das Vorzeichen stehen, ob plus 
oder minus. Ich gehe davon aus, wenn das MSB=True ist (most significant 
bit), dann ist das Ergebnis positiv, also Spannung größer Null. Ich habe 
es im Datenblatt aber noch nicht gefunden.

Jetzt geht es noch darum, die Bits zusammenzufassen, um ein 
Gesamtergebnis zu bekommen. Dazu habe ich mir gedacht, dass ich Bit0 
einfach um vier stellen nach links shifte, Bit1 vier Stellen nach 
rechts.

Regebnis = (result[0]<<4)&(result[1]>>4)

Aber irgendwie kommt da nichts gescheites bei heraus und ich weis nicht, 
wo der Fehler liegen könnte.

von Sven B. (scummos)


Lesenswert?

Huch? Komische Umrechnung. Ich würde sowas machen wie
(bits[1] << 8) + bits[0]
oder so. Modulo Vorzeichen.

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.