PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] ATmega > DAC (SPI Problem)



rrobert
20.08.2011, 23:42
Seid gegrüßt,

im Rahmen eines Projektes beschäftige ich mich derzeit mit der
Ansteuerung eines AD5624 (http://www.analog.com/static/imported-files/data_sheets/AD5624_5664.pdf)R mit Hilfe eines ATmega168 (http://www.atmel.com/dyn/resources/prod_documents/doc2545.pdf) über die SPI
Schnittstelle.

An sich soll das Ganze ja sehr einfach gehen mit SPI, jedoch beiße ich
mir derzeit einwenig die Zähne daran aus.

Die Signale auf den SPI bekomme ich schon (mittels Oszi überprüft), aber
eine Reaktion seitens des DAC bleibt leider aus.

Die beiden Bilder zeigen das Oszibild für eine SPI Übertragung. Einmal
CLK+Data und einmal SS+Data.

Hier nun vllt die Codeschnipsel, die jeden interessieren sollten:


void PORTS_init()
{
DDRB |= (1<<PB2)|(1<<PB3)|(1<<PB5); // LED , SPI::MOSI/CLK
DDRC |= (1<<PC1)|(1<<PC2)|(1<<PC3)|(1<<PC4); // SPI::SS1...4 outputs
PORTC |= (1<<PC1)|(1<<PC2)|(1<<PC3)|(1<<PC4); // all high
}

void SPI_MasterInit(void)
{
SPCR |= (1<<SPE) | (1<<MSTR) | (1<<SPR1) | (1<<SPR0) | (0<<CPOL) | (0<<DORD) | (1<<CPHA);
}

void SPI_Master_to_DAC_send(uint8_t mode, uint8_t dac, uint16_t wert)
{
uint8_t byte0 = 0x0;
uint8_t byte1 = 0x0;
uint8_t byte2 = 0x0;

switch( mode )
{
case 0: byte0 |= 0x0; break; // write to input register n
case 1: byte0 |= 0x8; break; // update dac register n
case 2: byte0 |= 0x10; break; // write to input register n, update all
case 3: byte0 |= 0x18; break; // write to and update dac channel n
}
switch( dac )
{
case 0: byte0 |= 0x0; break; // DAC A
case 1: byte0 |= 0x1; break; // DAC B
case 2: byte0 |= 0x2; break; // DAC C
case 3: byte0 |= 0x3; break; // DAC D
case 4: byte0 |= 0x7; break; // ALL DAC
}

byte1 = (wert>>4);
byte2 = (wert<<4);

PORTC &= ~(1<<PC4);
SPI_MasterSend( byte0, byte1, byte2 );
_delay_us(5);
PORTC |= (1<<PC4);
}

void SPI_MasterSend(char cData0, char cData1, char cData2)
{
cli();
SPDR = (uint8_t)cData0;
while(!(SPSR & (1<<SPIF)));
SPDR = (uint8_t)cData1;
while(!(SPSR & (1<<SPIF)));
SPDR = (uint8_t)cData2;
while(!(SPSR & (1<<SPIF)));
sei();
}

Initialisieren tu ich den DAC mit einem kompletten Reset, einschalten
der internen Referenz sowie setzen des LDAC für alle Outputs auf 1.

Sende ich nun ein SPI_Master_to_DAC_send() mit einem Wert, so passiert
nichts. Habe nun schon alle Modi durchprobiert.

Kann mir jemanden eine Hilfestellung liefern?

Beste Grüße,
Robert

wkrug
21.08.2011, 08:59
Hast Du schon mal nachgeguckt ob Deine Clock Polarity Clock Phase und Data Order einstellungen für den AD5624 Chip passen?
Hast Du in deiner Routine das /SS Signal per Software! bedient ( Bei der Einstellung als Master muss das per Software erledigt werden ) .
Wenn ich probleme mit der SPI hab, dann schreib ich mal schnell de software SPI routine - ist ja nicht besonders schwierig.
Wenn Die dann geht, dann weiß ich zumindest, das es von der Hardware her passt und der Fehler nur noch bei den Einstellwerten für die SPI liegen kann.
Dann kann man mit den Parametern der SPI noch mal rumspielen und kriegt es dann meistens auch hin.
Manche Chips lassen sich überhaupt nicht per Hardware SPI ansprechen, z.B. wenn Sie eine vom 8 Bit Raster ( also 8, 16, 24, 32Bit ) abweichende Bitbreite am SPI Bus haben ( ja es gibt so was! ).
Wenn man mal die richtigen einstellungen für die SPI gefunden hat, funktioniert es in der Regel problemlos.

rrobert
21.08.2011, 09:03
Ich bin mittlerweile der Meinung so ziemlich jede Einstellung ausprobiert zu haben, jedoch ohne Erfolg.

Welche SPI Einstellung würdest du Anhand des Datasheet des AD5624 (http://www.analog.com/static/imported-files/data_sheets/AD5624_5664.pdf) denn wählen? (Das Datasheet habe ich der Modellbezeichnung angehängt (einfach auf den Link klicken)

Und zum /SS. Den habe ich standardmäßig als Output definiert und dann zusätzlich vor jeder Übertragung noch auf high gesetzt (was laut Datasheet des ATmega168 aber nicht nötig sein soll, da es keine Auswirkung haben soll, wenn der SS/ als Output definiert ist). An dem SS/ Pin des ATmega168 ist bei meinem Crumb128 auch eine LED angeschlossen, die leuchtet, wenn in den Pin auf 0 setze,

BASTIUniversal
21.08.2011, 10:22
Hallo rrobert,

im Prinzip schaut die Ansteuerung des "SYNC" Pins gut aus. Er muss auf Low gezogen werden, bevor die Ausgabe an "SCLK" beginnt und dann erst nach dem letzten Clock-Impuls wieder auf High gebracht werden.
Es kann sein, dass die Zeit zwischen dem Wechsel an SYNC und dem ersten Clock-Impuls zu kurz ist. Wobei die min. Zeit mit 13ns angegeben ist und der Fall daher unwahrscheinlich.

Ich mache bei der SPI-Kommunikation immer erst einen Versuch über eine Software-SPI bevor ich auf Hardware-SPI umsteige. Das hat den Vorteil, dass du alle Pins so wackeln kannst, wie im Datenblatt vorgegeben. Die Verwirrung mit CPOL usw. kann man so erstmal umgehen und weiß, dass der Chip und Kommunikation funktioniert.

Gruß
Basti

rrobert
21.08.2011, 10:27
Wie genau erziele ich mit dem Software SPI eine Vorteil gegenüber dem Hardware SPI? Hast du vllt einen Link zu einem Software SPI?

Zu den Timings. Der ganze uC läuft bei 8MHz und die SPI Clock Rate ist bei fck/128. Also wirklich das Langsamste, was irgendwie möglich ist (hatte die Überlegung, dass so vllt die wenigsten Probleme auftreten).

021aet04
21.08.2011, 10:32
Wie sieht die Schaltung aus? Hast du die Referenz angeschlossen bzw kannst du da eine Spannug messen? Eventuell hast du keine Verbindung (vergessen bzw kalte Lötstelle,...)

MfG Hannes

rrobert
21.08.2011, 10:36
Ich habe keine Referenz angeschlossen. Habe diese jedoch in der Initialisierung auf "Intern ON" gestellt und kann sie messen (2,48V).

rrobert
21.08.2011, 10:55
Wie schaut es mit der Übertragung der einzelnen Byte des 24bit Wortes aus? Wie verhält sich so ein Shiftregister da? Ich sende ja das erste Byte, dann das Zweite, dann das Dritte. Jeweils mit dem MSB "links".

Derzeit versende ich ja das Paket mit dem MSB first und das Byte_high zuerst. Das könnte mit dem Datenblattinfos kollidieren, oder?

021aet04
21.08.2011, 11:12
Du hast zwar das falsche DB vom DAC gepostet, habe aber das richtige schon gefunden. Du hast das DB vom AD5624 nicht vom 5624R gepostet.

Ich habe im DB vom Atmega geschaut. Laut diesem DB steht das CPHA Bit für die Phase der Clockimpulse (Rising oder Falling Edge). Du hast dieses Bit auf 1 gestellt, laut DB ist das für rising edge. Beim DAC steht aber drinnen das die Daten bei falling edge übernommen werden. Versuche einmal in der Init Routine das CPHA auf 0 zu schalten (bzw lassen).

Hier ist das richtige DB vom DAC http://www.analog.com/static/imported-files/data_sheets/AD5624R_5644R_5664R.pdf

MfG Hannes

rrobert
21.08.2011, 11:36
Okay. Das habe ich nun versucht.

Mein SPI schaut wie folgt aus:


void SPI_MasterInit(void) {
// Set /SS, MOSI and SCK output, all others input
DDRB = (1<<PB2)|(1<<PB3)|(1<<PB5); // SS, MOSI, CLK are Outputs
//PORTB &= ~(1<<PB2); // LED on (PB2 low)

DDRC = (1<<PC1)|(1<<PC2)|(1<<PC3)|(1<<PC4); // SPI::SS1...4 outputs
PORTC = (1<<PC1)|(1<<PC2)|(1<<PC3)|(1<<PC4); // all high
// Enable SPI, Master, MSB first, SPI Mode 1, set clock rate fck/128
SPCR |= (1<<SPE) | SPI_MSB_1 | (1<<MSTR) | SPI_MODE_0 | SPI_SPEED_128;
}

void SPI_MasterSend(char high, char mid, char low) {

PORTB |= (1<<PB2); // SS is set high, effects that LED is off
cli();
_delay_us(5);
SPDR = high;
while(!(SPSR & (1<<SPIF)));
SPDR = mid;
while(!(SPSR & (1<<SPIF)));
SPDR = low;
while(!(SPSR & (1<<SPIF)));
_delay_us(5);
sei();
PORTB &= ~(1<<PB2); // SS is set low, effects that LED is on
}

void SPI_Master_to_DAC_send(uint8_t mode, uint8_t dac, uint16_t wert) {

uint8_t byte_high = 0x0;
uint8_t byte_mid = 0x0;
uint8_t byte_low = 0x0;
char dac_buf[128]; // Buffer für Konsolenausgabe-String

switch( mode )
{
case 0: byte_high |= 0x0; break; // write to input register n
case 1: byte_high |= 0x8; break; // update dac register n
case 2: byte_high |= 0x10; break; // write to input register n, update all
case 3: byte_high |= 0x18; break; // write to and update dac channel n
}
switch( dac )
{
case 0: byte_high |= DAC_A; break;
case 1: byte_high |= DAC_B; break;
case 2: byte_high |= DAC_C; break;
case 3: byte_high |= DAC_D; break;
case 4: byte_high |= DAC_ALL; break;
}

byte_mid = (wert>>4);
byte_low = (wert<<4);

PORTC &= ~(1<<PC1);
SPI_MasterSend( byte_high, byte_mid, byte_low );
PORTC |= (1<<PC1);
sprintf( dac_buf, "## SYSTEM ## DAC Data send (%d)(%d[%d][%d])\n\r", byte_high,wert,byte_mid,byte_low);
UART_puts(dac_buf);
}

#define SPI_MODE_0 ( (0<<CPOL) | (0<<CPHA) )
#define SPI_MODE_1 ( (0<<CPOL) | (1<<CPHA) )
#define SPI_MODE_2 ( (1<<CPOL) | (0<<CPHA) )
#define SPI_MODE_3 ( (1<<CPOL) | (1<<CPHA) )
#define SPI_MSB_1 ( (0<<DORD) )
#define SPI_LSB_1 ( (1<<DORD) )
#define SPI_SPEED_64 ( (1<<SPR1) | (0<<SPR0) )
#define SPI_SPEED_128 ( (1<<SPR1) | (1<<SPR0) )

021aet04
21.08.2011, 11:39
Funktioniert es?

MfG Hannes

rrobert
21.08.2011, 11:46
Nein. Es funktioniert so leider nicht.

Ist die Bytereihenfolge korrekt? Ich sende ja nun High, Mid, Low jeweils mit MSB zuerst. Auf dem Oszi schaut es so ja auch gut aus. Aber ist es das, was der Shiftregister erwartet? Habe eben mal Low, Mid, High mit LSB versucht, mit gleichem Ergebnis … =(

BASTIUniversal
21.08.2011, 12:05
Vielleicht doch erst mal mit Software-SPI probieren. Das funktioniert ganz einfach mit einer For-Next Schleife:

Reset SYNC
Pause

For i = 23 to 0 Step -1
Set SCLK
MOSI-Pin = Datenword.i
Pause
Reset SCLK
Pause
Next i

Set SYNC
Set SCLK


Das ganze dann halt in C umgeschrieben.

rrobert
21.08.2011, 12:11
Diese Sprache habe ich ja noch nie gesehen. Und sie stellt mich vor 2 Schwierigkeiten: Wie setze in den SCLK und wie kann ich ein 24bit Wort erstellen, das ich bitweise durchlaufen kann? Und kann ich diesen SoftwareSPI auch auf die HardwareSPI Pins legen?

021aet04
21.08.2011, 12:12
Die interne Referenz des DAC musst du einschalten, da sie Standartmäßig auf 0 (= Aus) geschaltet ist. Das bedeutet du musst entweder eine ext. Referenz anschließen oder die interne einschalten. Ich sehe nirgends die Commandos für das einschallten der int. Referenz.
Sende einmal 0x38, 0x00, 0x01

MfG Hannes

rrobert
21.08.2011, 12:22
Sry Hannes, hatte ich nicht gepostet.


#define AD5624_REF_ON 1

cli();
UART_init();
UART_puts("## INIT ## UART initialisiert\n\r");
//PWM_init();
//UART_puts("## INIT ## PWM initialisiert\n\r");
SPI_MasterInit();
UART_puts("## INIT ## SPI Master initialisiert\n\r");

AD5624_reset(1);
_delay_ms(200);
AD5624_ref_setup(AD5624_REF_ON);
_delay_ms(200);
AD5624_LDAC(4);
_delay_ms(200);
AD5624_power_down(0,4);
_delay_ms(400);
UART_puts("## INIT ## DAC powered normal mode (0)(all DAC)\n\r");

UART_puts("## INIT ## Initialisierung abgeschlossen\n\r");
sei();
UART_puts("## INIT ## Interrupts eingeschaltet\n\n\r");

void AD5624_ref_setup( uint8_t state )
{
if ( state == 0 ) // internal reference off
{
PORTC &= ~(1<<PC4);
SPI_MasterSend(0x38, 0x0, 0x0);
PORTC |= (1<<PC4);
UART_puts("## INIT ## DAC internal ref off\n\r");
}
else if ( state == 1 ) // internal reference on
{
PORTC &= ~(1<<PC4);
SPI_MasterSend(0x38, 0x0, 0x1);
PORTC |= (1<<PC4);
UART_puts("## INIT ## DAC internal ref on\n\r");
}
else {/* Fehlerausgabe an Host */}
}

Dort habe ich das Senden des ( 0x38, 0x0, 0x1 ) realisiert.

021aet04
21.08.2011, 12:33
Der Post von Bastiuniversal ist gepostet worden als ich geschrieben habe. Deswegen eine Erklährung erst jetzt. Diese Sprache wie Bastiuniversal geschrieben hat gibt es nicht. Dises Struktur soll nur verdeutlichen wie das Programm bzw der Ablauf aussieht.

Du kannst die Software SPI an jeden Pin legen den du willst, also auch auf die Hardware SPI Pins. Du musst nur die Hardware SPI ausschalten.

MfG Hannes

rrobert
21.08.2011, 12:44
Ah okay. Also Pseudo-Code. Danke für die Aufklärung. Hab mich schon gefragt, ob ich mich vllt für die falsche Sprache entschieden habe =)

rrobert
21.08.2011, 13:18
So Leute. Ich bin das Ganze noch mal von Grundauf angegangen und habe auch die Hardware nochmal überprüft. Es war ein Tricky Hardwarefehler den ich mir da selber eingebrockt habe. Da die beiden Teile uC und DAC auf verschiedenen Boards platziert sind war ein Verbinder zwischen diesen und auf dem Board des DAC war der MOSI mit dem MISO vertauscht. Somit konnte nichts ankommen.

Somit ist das Ganze gelöst und der Code funktioniert! Ich hoffe der Code hilft vllt anderen Leuten weiter, die sich mit dem Thema beschäftigen.

Gesendet wird nun mit folgenden Einstellungen:

MSB first, SPI Mode 0, Speed CLK/128 in der Reihenfolge High, Mid, Low

Vielen vielen Dank für eure Hilfe!

021aet04
21.08.2011, 13:37
Sehr schön wenn es jetzt funktioniert.

MfG Hannes

rrobert
21.08.2011, 13:48
Sehr schön wenn es jetzt funktioniert.
Du glaubst gar nicht, wie groß der Stein ist, der mir gerade vom Herzen fiel .. chakka!

Ceos
22.08.2011, 10:52
hehe hatte ein ähnliches Problem, bei mir war aber tatsächlich der Chip defekt ... was hab ich da stundenlang rumprobiert und sogar den Service genervt, weil das Datenblatt schrott war! Am Ende haben sie sogar ein neues exakters Datenblatt rausgebracht in dem die Clockphase und Bitreihenfolge nicht nur anhand von überfrachteten Bildern veranschaulicht wird!

rrobert
22.08.2011, 10:55
Na geteiltes Leid ist halbes Leid. Irgendwie gehts doch allen irgendwie gleich .. Trail & Error ..

Beste Grüße,
Robert