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/216844/LINER/LTC2641.html
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?
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?
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.
>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!
>> 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.
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:
1
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):
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.
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:
while(!(IFG2&UCB0TXIFG));// Warte bis TXBUF bereit
13
dac_buffer=dac&0xFF00;// Bit 15..8 senden
14
UCB0TXBUF=dac_buffer>>8;
15
while(!(IFG2&UCB0TXIFG));// Warte bis TXBUF bereit
16
UCB0TXBUF=dac&0x00FF;// Bit 7..0 senden
17
while(!(IFG2&UCB0RXIFG));// Warte bis TXBUF bereit
18
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?
@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?
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???
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)
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?
>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:
1
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)
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.
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:
// while (!(IFG2 & UCB0TXIFG)); // Warte bis TXBUF bereit
11
UCB0TXBUF=dac>>8;// Bit 15..8 senden
12
while(!(IFG2&UCB0RXIFG));// Warte bis gesendet
13
IFG2&=~UCB0RXIFG;
14
// while (!(IFG2 & UCB0TXIFG)); // Warte bis TXBUF bereit
15
UCB0TXBUF=dac;// Bit 7..0 senden
16
while(!(IFG2&UCB0RXIFG));// Warte bis gesendet
17
IFG2&=~UCB0RXIFG;
18
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.
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.
> 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.