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; }
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 :-)
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
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.
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
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") }
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
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; } }
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.