www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik SPI mit Interrupt: was macht der Compiler?


Autor: Marc Heimann (mheimann84)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich bin gerade dabei spi funktionen für einen ATxmega128 zu schreiben, 
um ein display anzusteuern. Dabei orientiere ich mich an dem Code von 
Atmel: http://www.atmel.com/dyn/resources/prod_documents/...

Da ich nur Daten versenden will, habe ich entsprechend den Code 
verkürzt. Im Anhang sieht man eine Aufzeichnung vom Logic Analyzer, wie 
es aussehen sollte. Es sollten also die vier folgenden Schritte 
stattfinden:

1. SS auf low
2. versende erstes byte
3. versende zweites byte
4. SS auf high

Ich bin quasi durch Zufall auf den "richtigen" Code gekommen. Der 
folgende Code macht nur Schritt 1 und 2, ein Interrupt wird nicht 
ausgelöst (die Initialisierung habe ich weggelassen):
void transmit_data( volatile struct spi_handle *sh )
{
  uint8_t data;
  
  // If more data
  if (sh->spi_stat.data_index < sh->spi_stat.data_size) {
    data = sh->spi_stat.data_pointer[sh->spi_stat.data_index];
    sh->spi_stat.data_index++; // Next byte
    sh->spi_reg->DATA = data;
  }
  
  // Transmission complete
  else {
    sh->spi_port->OUT |= SPI_SS_bm;
    sh->spi_stat.complete = 1;
  }
  
}

ISR(SPID_INT_vect)
{
  transmit_data( spi_handle_portd );
}

void spi_send( volatile struct spi_handle *sh, uint8_t *data, uint8_t size )
{
  while (sh->spi_stat.complete == 0); // wait until previous transmission finishes
  
  sh->spi_stat.data_pointer = data;
  sh->spi_stat.data_index = 0;
  sh->spi_stat.data_size = size;
  
  sh->spi_port->OUT &= ~SPI_SS_bm;
  transmit_data( sh );
}


int main( void )
{
  uint8_t data[2] = {0xaa, 0xf1};
  spi_send( sh, data, 2 );
  
  return 0;
}

Die nächste Version von transmit_data() war eigentlich die 
ursprüngliche. Hier wird Schritt 1, 2 und 3 durchgeführt, allerdings 
stimmen die Daten bei 3 nicht. Es wird einmal der Interrupt ausgelöst.
void transmit_data( volatile struct spi_handle *sh )
{
  // If more data
  if (sh->spi_stat.data_index < sh->spi_stat.data_size) {
    sh->spi_reg->DATA = sh->spi_stat.data_pointer[sh->spi_stat.data_index];
    sh->spi_stat.data_index++; // Next byte
  }
  
  // Transmission complete
  else {
    sh->spi_port->OUT |= SPI_SS_bm;
    sh->spi_stat.complete = 1;
  }
  
}

Da ich bereits ein funktionierendes hd44780 display habe, wollte ich mal 
den index ausgeben. Ich habe dann erstmal einen sprintf() Aufruf 
gebraucht, um den entsprechenden String zu bekommen, doch da 
funktionierte es auch schon. Die folgende Version macht alle 4 Schritte 
korrekt und die ISR wird auch zweimal aufgerufen:
void transmit_data( volatile struct spi_handle *sh )
{
  char buffer[16];
  sprintf( buffer, "index: %d", sh->spi_stat.data_index );
  
  // If more data
  if (sh->spi_stat.data_index < sh->spi_stat.data_size) {
    sh->spi_reg->DATA = sh->spi_stat.data_pointer[sh->spi_stat.data_index];
    sh->spi_stat.data_index++; // Next byte
  }
  
  // Transmission complete
  else {
    sh->spi_port->OUT |= SPI_SS_bm;
    sh->spi_stat.complete = 1;
  }
  
}

Ich glaube, dass ich irgendetwas noch nicht beachte. So will ich die 
Daten nicht übertragen, wer weiß was sonst noch passiert. Ich verwende 
avr-gcc. Kann mir bitte jemand sagen, warum hier so ein Chaos 
stattfindet?

Autor: Hans W. (hans_w30)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hy,
mit welcher Frequenz sendest du denn das ganze?
Bei mir gabs bei verwendetem (32 MHz (16MHz extern) Systemtakt) 4MHz 
Clock des SPI schon manchmal probleme. Versuche es mal mit ner kleineren 
Frequenz.
Was machst du eigentlich da mit deinem Buffer? Der wird doch nie benutzt 
oder?
Sicher das du nicht noch irgendwo anders was verändert hast?

Autor: Marc Heimann (mheimann84)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ausschnitt:
  // SPI configuration, p. 232f
  sh->spi_reg->CTRL = (0 << SPI_CLK2X_bp) | (1 << SPI_ENABLE_bp) | (0 << SPI_DORD_bp) | (1 << SPI_MASTER_bp) | (SPI_MODE_t)SPI_MODE_3_gc | (SPI_PRESCALER_t)SPI_PRESCALER_DIV4_gc;

Das ist nach Datenblatt clk/4. Ich verwende den internen Clock mit 
standardmäßig 2Mhz. Das ergibt 500 kHz, was ich auch mit dem Logic 
Analyzer messe.

Autor: Hans W. (hans_w30)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Probier einfach mal den Clk clk/16 aus.
Sollte aber eigentlich nicht das Problem sein.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dass du deine Zeiger volatile deklarierst, wird zwar helfen, den
Code zu pessimieren, aber ob es auch hilft, die tatsächlichen Daten
passend zu holen?

Anyway, SPI mit Interrupt hat bei hinreichend schnell getaktetem
SPI gar keinen Sinn: die Interrupt-Latenz (bis die ISR tatsächlich
was tut) ist größer als die Zeit, die du auf das Ende der SPI-
Übertragung warten würdest.

Autor: Marc Heimann (mheimann84)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der buffer wird von sprintf verwendet. Der Witz ist ja, dass ich ihn ja 
eigentlich nicht brauche, aber dann habe ich den Fall mit nur einem ISR 
Aufruf (Version 2). Irgendetwas löst sprintf aus und dann gehts auf 
einmal.

Habe mal noch eine anderen prescaler wert verwendet: 
SPI_PRESCALER_DIV128_gc. Damit komme ich auf 15625 Hz. Allerdings machen 
jetzt alle Versionen nur noch Schritt 1 und 2.

Mit SPI_PRESCALER_DIV16_gc kommt das selbe wie mit 
SPI_PRESCALER_DIV128_gc raus :( Habe mit SPI_PRESCALER_DIV4_gc wirklich 
einen Zufallstreffer gelandet.

Autor: Marc Heimann (mheimann84)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, wollte nur sagen, dass ich es jetzt mit polling probieren werde. 
Später soll für das 128x64 display sowieso extra ein uC für die Grafik 
spendiert werden. Melde mich dann nochmal kurz, wenn es geklappt hat, 
dann belassen wir es einfach dabei. Falls jemand eine Lösung kennt, darf 
er/sie es trotzdem gerne mitteilen.

Autor: Marc Heimann (mheimann84)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also, das Aufräumen hat jetzt länger gedauert, als den Code 
umzuschreiben. Folgendes funktioniert nun mit verschiedenen prescaler 
Einstellungen:
void spi_send( struct spi_handle *sh, uint8_t *data, uint8_t size )
{
  uint8_t data_index;
  
  sh->spi_port->OUT &= ~SPI_SS_bm;
  for (data_index = 0; data_index < size; data_index++)
  {
    sh->spi_reg->DATA = data[data_index];
    while(!(sh->spi_reg->STATUS & SPI_IF_bm));
  }
  sh->spi_port->OUT |= SPI_SS_bm;
}

Danke, dass ihr euch Zeit genommen habt.

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.