PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] Verständnisfrage zur SPI Kommunikation ..



Ritchie
30.03.2013, 08:09
Hallo Zusammen,

ich bin derzeitig meine Master <-> Slave Kommunikation zwischen zwei Atmega Controllern am prüfen/schreiben.

Wahrscheinlich habe ich noch ein Missverständnis der Protokollabfolge, daher hier meine Frage.

Ist folgende Abfolge korrekt:

- Der Master startet immer die Kommunikation
Hierzu legt er den CS des CHIPS auf LOW.

- Der Master sendet das erste Byte an den Slave

- Der Slave liesst das erste Byte ein und gibt
a) eine Dummy Antwort, wenn es nur ein Befehl war
b) das geforderte Byte zurück.

Am Ende der Kommunikation wird der CS wieder auf HIG gesetzt.

Generell muss der Slave immer antworten, auf jedes gesendete Byte des Masters.

Ich versuche auch einen ADIS16265 ans Leben zu bekommen, was mir bis jetzt noch nicht korrekt gelungen ist.

Der Logikanalyser zeigt mir keine korrekte Daten an.

Gruss R.

markusj
30.03.2013, 09:35
- Der Slave liesst das erste Byte ein und gibt
a) eine Dummy Antwort, wenn es nur ein Befehl war
b) das geforderte Byte zurück.

Nein. SPI arbeitet bidirektional, in dem Moment in dem der Master seine Daten an den Slave sendet, bekommt er gleichzeitig das erste Antwortbyte. Da der Slave aber (in der Regel, Ausnahmen sind entsprechend gestaltete Protokolle) das erste Kommandobyte nicht kennt, sendet er ein Dummy-Byte zurück. Die ersten echten Daten sind also erst im zweiten Byte vom Slave zu erwarten. Umgekehrt muss der Master üblicherweise nach dem Kommando weitere Dummy-Bytes nachschieben damit der Slave seine Antwort auch zurücksenden kann.

mfG
Markus

Ritchie
30.03.2013, 13:50
Hallo Markus,

das bedeutet aber dann, wenn ich Dich richtig verstehe, das ich für die Abfrage von mehreren Byte immer ein weiteres Byte senden muss.

Also wäre eine Abfrage für eine Info von einem Byte im folgenden Format:

1.Übertragung
------------------------------------------------------
Master Slave
Kommando (x1) ----------------->
<---------------Dummy Antwort Slave

2. Übertragung
-------------------------------------------------------
Master
Kommando + 1 ------------------> Slave
(oder auch Dummy)

Antwort von <--------------------- Slave
Kommando X1


Jetzt verstehe ich auch das Diagramm hier:
http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus

Es handelt sich im Prinzip um zwei Schieberegister, welche zeitgleich durch den CLK Impulse verschoben werden.

Gruss R.

markusj
30.03.2013, 14:30
Es handelt sich im Prinzip um zwei Schieberegister, welche zeitgleich durch den CLK Impulse verschoben werden.
Korrekt.

Es handelt sich übrigens um genau eine Übertragung, der Übertragungsbeginn ist durch CS High->Low gekennzeichnet, das Übertragungsende durch CS Low->High.

mfG
Markus

Ritchie
30.03.2013, 15:46
Hallo Markus,

heisst das, ich muss die CS Leitung für jedes Byte tooglen oder kann ich die gesamte Übertragung mit einem Low CS fahren, wenn ich mehre Bytes zu übertragen habe.

Bin derzeitig am Debuggen mit Logikanalyser..

Gruss R.

- - - Aktualisiert - - -

Hallo Markus,

hier die Startroutine, welche im Hauptprogramm liegt und die Interruptverarbeitung startet:




CLEAR_BIT(PORTB,OUTPUT_SELECT_SPI_GYRO); // Select the slave
// ReadGyroADIS16265(); // load Data for command
ReadAngleADIS16265();
Master_Start_Send_SPI( SPI_SLAVE_GYRO, GYRO_SPEED_CALCULATION );



Hier die eigentliche Startroutine, welche auch noch andere Steuerfunktionen übernimmt


// ================================================== ================
// Start the communication with the Slave
// ================================================== ================

void Master_Start_Send_SPI (int iChannel, int iBit )
{
m_Active_SPI_Channel=iChannel; // Select the channel
m_SPIByteToSend--; // Send the first byte out
SPDR=SPISendBuffer[0]; // Send it
m_ArrayReadPointer=0; // Set Pointer to first byte to receive
m_ArrayWritePointer=1; // Set Pointer to the next byte
m_DummyByteReceived=false; // Dummy byte not received
CLEAR_BIT( m_SPI_Status, iBit); // Clear the request
}



Hier die Interruptroutine


/ ================================================== ================
// SPI Interrupt for the received chars from Slaves
// ================================================== ================

SIGNAL (SPI_STC_vect)
{
unsigned char data=0; // Clear data bytes
data=SPDR; // get the data byte from the register

if(m_SPIByteToSend > 0)
{
SPDR=SPISendBuffer[m_ArrayWritePointer]; // Send the next byte out to slave
m_ArrayWritePointer++;
m_SPIByteToSend--; // to get the complete answer
}

if( m_SPIBytetoReceive > 0) // Do we wait still for answers
{
SPIReceiveBuffer[m_ArrayReadPointer]=data; // Store the byte received
if( m_DummyByteReceived == true ) // We have to send one more byte
{
m_SPIBytetoReceive--; // Still a byte received
m_ArrayReadPointer++; // Set pointer to the next position
}

if(m_SPIBytetoReceive > 0 && m_SPIByteToSend == 0 ) // Send of data finished
SPDR=0x0; // Send a dummy Byte to get the next bytes
}

if(m_SPIBytetoReceive == 0) // transmission block finished
{
SET_BIT(PORTB,OUTPUT_SELECT_SPI_GYRO); // De select the all slave
SET_BIT(PORTB,OUTPUT_SELECT_SPI_COMPASS);
SET_BIT(PORTB,OUTPUT_SELECT_SPI_ATMEGA8); // Select of which to select costs more CPU power, than all once
}

m_DummyByteReceived=true; // Dummy byte received
}





Im Anhang das Impulsdiagram der Schnittstelle.

Sieht meiner Meinung nach o.k. aus.

Wenn, nicht bitte komentar, sonst benutzt jemand anderes das hier und hat Probleme.

Ich teste noch weiter mit meinem Atmega8 Slave, den Code kann ich ja dann auch posten, wenn erst fertig ist.

Gruss R.

markusj
30.03.2013, 15:58
heisst das, ich muss die CS Leitung für jedes Byte tooglen oder kann ich die gesamte Übertragung mit einem Low CS fahren, wenn ich mehre Bytes zu übertragen habe.

Du musst CS während der ganzen Übertragung auf Low halten! Mit CS Low->High beendest du die Übertragung und setzt (üblicherweise) den Zustandsautomaten des Slaves zurück, danach beginnt die Kommunikation wieder ganz von vorne!

Der angehängte Plot sieht gut aus, der Code bei grobem Überfliegen auch.

mfG
Markus

Ritchie
30.03.2013, 18:44
Hallo Markus,

die erste Auswertung der übertragenen Werte sieht auch von meiner Seite gut aus.
Ich muss die Platine jetzt wieder mit dem Hauptrechner (linux cpu) verdrahten, da dieser eine bessere
Darstellung der Messwerte hat (Interface zum PC)

Hier wie versprochen der Quellcode des Slaves (ATMEGA8 auf Interrupt-Basis:

Init Master


// ================================================== ================
// Setup of the SPI Interface
// ================================================== ================

void InitSPIInterface(void)
{
SPCR = (1<<CPOL)| (1<<CPHA); // Signal Mode 3
SPCR |= (1<<SPE) | (1<<SPIE); //Activate the SPI Interrupt
SPSR =0; // Clear the Control Register
}


Init Slave


// ================================================== ================
// Setup of the SPI Interface
// ================================================== ================

void InitSPIInterface(void)
{
SPCR = (1<<CPOL)| (1<<CPHA); // Signal Mode 3
SPCR |= (1<<SPE) | (1<<SPIE); //Activate the SPI Interrupt
SPSR =0; // Clear the Control Register
}




// ================================================== ================
// SPI Interrupt handling
// ================================================== ================

SIGNAL (SIG_SPI)
{
char *pointer;
unsigned char data=0;

data=SPDR; // get the data byte from the register
if ( data != 0)
{
if (m_buffer_adr == NULL) // Is it the first byte to receive (setup buffer)
{
switch( data ) // get the command byte, which data area
{
case 'G': // Actual Angle by the Gyro module (Angle change per second)
m_buffer_adr = (char*) &DataBuffer;
m_NumberOfBytesToSend=10;
pointer=(char*) &m_FilteredGyro;
DataBuffer[0]= (char)*pointer++;
DataBuffer[1]= (char)*pointer++;
DataBuffer[2]= (char)*pointer++;
DataBuffer[3]= (char)*pointer;
pointer=(char*) &m_accelerometer_x;
DataBuffer[4]= (char) *pointer++;
DataBuffer[5]= (char) *pointer;
pointer=(char*) &m_accelerometer_y;
DataBuffer[6]= (char) *pointer++;
DataBuffer[7]= (char) *pointer;
pointer=(char*) &m_accelerometer_z;
DataBuffer[8]= (char) *pointer++;
DataBuffer[9]= (char) *pointer;
break;
case 'C':
m_buffer_adr = (char*) &m_OrientationFromCompass; // Actual Compass value of the extern compass
m_NumberOfBytesToSend=sizeof(m_OrientationFromComp ass);
break;
case '!': // Status of the Program
m_buffer_adr = (char*) &m_Status;
m_NumberOfBytesToSend=sizeof(m_Status);
break;
case 'I': // Version of the
m_buffer_adr = (char*) &m_version;
m_NumberOfBytesToSend=sizeof(m_version);
break;
case 'T': //
m_buffer_adr = (char*) &DataBuffer;
m_NumberOfBytesToSend=4;
DataBuffer[0]=m_MotorTemperatur_Left;
DataBuffer[1]=m_MotorTemperatur_Right;
DataBuffer[2]=m_ControllerTemperatur_Left;
DataBuffer[3]=m_ControllerTemperatur_Right;
break;
case 'S': //
m_buffer_adr = (char*) &DataBuffer;
pointer=(char*) &m_MotorCurrent_Left;
DataBuffer[0]= (char) *pointer++;
DataBuffer[1]= (char) *pointer;
pointer=(char*) &m_MotorCurrent_Right;
DataBuffer[2]= (char) *pointer++;
DataBuffer[3]= (char) *pointer;
m_NumberOfBytesToSend=4;
break;

case 'V': //
m_buffer_adr = (char*) &m_AkkuVoltage;
m_NumberOfBytesToSend=sizeof(m_AkkuVoltage);
break;
default:
m_buffer_adr = (char*) &m_version;
m_NumberOfBytesToSend=sizeof(m_version);
}
SPDR = *m_buffer_adr; // Send the received command as a feedback return to master
m_NumberOfBytesToSend--;
}
else // get no the data into the buffer
{
if(m_NumberOfBytesToSend > 0)
{
if( m_buffer_adr != NULL ) // Do we have a valid pointer
{
*m_buffer_adr=data; // Store the data into the buffer
m_buffer_adr++; // Incr. Buffer address for the next byte
}

m_NumberOfBytesToSend--; // dec. counter of byte to send/receive
if(m_NumberOfBytesToSend <= 0)
{
m_buffer_adr=NULL; // Clear Buffer Pointer
}
}
else
{
m_buffer_adr=NULL; // Clear Buffer Pointer
}

SPDR = 0x0; // Dummy Answer
}
}
else
{
//Slave transmitter ================================================== ==========================

if(m_buffer_adr != NULL) // Buffer set up ?
{
if(m_NumberOfBytesToSend > 0) // do we have to send bytes still
{
SPDR = *m_buffer_adr; // send the data byte
m_buffer_adr++; // get the next byte of the buffer
m_NumberOfBytesToSend--; // dec. counter of byte to send
}
else
{
m_buffer_adr=NULL; // Clear Buffer Pointer
SPDR=0x0; // No more values in the buffer
}
}
else
{
m_buffer_adr=NULL; // Clear Buffer Pointer
SPDR=0x0; // No more values in the buffer
}
}
}



Vielen Dank für die Hilfe. Manchmal sieht man den Wald vor lauter Bäumen nicht und ist so eine Hilfe doch nicht schlecht. :-)

Gruss R.