mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik gepufferte SPI Ausgabe


Autor: Alexander (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Salut,

ich möchte eine gepufferte SPI Ausgabe schreibe. damit ich nicht jedes 
Mal im Program auf des SPIF warten muss. Nun bin ich schon eine Weile am 
rumdoktern und der Verzweiflung nahe, weiol es nicht so läuft, wie 
gewünscht. Ich benutze Ringpuffer mit schreib und Lesezeiger und 
Statusflag, ob er leer ist oder etwas drin steht. Wenn der Puffer leer 
ist, schreibe ich in der spi_putc(byte) direkt ins SPDR ansonsten in den 
Puffer. In der ISR wird dann geguck, ob noch was im Puffer zu senden 
ist. Quellcode siehe. Wenn jemand ein offensichtliche Dummheit entdeckt, 
möge er sie mir doch bitte verraten.

Danke, Alex

void spi_masterinit(void) {

  spi_out_wr = 0;
  spi_out_rd = 0;
  spi_buf_sts = 0;

  DDRB |= (1<<DDB7)|(1<<DDB5)|(1<<DDB4);
  SPCR |= (1<<SPIE)|(1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);
}

char spi_putc(char send_byte) {

  if(spi_buf_sts == 0) SPDR = send_byte;

  else {

    spi_out_buf[spi_out_wr ++] = send_byte;

    if(spi_out_wr == SPI_BUFLENGTH) spi_out_wr = 0;

  }

  return (send_byte);
}


ISR(SPI_STC_vect) {

  if(spi_out_wr != spi_out_rd) {

    SPDR = spi_out_buf[spi_out_rd ++];

    if(spi_out_rd == SPI_BUFLENGTH) spi_out_rd = 0;

    spi_buf_sts = 1;
  }

  else spi_buf_sts = 0;
}

Autor: crazy horse (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was geht denn denn nicht?
Auf jeden Fall solltest du während der spi_putc-Routine die Interupts 
sperren, sonst ist Datenmüll vorprogrammiert. Fällt nicht sofort durch 
Fehler auf, kommt aber irgendwann :-)

Autor: Alexander (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

wenn ich dreimal direkt hintereinander spi_putc aufrufe, wird nur einmal 
gesendet, auch die ISR wird nur einmal angesprungen.
Das mit der Interrupt-Sperre probiere ich direkt mal aus..
Feddback in einigen Minuten.

Alex

Autor: Benedikt K. (benedikt) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jetzt nicht direkt ein Fehler, aber ein Problem: Du prüfst nicht, ob 
noch Platz im Puffer ist, wenn du die zu sendenden Daten reinschreibst.
Ein weiters Problem: Was passiert, wenn der Interrupt kommt, während du 
gerade spi_putc ausfürhrst ?
Sagen wir mal der Puffer wäre leer, dann setzt die Interruptroutrine 
spi_buf_sts auf 0, obwohl direkt danach Daten in den Puffer geschrieben 
werden. Diese werden erst gesendet, wenn das nächste Byte mit spi_putc 
geschrieben wird.

Ansonsten sehe ich jetzt eigentlich keine Fehler, wiso das Programm 
nicht funktionieren sollte.

Autor: Alexander (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
der Puffer ist 32 byte groß, in meinem Programm werde ich nie 32 Byte in 
Folge senden. Ein überlauf ist also quasi ausgeschlossen. Das andere 
Problem werde ich wohl noch überdenken müssen. Vielleicht lässt es sich 
ja mit dem Vorschlag deines Vorredners lösen.

Danke, Alex

Autor: crazy horse (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier mal ein Beispiel einer funktionierenden gepufferten UART-Ausgabe,
sollte sich leicht auf SPI umbiegen lassen.

// USART Transmitter buffer
#define TX_BUFFER_SIZE 8
char tx_buffer[TX_BUFFER_SIZE];
unsigned char tx_wr_index,tx_rd_index,tx_counter;

// USART Transmitter interrupt service routine
interrupt [USART_TXC] void usart_tx_isr(void)
{
if (tx_counter)
   {
   --tx_counter;
   UDR=tx_buffer[tx_rd_index];
   if (++tx_rd_index == TX_BUFFER_SIZE) tx_rd_index=0;
   };
}


void putchar(char c)
{
while (tx_counter == TX_BUFFER_SIZE);
#asm("cli")
if (tx_counter || ((UCSRA & DATA_REGISTER_EMPTY)==0))
   {
   tx_buffer[tx_wr_index]=c;
   if (++tx_wr_index == TX_BUFFER_SIZE) tx_wr_index=0;
   ++tx_counter;
   }
else
   UDR=c;
#asm("sei")
}

Autor: Alexander (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Den Interrupt wärend spi_putc zu sperren, hat das Problem nicht gelöst. 
Ich hab noch mal mit dem Oszi geschaut, es wird nur das erste 
ausgegeben, die anderen beiden...? Werde wohl noch etwas rumprobieren 
müssen. Hat noch jemand 'nen vorschlag, wie man sie 'ner Lösung annähern 
könnte?

Alex

Autor: Alexander (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Problem gelöst.

Vielen Dank an Euch

Autor: crazy horse (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
nun aber auch raus mit der Sprache, was es war.

Autor: Alexander (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich habe noch ein busy-flag eingeführt, was immer dann gesetzt wird, 
wenn das SPDR beschrieben wird und gelöscht wird, wenn das letzte byte 
gesendet, das heißt, die ISR augerufen und der Buffer leer vorgefunden 
wurde. Ohne dieses Flag, schreibt die spi_putc, so wie sie oben steht 
jedes mal direktt in SPDR, weil der spi_buf_sts noch auf null steht, 
weil der erste Interrupt noch gar nicht erfolgt ist und somit 
spi_buf_sts noch nicht gesetzt wurde.

Yippi, ich könnte feiern ;-)

Codeschnipsel:

char spi_putc(char send_byte) {

  SPCR &= ~(1<<SPIE);

  if(spi_buf_sts == 0 && spi_busy == 0) {

    spi_busy = 1;

    SPDR = send_byte;
  }
  else {

    spi_out_buf[spi_out_wr ++] = send_byte;

    if(spi_out_wr == SPI_BUFLENGTH) spi_out_wr = 0;

  }

  SPCR |= (1<<SPIE);

  return (send_byte);
}


ISR(SPI_STC_vect) {



  PORTC ^= (1<<7);



  if(spi_out_wr != spi_out_rd) {

    spi_buf_sts = 1;

    SPDR = spi_out_buf[spi_out_rd ++];

      if(spi_out_rd == SPI_BUFLENGTH) spi_out_rd = 0;

  }

  else {

    spi_buf_sts = 0;

    spi_busy = 0;
  }
}

Autor: 6642 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Meines Erachtens lohnen sich SPI interrupts nicht wenn man mit hohen 
Bitraten arbeitet. Bei Bitrate = Clock ist ein byte nach 8 clocks 
draussen, Da kann ich grad mal 2, 3 bytes pushen und popen. Dann kann 
man's gleich im Polling betrieb laufen lassen.

Autor: Alexander (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mag sein, aber manchmal muss es eben langsamer auch vernünftig und ohne 
große Wartezeiten im Programmablauf gehen. Außerdem freue ich mich ja 
nur, dass ich's gelöst habe.

Alex

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.