PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Probleme mit SPI-Schnittstelle



Sven_77
22.02.2012, 15:53
Hi und hallo,

ich versuche mich gerade daran einen Sensor per SPI anzusprechen. Leider funktioniert es bis jetzt noch
nicht.

Der Chip-Select Pin funktioniert. Das habe ich messen können.

Das Ganze läuft zwar durch, aber es steht hinterher nur Müll in der Variable "daten". Ich habe das Gefühl, dass die Kommunikation erst gar nicht anspringt.
Ich habe es zwar nicht richtig messen können, aber anscheinend läuft die Clock des SPI nicht an.

Wäre klasse, wenn mir da jemand weiter helfen könnte. Vielleicht gibt es ja schon einen fehler bei der Initialisierung des SPI.

Vielen Dank schon einmal!



Hier einmal die Teile meines Codes:




#define SPI_PIN PINB
#define SPI_DDR DDRB
#define SPI_PORT PORTB

#define MISO 3
#define MOSI 2
#define Clock 1
#define SS 0

CS_PIN PINE

void SPI(void)
{
SPI_PORT_DDR &=~(1<<MISO); // miso auf input
SPI_PORT_DDR |= (1<<Clock); // clock auf output
SPI_PORT_DDR |= (1<<MOSI); // mosi auf output
SPI_PORT_DDR |= (1<<SS); // wird nicht gebraucht, ss auf output
CS_PIN |= (1<<PE2); // chip selet auf high
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0)|(1<<SPR1);
}


Die Kommunikation mit dem Sensor soll dann wie folgt laufen:



#include <avr\io.h>
#include <stdlib.h>
#include <avr/interrupt.h>


int main(void)
{
cli();
Ports();
SPI();
sei();

CS_PIN &= ~(1<<CS_TEMP); // CS auf LOW
_delay_us(10); // kurz warten
SPDR= 0x10; // Daten senden
while(!(SPSR & (1<<SPIF)));
daten= (SPDR<<8) & 0x3FFF; // Ersten 8 Bit als MSBs ablegen
SPDR= 0x01;
while(!(SPSR & (1<<SPIF)));
daten+= SPDR; // Letzten 8 Bit dazu addieren
CS_PIN |= (1<<PE2); // cs High

while(1)
{
.....
}
}/* main */

markusj
22.02.2012, 18:19
Testen kannst du die SPI-Kommunikation indem du MISO mit MOSI direkt verbindest. Stimmt der SPI-Modus (Polarität + Phase)?

mfG
Markus

Sven_77
24.02.2012, 17:33
Hallo,



Stimmt der SPI-Modus (Polarität + Phase)?

Ich denke doch. ich habe die SPI INitialisierung mal ein wenig überarbeitet:


void initSPI(void) // Initialisierung der SPI Schnittstelle
{
SPI_PORT_DDR &=~(1<<SPI_MISO); // miso auf input
SPI_PORT_DDR |= (1<<SPI_Clock); // clock auf output
SPI_PORT_DDR |= (1<<SPI_MOSI); // mosi auf output
SPI_PORT_DDR |= (1<<SPI_SS);
Disable();


SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPI2X)|(1<<SPR0)|(1<<SPR1)|(1<<CPOL)|(1<<CPHA); // SPI Enable
// Master Select
// f_clkio/64
// Leading Edge -> Falling
// Trailing Edge -> Rising


Mein Aufruf sieht nun so aus:



volatile uint16_t data= 0;

Enable();
_delay_us(10);

SPDR= 0x10;
while(!(SPSR & (1<<SPIF)));

data= (SPDR<<8) & 0x3FFF;

SPDR= 0x10;
while(!(SPSR & (1<<SPIF)));

data+= SPDR;

_delay_us(10);
Disable();


Was ich sagen kann, ist, dass das CS funktioniert. Geht am Anfang auf High, zum Auslesen wird es dann eben LOW gesetzt und danach wieder High. Anscheinen läuft die Clock nicht.
Der Code läuft aber so durch. Danach habe ich einfach noch Kontrollausgaben per LED auf meinem Board. Daher sehe ich, dass der komplette Code durchlaufen wird.
In der Variable data steht aber am Ende nur eine Null drin, wird der Variable am Anfang ja auch zugewiesen. Also ändert sich da nichts, d.h. der Sensor sendet keine Daten. Wie oben schon geschreiben, anscheinend läuft die Clock nicht, somit kann ja auch nichts empfangen werden.

Wie es scheint, läuft die Clock auch nicht, wenn ich MISO und MOSI direkt verbinde.

Hat jemand noch eine Idee, woran es liegen könnte?

sternst
25.02.2012, 01:02
Was ich sagen kann, ist, dass das CS funktioniert. Geht am Anfang auf High, zum Auslesen wird es dann eben LOW gesetzt und danach wieder High. Anscheinen läuft die Clock nicht. Ich habe da große Zweifel.
Aber wirklich eindeutig kann ich das nicht sagen, denn das, was du in deinem ersten Post an Code gepostet hast, ist schon einigermaßen ärgerlich. Das sind offenbar nicht sonderlich sorgfältig zusammen kopierte Fragmente. Anscheinend gehören diese Fragmente noch nicht mal zum selben Code, denn es gibt die Defines "SPI_DDR" und "SPI_PORT", benutzt wird dann aber "SPI_PORT_DDR".

CS_PIN PINEWas ist das für eine Zeile? [1]



CS_PIN &= ~(1<<CS_TEMP); // CS auf LOW
...
CS_PIN |= (1<<PE2); // cs High
Wieso wird der CS-Pin einmal als "PE2" angesprochen, und einmal als "CS_TEMP"? Was ist "CS_TEMP"?


Jedenfalls wenn ich die Ungereimtheiten per Raten interpoliere ([1] um "#define" ergänzt, und CS_TEMP=PE2), dann funktioniert dein CS sicher nicht.

Sven_77
25.02.2012, 11:09
Hallo,

dass der Code ein wenig durcheinander ist, liegt nicht daran, dass es wild zusammenkopiert ist, sondern daran, dass ich das eine Menge rumprobiert habe und dabei die Ordnung habe schleifen lassen.

Ich habe das Ganze einmal aufgeräumt, siehe unten.

Das CS funktioniert sicher. erst ist der Pin HIGH, dann setze ich ihn auf LOW und dann wieder auf HIGH. Das habe ich mir mit dem Oszi angeschaut.
Ich habe einfach mal den SPI-Kram auskommentiert, ein delay zwischen Sensor-aktivieren und Sensor-deaktivieren eingefügt, CS am Oszi angeschaut und das springt eben wie gewünscht HIGH - LOW - HIGH.

Bei der Clock sehe ich aber leider nichts.

Ich habe das Gefühl, dass ich vor lauter Bäumen den Wald nicht mehr sehe.
SPI - Init:


#define SPI_PIN PINB
#define SPI_DDR DDRB
#define SPI_PORT PORTB

#define SPI_MISO PB3
#define SPI_MOSI PB2
#define SPI_Clock PB1
#define SPI_SS PB0

SPI_DDR &=~(1<<SPI_MISO); // miso auf input
SPI_DDR |= (1<<SPI_Clock); // clock auf output
SPI_DDR |= (1<<SPI_MOSI); // mosi auf output
SPI_DDR |= (1<<SPI_SS); // ss auf output
PORTE |= (1<<PE2); // Sensor deaktivieren

SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPI2X)|(1<<SPR0)|(1<<SPR1)|(1<<CPOL)|(1<<CPHA);
// SPI Enable
// Master Select
// f_clkio/64
// Leading Edge -> Falling
// Trailing Edge -> Rising


Code aus der main:


PORTE &= ~(1<<PE2); // Sensor aktivieren, CS -> LOW
_delay_us(10); // wait
SPDR= 0x10;
while(!(SPSR & (1<<SPIF)));
data= (SPDR<<8) & 0x3FFF; // 8 Bit auslesen


SPDR= 0x01;
while(!(SPSR & (1<<SPIF)));
data+= SPDR; // Weitere 8 Bit auslesen
_delay_us(10); // wait
PORTE |= (1<<PE2); // Sensor deaktivieren

021aet04
25.02.2012, 11:13
Was ist das für ein µC?

MfG Hannes

Sven_77
25.02.2012, 11:46
Hallo,

das ist ein AT90CAN128. Der Sensor ist ein Temperatursensor ADT 7301.

021aet04
25.02.2012, 12:17
SPI2X befindet sich im Register SPSR (DORD wäre noch im SPCR Register).

Versuche es einmal zu ändern.

MfG Hannes

Sven_77
25.02.2012, 12:56
Hallo,

das mit dem SPI2X ist mir voll durchgegangen. Ich habe es jetzt mal rausgenommen und lasse das Ganze einfach auf f_clkio/64 laufen nur mit SPR1 gesetzt.

Leider bringt das noch nicht den erwünschten Erfolg. Auch die Einstellung DORD bringt erst einmal scheinbar keine sichtbare Änderung.

Ich habe so langsam das Gefühl, dass es irgendwie an dem Sensor liegt oder eben am Zusammenspiel. Wenn ich den Sensor abnehme und das Programm laufen lasse, habe ich in "data" immer eine "Null" stehen. Wenn ich ihn anschließe, scheinen Daten empfangen zu werden, allerdings nur Müll wie es scheint.

021aet04
25.02.2012, 14:38
Laut Datenblatt muss das 3te Bit eine 1 sein (Shutdown Bit). Die restlichen 15Bit müssen 0 sein. Bei einem 8Bit Register (wie bei deinem µC) musst du einmal 0x20 senden und dann 0x00.

MfG Hannes

Sven_77
25.02.2012, 16:41
Hallo,

das dient ja nur zum schlafen legen. Ich will den Sensor ja nicht schlafen legen. Ich sende jetzt einfach 0x00. Da nur das 3. Bit wichtig ist, sollen die anderen ja null sein. Das 3. Bit ist dann eben auch null, also der Sensor wird nicht schlafen gelegt.

Ich bin irgendwie soweit, dass ich immer die gleichen Daten empfange, wobei ich allerdings das Gefühl habe, dass der Datenmüll weniger vom Sensor kommt, als mehr durch meine Speicherung und Verarbeitung der Daten.




uint16_t data= 0;
unsigned char str[8];

/* Daten auslesen */

PORTE &= ~(1<<PE2); // Sensor aktivieren, CS -> LOW
_delay_us(10); // wait
SPDR= 0x00;
while(!(SPSR & (1<<SPIF)));
data= (SPDR<<8) & 0x3FFF; // 8 Bit auslesen
SPDR= 0x00;
while(!(SPSR & (1<<SPIF)));
data+= SPDR; // Weitere 8 Bit auslesen
_delay_us(10); // wait
PORTE |= (1<<PE2); // Sensor deaktivieren


// Daten umrechnen, Formeln aus Datenblatt
if ((0x2000 & temp_data) == 0x2000)
{
data = (int)(data - 16384)/32;
}
else
{
data = (int)(data/32);
}

utoa(data,(char*)str,10);
send(str);

021aet04
25.02.2012, 16:54
Ich würde einmal versuchen 2 einzelne 8Bit variablen zu verwenden. Wenn das funktioniert kann man weiterschauen.
In etwa so:
....
data1=spdr
....
data2=spdr

Anschließend kann man die 2x 8bit in 16bit wandeln (wenn es so funktioniert).

PS: Mit dem Shutdownbit hast du recht. Habe ich falsch gelesen (zu schnell drübergeschaut).

MfG Hannes

Sven_77
28.02.2012, 14:41
Hallo,

an den einzelnen Variablen kann es nicht liegen.

Es werden immer die gleichen Daten ausgelesen, egal, welche Temperatur herscht. Das konnte ich mitlerweile mit einem kleinen LogicAnalyzer überprüfen. Die Clock läuft auch, CS tuts auch.
Anhand der Daten des LogicAnalyzers konnte ich dann eben auch sehen, dass die Daten, die dann rein kommen auch richtig umgerechnet werden. Es kommen aber eben keine sinnvollen Daten rein.

Ich würde sagen, dass es nur an den Einstellungen des SPI liegen kann, aber ich finde den Fehler einfach nicht.


Beste Grüße