Forum: Mikrocontroller und Digitale Elektronik gepufferte SPI Ausgabe


von Alexander (Gast)


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;
}

von crazy horse (Gast)


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 :-)

von Alexander (Gast)


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

von Benedikt K. (benedikt)


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.

von Alexander (Gast)


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

von crazy horse (Gast)


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")
}

von Alexander (Gast)


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

von Alexander (Gast)


Lesenswert?

Problem gelöst.

Vielen Dank an Euch

von crazy horse (Gast)


Lesenswert?

nun aber auch raus mit der Sprache, was es war.

von Alexander (Gast)


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;
  }
}

von 6642 (Gast)


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.

von Alexander (Gast)


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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.