ADC-Register-Setup mit Spi-Kommunikation

Ich bin neu bei Mikrocontrollern - ich versuche, externe ADC-Werte von einem AD7798-ADC mithilfe von SPI-Kommunikation zu lesen.

Zunächst muss ich einige ADC-Register einrichten - einige Register sind nicht konfiguriert. Um die Register zu konfigurieren, muss ich das Kommunikationsregister verwenden, um auszuwählen, welches Register ich konfigurieren möchte.

Zum Beispiel möchte ich das AD7798-Konfigurationsregister (16 Bit) einstellen. Ich habe Code wie diesen: #include #define ADC_CS PORTB.3 #define WG_CS PORTB.4 #define MOSI PORTB.5 #define MISO_PU PORTB.6 #define MISO_PIN PINB.6 #define SCK PORTB.7

//global functions.
unsigned int adcConfig;
unsigned int adcMode;
unsigned int adcId;

void init_io(void) 
{ 
DDRB = 0xBF;        // make SCK, MOSI, CS1, CS2 outputs 
ADC_CS = 1;              //disable ADC 
WG_CS = 1;               //disable WaveGenerator 
MISO_PU = 1;             //enable pull-up on MISO so we can test !RDY 
} 

unsigned char spi(unsigned char data) 
{ 
//Start transmision 
SPDR = data; 
//Wait for transmision complete 
while (!(SPSR & (1<<SPIF))); 
return SPDR; 
} 

//Sets the waveform generator output to given phase 
void SetWGPhase(unsigned int phase) 
{ 
SPCR = 0x5A; // mode #2 F_CPU/64 
WG_CS = 0;                      // enable 
spi(0x20); 
spi(0x00); 
spi((phase >> 8) | 0xC0);       //Load into phase register 0 
spi(phase & 0x00FF); 
WG_CS = 1; 
} 

void setupAd(){ 
    SPCR = 0x5D; 
    ADC_CS = 0; 
   // while(spi(0x10) != 0x10); 
    spi(0x10);                  //set up communication register for configuration reg. 
    spi(0x07);        
    spi(0x10); 

    spi(0x08);                  //set up communication register for mode reg. 
    spi(0x00);        
    spi(0x0A); 
    ADC_CS = 1; 
    } 


 unsigned int ReadAd(void) 
 { 
unsigned int data; 
SPCR = 0x5D; // mode #3 F_CPU/16 
CheckStatus();
ADC_CS = 0;                     // enable 
while (MISO_PIN != 0) ;         // wait for  DOUT/!RDY line to go low 
//Read data 
spi(0x58);                      //Place readinstruction in communication register 
data = spi(0xFF);               // read hi-byte 
data = (data << 8) | spi(0xFF); // and lo-byte. 
ADC_CS = 1;                     // disable 
return data; 
} 

 unsigned char CheckStatus(void)
{
char adcStatus; 
            SPCR = 0x5D;
            ADC_CS = 0;                     // enable  
            while(ADC_CS_PIN);
            adcStatus = 0xFF; 
while(!(adcStatus & 0x80)){                                     

             spi(0x40);
             adcStatus = spi(0xFF); 
          }          
ADC_CS = 1;                      

return adcStatus;
}

unsigned int ReadAdConfReg(void) 
{              
  unsigned int retvalconfig;
SPCR = 0x5D;  
ADC_CS = 0;      
while (MISO_PIN != 0) ; 
spi(0x50); 
adcConfig = spi(0xFF);    
adcConfig = (adcConfig << 8) | spi(0xFF); 
retvalconfig= adcConfig;
ADC_CS = 1; 
return retvalconfig;
} 

unsigned int ReadAdModeReg(void) 
{              
  unsigned retvalmode;
SPCR = 0x5D;  
ADC_CS = 0;        
while (MISO_PIN != 0) ; 
spi(0x48); 
adcMode = spi(0xFF);  
adcMode = (adcMode << 8) | spi(0xFF); 
retvalmode =adcMode;   
ADC_CS = 1;
return retvalmode;
} 
unsigned int ReadAdIdReg(void) 
{              

SPCR = 0x5D;  
ADC_CS = 0;          
while (MISO_PIN != 0) ; 
spi(0x60); 
adcId = spi(0xFF);    
ADC_CS = 1;
 return adcId; 
} 

Wenn ich das Konfigurationsregister drucke, gibt es den Wert "16383" an. aber wenn ich das Ziel aus-/einschalte, erhalte ich "1808 (was 0x0710 entspricht)", danach gibt es den gleichen Wert wie "16383". Ich habe auch mit verschiedenen Konfigurationen getestet, aber es ändert sich nicht, es wird immer "16383" gedruckt, außer beim Ausschalten / Einschalten. Ich denke, der Standardwert.

Selbst mit dem Modusregister wird immer "10 (was 0x000A entspricht)" gedruckt, aber das ist der Wert, den ich immer bekomme, auch wenn ich die Konfiguration auf "0x0022" ändere.

Sogar ich habe versucht, das ID-Register zu lesen, aber es gibt "0x48". aber im Datenblatt wurde "0xX8" für AD7798 erwähnt. Vielen Dank im Voraus.

Jemand hilft mir bitte, ich bekomme keine Ahnung, welchen Fehler ich hier mache.

Sie werden feststellen, dass Benutzer eher bereit sind zu antworten, wenn sie alle möglichen relevanten Informationen erhalten, wie z. B. einen Link zum Datenblatt des ADC.
Was soll spi(0x07)<<8 tun? Der <<8-Teil hat hier keine Wirkung.
Sie sagten in einem Kommentar zu einer früheren Frage , dass Sie mit einem Oszilloskop überprüft haben, ob die Wellenform korrekt ist. Jetzt bin ich mir nicht mehr so ​​sicher. Können Sie einen Screenshot vom Scope- oder Logikanalysator posten?
@Rocketmagnet Es tut mir sehr leid für den vorherigen Kommentar. Ich habe es falsch gecheckt. Ich habe es nach Ihrem Vorschlag noch einmal überprüft, also habe ich festgestellt, dass ein Unterschied im Taktsignal vorhanden war, und ich habe festgestellt, dass mein Konfigurationsregister nicht eingestellt war. Ich habe einige Änderungen im vorherigen Code vorgenommen, aber ich muss das versuchen und ich werde es dort bearbeiten.
@verendra - Wenn Sie Ihrer Frage das 'c'-Tag hinzufügen, erhält der Code automatisch eine Syntaxhervorhebung.

Antworten (4)

Während dieses Datenblatt meine Augen mit der Komplexität einer einfachen Konvertierung/Lesung bluten lässt, müssen Sie einige einfache Änderungen vornehmen.

Ich bin mir nicht sicher, warum die Leute es sich schwerer machen, als es wirklich ist. Sie ziehen die Chipauswahlleitung nach unten, und der Ziel-IC sollte einfach lesen, was Sie ihm geben, bis Sie ihn wieder nach oben ziehen. Nur weil Ihre Methode einen 8-Bit-Wert annimmt, heißt das nicht, dass Sie sie nicht zweimal aufrufen können, um einen 16-Bit-Wert an Ihren IC zu übergeben. Es ist nicht nötig, irgendetwas zu bit-bangen. Das ist Unsinn.

Rufen Sie Ihre SPI-Methode auf, um dem ADC mitzuteilen, dass Sie in das Konfigurationsregister schreiben möchten:

spi(0x10);

Wie ich aus dem Datenblatt verstehe, können Sie Ihren 16-Bit-Wert danach sofort in das Konfigurationsregister schreiben, also würden Sie Folgendes tun:

spi(0x07);
spi(0x10);

Ich habe vergessen, auf welche Weise das auf der Ziel-IC-Seite zusammengebaut wird, sodass Sie sie einfach umkehren können, wenn die Dinge nicht richtig funktionieren. Es besteht überhaupt keine Notwendigkeit, irgendwelche Werte zu verschieben. Im schlimmsten Fall müssen Sie die CS-Leitung wieder auf High ziehen, bevor Sie sie wieder auf Low ziehen, um die Daten tatsächlich zum Schreiben in das Konfigurationsregister zu senden.

Ansonsten ist das super einfach. Denken Sie daran, wenn das Ziel-SPI-Gerät mehr als ein Byte erwartet, dh einen 16-Bit-Wert, stehen die Chancen gut, dass die Bytes in der Reihenfolge gesendet werden, in der sie sonst normal erscheinen würden (wenn Sie also 0x1234 senden möchten, hätten Sie 0x12 und 0x34) funktionieren gut und Sie müssen nichts verschieben.

Wenn der SPI-Controller auf dem Mikrocontroller 8-Bit ist (sehr wahrscheinlich), wird die SS nach 8 Bit freigegeben, wie Sie sagen, wodurch die Daten zwischengespeichert werden. Die nächsten 8 Bits werden das beheben: Wenn sie zwischengespeichert werden, ist das 16-Bit-Wort vollständig, aber der erste Zwischenspeicher wird vom ADC verarbeitet und wahrscheinlich als Müll oder Schlimmeres angesehen. Bit-Banging kann eine Lösung sein.
Das ist, wenn Sie die SS freigeben ... was Sie nicht müssen. Ich habe 16-Bit-Übertragungen auf einem LPC1769 mit einem 16-Bit-SSP-Controller und auf einem normalen alten Arduino Uno mit vermutlich einem 8-Bit-SPI-Controller durchgeführt. Die Steuerung der SS funktioniert in beiden Fällen wie vorgesehen. Ziehen Sie es niedrig. Senden Sie 16 Bit. Zieh es hoch. Voila. Das sieht wirklich so aus, als würde es zu dieser verrückten harten Lösung gemacht, aber es gibt keine Möglichkeit, dass jeder da draußen mit einem ATmega Bit-Banging SPI macht, nur um eine verdammte 16-Bit-Übertragung durchzuführen. :P Der Schlüssel ist, die SS-Leitung selbst zu steuern.
ja, du hast vollkommen recht. Für einen Moment dachte ich, der SS würde vom SPI-Controller gesteuert, aber das ist unmöglich, weil er nicht weiß, welche der 57 :-) SS-Leitungen umgeschaltet werden sollen. Ich stehe korrigiert: kein Bit-Knallen erforderlich. (Mit dem NXP LPC17xx sowieso kein Problem, da seine SPI-FIFO-Länge vom Benutzer wählbar ist.)
Rechts. Ich steuere die SS in meinem Fall aus genau diesem Grund manuell ... Ich weiß besser als der Controller, was ich senden und empfangen möchte. Das war die Grundlage meiner gesamten Argumentation – steuere es manuell, und du bist goldrichtig. Tut mir leid, wenn das nicht klar rübergekommen ist. :)
spi()Aber OP muss dann seine Funktion ändern . Zwischen den Cals deaktiviert er SS nie, so dass dies automatisch in der Funktion ist. Vielleicht könnte er ein optionales Argument hinzufügen hold_ss.
Es sieht so aus, als hätte er ChipSelectAd(), um die SS-Leitung speziell für den ADC umzuschalten. Er muss nur die Bestätigung aufheben und erneut bestätigen, bevor er die eigentlichen Registerinhalte sendet.
@Tody: Sie haben Recht mit der ChipSelectAd()Funktion, aber falsch, sie zwischen einem Befehl und den zugehörigen Daten zu deaktivieren.
Fair genug. Ich war nicht wirklich besorgt darüber, wie der ADC seine Daten für eine Operation nahm, aber ich war mehr damit beschäftigt, darauf hinzuweisen, dass das Zuführen von 16 Bit dummerweise einfach und nicht kompliziert war. :D
@TobyLawrence Hier verwende ich einen ATmega32-A-Controller. Darf ich etwas schlagen? Ich weiß es nicht. kann ich es wie gewohnt versuchen, wie Sie vorgeschlagen haben.
Das Codebeispiel von @verendra Ben ist möglicherweise eine korrektere Implementierung, aber Sie sollten nichts schlagen müssen. Sie haben bereits alle Methoden, die erforderlich sind, um dies richtig zu tun. :)
@TobyLawrence Immer noch das gleiche Problem kannst du dir meinen bearbeiteten Code ansehen.

Die Chipauswahlleitung auf dem AD7798 ist aktiv niedrig. Es sieht so aus, als hätten Sie die Polarität in Ihrem Code rückwärts.

Sie müssen CS zu Beginn der Übertragung auf niedrig (0) und nach der Übertragung auf hoch setzen. Also versuchen Sie Folgendes:

void setupADC()
{
    ChipSelectAd(0); // was 1
    spi(0x10);
    spi(0x07);
    spi(0x10);
    ChipSelectAd(1); // was 0  
}

Überprüfen Sie auch, ob Sie den richtigen SPI-Modus verwenden (auf vielen Mikrocontrollern CPOL und CPHA genannt, für "Taktpolarität" und "Taktphase"). Dies bestimmt, welche Taktflanke Datenübergänge auslöst und welchen Pegel der Takt zwischen Transaktionen (im Leerlauf) hat.

Der AD7798 erfordert CPOL=1 und CPHA=1 (SPI-Modus 3).

Immer noch das gleiche Problem, können Sie sich meinen bearbeiteten Code ansehen.
@verendra: Oh ok, sieht so aus, als ob ChipSelectAddie Inversion durchgeführt wird (deshalb hilft es, mehr Code anzuzeigen). Welchen Prozessor verwendest du? (füge dies der Frage hinzu)
Kannst du dir meinen neuen Beitrag ansehen, aber er wurde geschlossen. Dort habe ich Oszilloskopbilder hinzugefügt, um Taktpolaritäten und Chipauswahl zu überprüfen.
@verendra: Bei diesen Oszilloskopaufzeichnungen sieht das Timing ziemlich gut aus, aber die Daten sind nicht 0x10, 0x07, 0x10. Irgendeine Idee warum? Ist dies eine Erfassung eines anderen Befehls? Sagen Sie auch, in welcher Reihenfolge die Aufnahmen sind (die oberste Spur in jeder ist eindeutig die Uhr, aber sind sie dann MOSI, MISO, CS?)
Diese Bilder beziehen sich nicht auf das ADC-Setup. Diese Bilder beziehen sich auf das Lesen des ADC-Register-Setups. Ich habe Clock mit CS, MOSI, MISO getestet, während ich ADCsetup gelesen habe. Es scheint richtig. Aber ich frage, wie man die Einrichtung des ADC-Registers überprüft.

Wie RocketMagnet sagt

spi(0x07)<<8;   

macht keinen Sinn. Obwohl die Funktion spi()definiert ist char, verschieben Sie sie nach links und verwerfen sie dann.

spi(0x07 << 8);   

verschiebt das Argument um 8 Bits nach links, also wird es sein 0x0700. Aber das macht nicht das, was du willst. Sie deaktivieren SS (Slave Select) zwischen Aufrufen der spi()Funktion nicht, sodass dies automatisch erfolgt. Ich würde es so ändern, dass es ein optionales Argument hold_sshat, sodass Sie das erste Byte und dann das zweite verschieben und erst dann SS deaktivieren können.

Auf diese Weise können Sie 16 Datenbits zum ADC verschieben, ohne Daten vor dem spi()Anruf verschieben zu müssen. Alternativ können Sie eine Funktion schreiben spi16():

unsigned int spi16(unsigned int data16)
{ 
    assert_SS(IOpin);          
    spi8(data >> 8); /* assuming LSB is shifted first */
    spi8(data);
    deassert_SS(IOpin);
}

(Ich ignoriere die eingehenden Daten im Moment.)

Das Datenblatt zeigt an, dass das Konfigurationsregister tatsächlich 16 Bit benötigt, aber obwohl es keine Details enthält, würde ich erwarten, dass es sie als einzelne Übertragung benötigt. Überprüfen Sie die spi()Funktion des Compilers Ihres Mikrocontrollers und das Datenblatt des Mikrocontrollers, um festzustellen, ob 16-Bit-Übertragungen überhaupt möglich sind. Wenn der Controller 16-Bit-Übertragungen ausführen kann, sollten Sie dies tun können:

spi(0x0710);

Wenn nur 8-Bit-Übertragungen möglich sind, müssen Sie meiner Meinung nach auf ein Bit-Banged-SPI zurückgreifen (was nicht so schwierig ist).

bearbeiten
Rocketmagnet weist mich auf die Definition der spi()Funktion hin:

char spi(char data) 

Das sagt alles: Das Argument ist vom Typ char, also nur 8-Bit.

( Danke für den Hinweis (kein Wortspiel beabsichtigt), Rocketmagnet )

Siehe seine vorherige Frage zur Definition von char spi (char data).
Er hat den Chip-Select-Pin schon zu den passenden Zeiten angesteuert, wenn auch vielleicht mit den falschen Pegeln.
@BenVoigt Ich steuere den Chip-Select-Pin genau. Jetzt schau dir meinen bearbeiteten Code an.

Welches Mikro verwendest du? Sie haben keine Angaben dazu gemacht, was spi()dort vor sich geht. Es könnte 8 Bit senden. In diesem Fall führt Ihre 8-Bit-Verschiebung zu Unsinn. Jetzt haben Sie angegeben, dass Sie "0x0710" senden möchten. Denken Sie eine Sekunde lang darüber nach, was in Ihrem Code passieren könnte.

Sie scheinen verwirrt darüber zu sein, wie diese SPI-Übertragung stattfindet.

Zuerst senden Sie 0x10. Angenommen , Ihre spi()Funktion sendet 16 Bits, senden Sie 0x0700. Sie haben gerade 0x100700 übertragen.

Ich bin mir ziemlich sicher, dass Ihre spi()Funktion Ihre 16 Bits nicht sendet. Sie senden also im Grunde zwei Bytes: 0x10 und 0x00. Gut gemacht.

Sie müssen die Byte-Reihenfolge umkehren, wenn Sie mehrere Bytes über SPI senden. Senden Sie zuerst 0x07 und erst dann 0x10.