mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik 16-Bit DAC an SPI Schnittstelle mit MSP430


Autor: Albert K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit der SPI Schnittstelle des MSP kann man ja maximal 8 Bit in das 
UCB0TXBUF Register laden und dann senden.

Jetzt ist es aber so, dass ich einen 16-Bit DAC habe, bei dem ich 16 Bit 
am Stück senden muss.
Ist das irgendwie möglich mit dem MSP ohne jetzt selbst so ein riesiges 
SPI Protokoll entwickeln zu müssen?

Der DAC ist ein LTC2641 -> siehe Seite 10/11
http://pdf1.alldatasheet.com/datasheet-pdf/view/21...

ich verwende den UCB0

Momentan habe ich den DAC so angeschlossen:
   DIN     P3.1/UC1SIMO/UC1SDA
   /CS     P3.2/UC1SOMI/UC1SCL
   SCLK    P3.3/UC1CLK/UC0STE

Ist das richtig so?

Autor: Moi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Öhm... SPI Protokoll entwickeln?!
Schicke einfach 2 mal 8 Bit hintereinander und du hast deine 16 Bit.

Autor: Albert K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
If there are less than 16 low-to-high transitions on SCLK while /CS remains
low, the data will be corrupted, and must be reloaded.

Es müssen vom DAC aus 16-Bit nacheinander kommen.

Autor: smd (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
weiß nicht ob es beim MSP auch so ist, aber wahrscheinlich gibt es ein 
Flag, das Du abfragen kannst und das gesetzt wird, sobald Dein erstes 
Byte fertig gesendet ist. Dann sendest Du das zweite und fertig bist Du.

Aber mal interessehalber - wo hast Du diesen Chip gekauft / wieviel 
bezahlt?

Autor: Albert K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Meinst du den LTC2641?
Das ist ein Sample von Linear.

Autor: smd (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der MSP agiert doch als Master, oder? Dann kannst Du ja dafür sorgen, 
daß immer 16 clocks (2 Byte) gesendet werden, siehe oben, bevor CS 
wieder auf high geht. wenn Deine SPI-SChnittstelle immer ein CS nach 
jedem Byte generieren will, nimmst Du eben ein normales Portpin als CS.

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Es müssen vom DAC aus 16-Bit nacheinander kommen.

Freilich, aber da steht nix von einer Zeitbegrenzung.
Lediglich /CS darf nicht zappeln zwischen den beiden 8-bit Daten.
Um /CS musst Du Dich eh' selbst kümmern, per I/O-Port.
Also liegt es ganz in Deiner Hand!

Autor: Christian R. (supachris)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Albert K. wrote:
>
> If there are less than 16 low-to-high transitions on SCLK while /CS
> remains
> low, the data will be corrupted, and must be reloaded.
> 
>
> Es müssen vom DAC aus 16-Bit nacheinander kommen.

Nein, das heißt lediglich, dass du das CS erst wieder auf H setzen 
darfst, wenn du 16 Bit reingeschoben hast.
Ich benutze auch LT ADCs mit 16 Bit SPI am MSP430 ohne Probleme. Ist 
doch nur ein Schieberegister in Hardware, das macht nur was, wenn eine 
Flanke am SCLK ist.

Autor: Albert K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, ich habe es grundsätzlich hingekriegt mit den 16-Bit, aber habe 
dennoch noch zwei Probleme.

1.) Wenn ich im Debugger schritt für schritt vorgehe, erhalte ich am DAC 
ausgang einmal 2.86V und 2.97V was stimmt und super ist.
Aber wenn ich dann das Programm in normaler Geschwindigkeit laufen 
lasse, verändert sich das dramatisch.
Statt 2.86V habe ich noch 2.25V und statt 2.97V nur noch 2.80V.
Kommt da der DAC nicht nach, bzw. muss ich irgendwo noch delays 
einbauen?

2.) Also wenn ich manuell eingebe was er im TXBUF dann an den DAC 
schicken soll, dann funktioniert es aber wenn ich wie unten eine 16-Bit 
variable einmal mit den oberen 8-Bit UND-Verknüpfe, sende und dann die 
unteren 8-Bit UND-Verknüpfe und sende, dann funktioniert es nur mit den 
unteren.

Bei den oberen also:
UCB0TXBUF = (dac & 0xFF00);
, schreibt er dann einfach 0x0000 in den TXBUF.

Was ist daran falsch?


Hier noch der C-Code (dac ist eine 16-Bit Unsigned variable):
/* init */
  UCB0CTL0 |= UCCKPH + UCMSB + UCMST + UCSYNC;       // 3-pin, 8-bit SPI master, MSB 1st
  UCB0CTL1 |= UCSSEL_2;               // SMCLK
  UCB0BR0 = 0x02;
  UCB0BR1 = 0;
  IE2 = UCB0TXIE;                     // Übertragungsinterrupt erlauben
  UCB0CTL1 &= ~UCSWRST;               // USCIB0 starten

/* code */
     P3OUT &= ~0x04;                   // DAC /CS enable
     while (!(IFG2 & UCB0TXIFG));      // Warte bis TXBUF bereit
     UCB0TXBUF = (dac & 0xFF00);               // Bit 15..8 senden
     while (!(IFG2 & UCB0TXIFG));      // Warte bis TXBUF bereit
     UCB0TXBUF = (dac & 0x00FF);               // Bit 7..0 senden
     P3OUT |=  0x04;                   // 16-Bit Übertragung fertig

Autor: Christian R. (supachris)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du nimmst das CS zu schnell wieder auf High. Wenn der TX-Buffer bereit 
ist, heißt das nicht, dass das Byte schon rausgeschoben ist. Das 
TX-Register ist doppelt gepuffert. Du musst warten, bis das RX-Flag im 
IFG2 gesetzt wird, erst dann ist das Byte raus. SPI sendet und empfängt 
ja gleichzeitig, also musst du warten bis der Empfang komplett ist. Dann 
musst du noch das Flag manuell zurücksetzen, oder den RX-Buffer in eine 
Dummy-Variable auslesen, da wird es zurück gesetzt.

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Bei den oberen also:
UCB0TXBUF = (dac & 0xFF00);
>, schreibt er dann einfach 0x0000 in den TXBUF.

Nö, er schreibt 0x00 rein, ist doch nur ein 8-bit-Register,
was ja anfänglich Dein Problem war!

Autor: Christian R. (supachris)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jo, das außerdem noch, du musst natürlich das HighByte nach unten 
schieben:
Etwa so:
unsigned char dummy;

P3OUT &= ~0x04;                   // DAC /CS enable
while (!(IFG2 & UCB0TXIFG));      // Warte bis TXBUF bereit
UCB0TXBUF = (dac >> 8);               // Bit 15..8 senden
while (!(IFG2 & UCB0RXIFG));
dummy = UCB0RXBUF;
while (!(IFG2 & UCB0TXIFG));      // Warte bis TXBUF bereit
UCB0TXBUF = (dac);               // Bit 7..0 senden
while (!(IFG2 & UCB0RXIFG));
dummy = UCB0RXBUF;
P3OUT |=  0x04;                   // 16-Bit Übertragung fertig


Autor: Albert K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, er schneidet die oberen 8 Bit einfach ab, das war wohl ein 
überlegungsfehler von mir.
Jetzt mit einer buffervariable funktioniert es.

Aber das andere Problem habe ich noch immer:

Hier mal der aktuelle Code:
/* init */
  INT16U dac_buffer;
  UCB0CTL0 |= UCCKPH + UCMSB + UCMST + UCSYNC;       // 3-pin, 8-bit SPI master, MSB 1st
  UCB0CTL1 |= UCSSEL_2;               // SMCLK
  UCB0BR0 = 0x02;
  UCB0BR1 = 0;
  IE2 = UCB0TXIE + UCB0RXIE;                     // Übertragungsinterrupt erlauben
  UCB0CTL1 &= ~UCSWRST;               // USCIB0 starten

/* code */
     P3OUT &= ~0x04;                   // DAC /CS enable
     while (!(IFG2 & UCB0TXIFG));      // Warte bis TXBUF bereit
     dac_buffer = dac & 0xFF00;        // Bit 15..8 senden
     UCB0TXBUF = dac_buffer >> 8;
     while (!(IFG2 & UCB0TXIFG));      // Warte bis TXBUF bereit
     UCB0TXBUF = dac & 0x00FF;         // Bit 7..0 senden
     while (!(IFG2 & UCB0RXIFG));      // Warte bis TXBUF bereit
     P3OUT |=  0x04;                   // 16-Bit Übertragung fertig

Braucht es die erste while überhaupt gleich nach dem /CS enable 
schalten?

Also die DA-Werte bei Step by Step Modus:
1.53V und 3.03V

Im normalen Modus:
mit RX Flagabfrage am Schluss:
2.78V und 3.03V

mit TX Flagabfrage am Schluss, anstatt dem RX-Flag (da ich ja nur sende 
könnte er ja das empfangen auch abbrechen):
2.30V und 3.03V

Ist es so nicht richtig mit den RX/TX Flags?

Autor: Albert K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@christian
warum muss ich den RXBUF in eine dummy variable speichern?

Im user guide steht ja zum TXFlag:
UCB0TXIFG is set when UCB0TXBUF is empty.

Reicht es da nicht einfach dieses abzufragen?

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
UCB0TXBUF is empty
heißt nicht, dass die Datenübertragung beendet ist, sondern nur dass 
der TXBuffer in das interne (nicht user-zugängliche) Schieberegister 
übertragen wurde!

Wo sind eigentlich Deine ISR-Routinen für RX/TX-IRQ???

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das hat Christian doch sehr verständlich beschrieben:

> Wenn der TX-Buffer bereit ist [UCB0TXIFG also gesetzt ist],
> heißt das nicht, dass das Byte schon rausgeschoben ist.
> Das TX-Register ist doppelt gepuffert. Du musst warten,
> bis das RX-Flag im IFG2 gesetzt wird, erst dann ist das
> Byte raus.
> SPI sendet und empfängt ja gleichzeitig, also musst du warten
> bis der Empfang komplett ist.
> Dann musst du noch das Flag manuell zurücksetzen,
> oder den RX-Buffer in eine Dummy-Variable auslesen, da[durch]
> wird es zurück gesetzt.

(Anmerkungen und Hervorhebungen von mir)

Autor: Albert K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also so wie christian es gesagt hat funktioniert es jetzt.
Aber wozu brauch ich den RXBUF immer in eine dummy variable zu 
speichern?

Brauche ich wirklich alle diese while schleifen/abfragen?

@stefan
ISR zu RX/TX habe ich keine. Was hätte ich dadurch konkret für vorteile?

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>ISR zu RX/TX habe ich keine. Was hätte ich dadurch konkret für vorteile?
Hängt von Dir ab ;-)
Du hast aber beide IRQs aktiviert:
IE2 = UCB0TXIE + UCB0RXIE; 
Ohne dazugehörige ISR kann Dein Programm durchaus abschmieren (bei Dir 
hat Dich Dein Compiler offensichlich gerettet, indem er die 
IRQ-Vektortabelle mit reti's gefüllt hat)

Autor: Christian R. (supachris)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Albert K. wrote:
> Also so wie christian es gesagt hat funktioniert es jetzt.
> Aber wozu brauch ich den RXBUF immer in eine dummy variable zu
> speichern?

Um das RX-Flag zu löschen. Kann man natürlich auch selber im IFG2 
löschen.

> Brauche ich wirklich alle diese while schleifen/abfragen?

Nö, natürlich nicht, aber dann funktioniert dein Programm nicht, wie du 
ja schon bemerkt hast. Du kannst die ganze Kiste natürlich auf Interrupt 
umbauen, macht aber nur Sinn, wenn die SPI viel langsamer getaktet ist 
als der Prozessor, und du zwischen dem Senden der Bytes noch andere 
Dinge erledigen willst.

Achja, deaktivier mal die Interrupts. Sonst kann der µC wie schon 
geschrieben abschmieren. Nicht jeder Kompiler ist so gnädig.

Autor: Albert K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für eure Hilfe.
Ist das erste Mal seit einem Jahr, dass ich wieder was programmiere und 
mit SPI hab ich auch noch nichts gemacht, darum kann es teilweise etwas 
länger gehen bis ich etwas verstanden habe, aber jetzt habe ich es 
glaube ich kappiert.

Mein neuer code:
/* init */
  UCB0CTL0 |= UCCKPH + UCMSB + UCMST + UCSYNC;       // 3-pin, 8-bit SPI master, MSB 1st
  UCB0CTL1 |= UCSSEL_2;               // SMCLK
  UCB0BR0 = 0x02;
  UCB0BR1 = 0;
  UCB0CTL1 &= ~UCSWRST;               // USCIB0 starten

/* code */
     P3OUT &= ~0x04;                   // DAC /CS enable
//     while (!(IFG2 & UCB0TXIFG));      // Warte bis TXBUF bereit
     UCB0TXBUF = dac >> 8;             // Bit 15..8 senden
     while (!(IFG2 & UCB0RXIFG));      // Warte bis gesendet
     IFG2 &= ~UCB0RXIFG;
//     while (!(IFG2 & UCB0TXIFG));      // Warte bis TXBUF bereit
     UCB0TXBUF = dac;                  // Bit 7..0 senden
     while (!(IFG2 & UCB0RXIFG));      // Warte bis gesendet
     IFG2 &= ~UCB0RXIFG;
     P3OUT |=  0x04;                   // 16-Bit Übertragung fertig

Die auskommentierten schleifen braucht es doch eigentlich nicht oder? 
Funktionieren tuts ohne, weil wenn man ja das RXFlag abfragt, braucht 
man das TXFlag ja nicht auch noch, da das TXFlag ja zuerst gesetzt wird 
oder?

Der Taktprescaler ist auf 2x256=512 gestellt. Stimmt das?
Das habe ich aus dem TI Sample übernommen.

Also das ganze Senden des 16-Bit Wertes geht ja nicht allzulange denke 
ich mal. Da sollte es hoffe ich reichen, wenn ich die anderen Dinge erst 
mache wenn das mit dem DAC abgeschlossen ist.

Autor: Christian R. (supachris)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nein, dein SPI Takt läuft mit dem halben SMCLK. Wenn du :512 haben 
willst, musst du das UCB0BR1 auf 2 setzen. Macht aber keinen Sinn, denn 
der LTC2641 kann maximal 50MHz SPI Clock, du kannst also so schnell 
machen, wie der MSP430 kann. Meines Wissens können die neuen MSPs den 
Takt auch ungeteilt benutzen, also :1, dann hast du den vollen SMCLK als 
SPI Takt. Ist ja sinnvoll. Und die Schleifen zum TX-Busy abfragen kann 
man im konkreten Fall sicherlich weglassen, aber die 2 Takte kann man 
auch sicherheitshalber reinmachen.

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Aber wozu brauch ich den RXBUF immer in eine dummy variable zu
> speichern?

Nun, in eine Variable musst Du den nicht speichern, aber einen 
Lesezugriff auf RXBUF musst Du durchführen. Und das geht eben am 
simpelsten mit einer Variablenzuweisung.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.