Datum: 04.04.2008 17:08
Hi.. Ich guck mir grad die SPI Schnittstelle von einem ATmega1281 (Meshnetics ZigBit Modul) und dessen Datenaustausch mit dem RadioChip AT86RF230 an. Initialisierung der SPI Schnittstelle ohne Interrupt Flag:
// SPI Config: Interrupt OFF, SPI ON, MSB first, MasterMode, SPI-Mode 0, Clock Rate F_OSC/2 SPCR = (0<<SPIE)|(1<<SPE)|(0<<DORD)|(1<<MSTR)|(0<<CPOL)|(0<<CPHA)|(0<<SPR1)|(0<<SPR0); SPSR = (1<<SPI2X); |
Der Mega1281 läuft mit 1MHz.. dh. die SPI dann mit 500kHz. Der RF230 kann asynchron bis 7,5MHz sampeln und versteht bisher auch alles. Wenn ich die CKDIV8-Fuse disable, läuft der Mega1281 mit 8MHz und SPI-SCK mit 4MHz.. alles astrein soweit. So, nun zur eigentlichen Frage. Ich hab nach dem Schreiben des Bytes in das SPDR 16 clockcycles gewartet (mit dem Oszi sieht man, dass er dann fertig ist) und schreib dann das nächste Byte rein <<< IST DAS ERLAUBT? Das ganze sieht folgendermassen aus (GCC):
PORTB &= ~(1<<PB0); // SEL low SPDR = 0x8B; // SPI - byte #1 __asm__ __volatile__ (" nop \n" :: ); // NOP 1 __asm__ __volatile__ (" nop \n" :: ); // NOP 2 __asm__ __volatile__ (" nop \n" :: ); // NOP 3 __asm__ __volatile__ (" nop \n" :: ); // NOP 4 __asm__ __volatile__ (" nop \n" :: ); // NOP 5 __asm__ __volatile__ (" nop \n" :: ); // NOP 6 __asm__ __volatile__ (" nop \n" :: ); // NOP 7 __asm__ __volatile__ (" nop \n" :: ); // NOP 8 __asm__ __volatile__ (" nop \n" :: ); // NOP 9 __asm__ __volatile__ (" nop \n" :: ); // NOP 10 __asm__ __volatile__ (" nop \n" :: ); // NOP 11 __asm__ __volatile__ (" nop \n" :: ); // NOP 12 __asm__ __volatile__ (" nop \n" :: ); // NOP 13 __asm__ __volatile__ (" nop \n" :: ); // NOP 14 __asm__ __volatile__ (" nop \n" :: ); // NOP 15 __asm__ __volatile__ (" nop \n" :: ); // NOP 16 SPDR = 0xAA; // SPI - byte #2.. __asm__ __volatile__ (" nop \n" :: ); // NOP 1 __asm__ __volatile__ (" nop \n" :: ); // NOP 2 __asm__ __volatile__ (" nop \n" :: ); // NOP 3 __asm__ __volatile__ (" nop \n" :: ); // NOP 4 __asm__ __volatile__ (" nop \n" :: ); // NOP 5 __asm__ __volatile__ (" nop \n" :: ); // NOP 6 __asm__ __volatile__ (" nop \n" :: ); // NOP 7 __asm__ __volatile__ (" nop \n" :: ); // NOP 8 __asm__ __volatile__ (" nop \n" :: ); // NOP 9 __asm__ __volatile__ (" nop \n" :: ); // NOP 10 __asm__ __volatile__ (" nop \n" :: ); // NOP 11 __asm__ __volatile__ (" nop \n" :: ); // NOP 12 __asm__ __volatile__ (" nop \n" :: ); // NOP 13 __asm__ __volatile__ (" nop \n" :: ); // NOP 14 __asm__ __volatile__ (" nop \n" :: ); // NOP 15 __asm__ __volatile__ (" nop \n" :: ); // NOP 16 SPDR = 0x44; // SPI - byte #3 __asm__ __volatile__ (" nop \n" :: ); // NOP 1 __asm__ __volatile__ (" nop \n" :: ); // NOP 2 __asm__ __volatile__ (" nop \n" :: ); // NOP 3 __asm__ __volatile__ (" nop \n" :: ); // NOP 4 __asm__ __volatile__ (" nop \n" :: ); // NOP 5 __asm__ __volatile__ (" nop \n" :: ); // NOP 6 __asm__ __volatile__ (" nop \n" :: ); // NOP 7 __asm__ __volatile__ (" nop \n" :: ); // NOP 8 __asm__ __volatile__ (" nop \n" :: ); // NOP 9 __asm__ __volatile__ (" nop \n" :: ); // NOP 10 __asm__ __volatile__ (" nop \n" :: ); // NOP 11 __asm__ __volatile__ (" nop \n" :: ); // NOP 12 __asm__ __volatile__ (" nop \n" :: ); // NOP 13 __asm__ __volatile__ (" nop \n" :: ); // NOP 14 __asm__ __volatile__ (" nop \n" :: ); // NOP 15 __asm__ __volatile__ (" nop \n" :: ); // NOP 16 PORTB |= (1<<PB0); // SEL high |
Ich hab nun schon ein paar Datenblätter durchwühlt.. auch App-Notes.. aber ich finde nirgends Angaben (bin ich blind?) darüber, wann man wieder an das SPDR ran darf.. :( Mittels Interrupt oder SPIF-pollen dauerts mir zu lange, bis ich das nächste byte rausschieben darf. Da ist dann immer so viel Leere Zeit wo ich schon die nächsten Befehle übertragen könnte.. Grüsse Joan PS: die angehängten Bilder zeigen die obige Sequenz einmal mit F_osc 1Mhz (SPI 500kHz) und einmal mit F_osc 8MHz (SPI 4MHz).
Datum: 04.04.2008 17:58
>Ich hab nach dem Schreiben des Bytes in das SPDR 16 clockcycles gewartet >(mit dem Oszi sieht man, dass er dann fertig ist) und schreib dann das >nächste Byte rein <<< IST DAS ERLAUBT? Ja, wieso nicht. Hab ich auch schon so gemacht. Ich meine aber das ich 17 clockcycles warten musste bevor ich wieder in SPDR schreiben konnte. Kann mich aber auch verzählt haben.
Datum: 04.04.2008 18:17
holger wrote: > Ja, wieso nicht. Hab ich auch schon so gemacht. Hehe.. ok. Aber irgendwo muss das doch stehen, dass man mehr als 8x2 clock cycles warten soll und dann ohne Flag SPDR neu laden kann? Sämtliche Beispiele sind mit Polling oder ISR.. :( Nirgendwo etwas über.. "if you want to send x-bytes in a row, do this.." > Ich meine aber das ich 17 clockcycles warten musste > bevor ich wieder in SPDR schreiben konnte. Kann mich aber > auch verzählt haben. Jau, die restlichen cycles werden bei mir schon irgendwo in dem "SPDR = xyz;" Befehl stecken, muss ich mir mal den Assembler Code zu angucken, damit ich mir sicher bin.
Datum: 04.04.2008 18:40
>Hehe.. ok. Aber irgendwo muss das doch stehen, dass man mehr als 8x2 >clock cycles warten soll und dann ohne Flag SPDR neu laden kann? Warum sollte das irgendwo stehen ? Das SPI Modul braucht halt mindestens 8 * 2 clock cycles. Nach SPDR=xy; kannst du ja auch einfach ein Delay1ms(200); oder eine ähnlich dumme Warteschleife einfügen. Funktionieren tut es trotzdem. Die Abfrage von SPIF ist nicht zwingend notwendig. Die ganze Geschichte mit den 16 clock cyles funktioniert natürlich nur wenn das SPI Modul auf F_CPU/2 konfiguriert ist. Also Kinder: Bitte nicht nachmachen wenn ihr nicht wisst wie euer SPI Modul eingestellt ist !
Datum: 04.04.2008 21:55
Joan P. wrote: > Mittels Interrupt oder SPIF-pollen dauerts mir zu lange, bis ich das > nächste byte rausschieben darf. Dass ein SPI-Interrupt bei voller Datenrate sinnlos ist, ist klar. Aber warum denkst du, dass deine NOPs schneller sind als das Pollen des SPIF? Die Zeit abwarten musst du so oder so. Dafür fällt das Pollen wohl unter ,,defensive Programmierung'': wenn mal jemand die Datenrate der SPI-Schnittstelle umstellt (warum auch immer), geht das trotzdem noch (nur langsamer). Die SPI-Schnittstelle ist bei 4 MHz übrigens deutlich schneller als die Luftschnittstelle, insofern ist sie gar nicht der Flaschenhals, und es kommt auf einen Takt mehr gar nicht an. Falls du noch einen AT86RF230 rev A hast, musst du ja ohnehin auch noch die CRC-16 mit berechnen beim Empfang, das kann man bequem parallel mit jedem neu gelesenene Byte ausführen, ohne dass man dadurch insgesamt langsamer wird. Beim rev B gibt's dann ein "CRC OK"-Bit, da muss man das nicht mehr selbst machen. Ob du einen rev A oder B in deinem ZigBit hast, erfährst du aus dem Versionsregister.
Datum: 04.04.2008 22:30
holger wrote: > Die Abfrage von SPIF ist nicht zwingend notwendig. Gut, dann fühl ich mich jetzt bestätigt genug, dass ich das als 'gesichert' verwenden kann.. wie gesagt, ich hab nirgends was dazu gefunden - auch nicht beim stöbern in diversen Threads mit den Suchwörtern und Kombinationen daraus: "AVR SPI Geschwindigkeit Latenz Byte Flag ISR..." ;) > Die ganze Geschichte mit den 16 clock cyles funktioniert natürlich > nur wenn das SPI Modul auf F_CPU/2 konfiguriert ist. jup, is klar. Jörg Wunsch wrote: > Dass ein SPI-Interrupt bei voller Datenrate sinnlos ist, ist klar. > Aber warum denkst du, dass deine NOPs schneller sind als das Pollen > des SPIF? Die Zeit abwarten musst du so oder so. Dafür fällt das > Pollen wohl unter ,,defensive Programmierung'': wenn mal jemand die > Datenrate der SPI-Schnittstelle umstellt (warum auch immer), geht > das trotzdem noch (nur langsamer). Die NOPs sind nur testweise.. keine Angst. Das füll ich schon noch mit sinnvollem Code auf. > Die SPI-Schnittstelle ist bei 4 MHz übrigens deutlich schneller als > die Luftschnittstelle, insofern ist sie gar nicht der Flaschenhals, > und es kommt auf einen Takt mehr gar nicht an. Jau.. ich will auch nur Zeit gewinnen.. RF230 initialisieren in Null Komma nix und dann ab die Lutzi.. rumschweinsen kann ich dann später immer noch, wenns dann Richtung Wiederholung und Kanalwechsel geht. Geht nur um wenige Bit, aber relativ störsicher und schnell soll es werden. > Falls du noch einen AT86RF230 rev A hast, musst du ja ohnehin auch noch > die CRC-16 mit berechnen beim Empfang, das kann man bequem parallel mit > jedem neu gelesenene Byte ausführen, ohne dass man dadurch insgesamt > langsamer wird. Beim rev B gibt's dann ein "CRC OK"-Bit, da muss man das > nicht mehr selbst machen. Ob du einen rev A oder B in deinem ZigBit > hast, erfährst du aus dem Versionsregister. Danke für den Hinweis. ------------------------------------ Allgemein muss ich halt sagen, kams mir auf dem Oszi so vor, als würde ich die SPI 'knüppeln', wenn ich nicht erst auf das Flag warte.. die Version mit Flag (ISR oder Polling) hatte meistens mehr wie 3x1 Byte Pause dazwischen, wo nix anderes passierte. Und da wollte ich mich mal erkundigen, ob das Vorgehen 'normal' ist, weil eben nirgends dokumentiert. Grüsse und Danke Joan
Datum: 04.04.2008 22:45
btw: hab Revision A
Datum: 04.04.2008 23:07
Joan P. wrote: > Die NOPs sind nur testweise.. keine Angst. Das füll ich schon noch mit > sinnvollem Code auf. Dann wäre aber Abfrage des SPIF noch besser, da die tatsächliche Länge des ausgeführten Codes nicht mehr ausgezählt werden muss. Geht's dir ums Senden oder Empfangen? Beim Senden bist du eigentlich auch aus dem kritischen Bereich, wenn du die Übertragung einfach erst einmal startest (durch Impuls an SLP_TR) und erst danach die Daten runterfütterst. Der Chip muss nämlich erstmal die Präambel rauspusten (4 + 1 Bytes, also 160 µs), bevor er deine Daten benötigt. Du hast dann gewissermaßen alle Zeit der Welt, ihm die Daten nachzureichen...
Datum: 04.04.2008 23:32
Danke @Udo.. die kannte ich noch nicht. Gleich mal den Link abspeichern. Jörg Wunsch wrote: > Geht's dir ums Senden oder Empfangen? Beim Senden bist du eigentlich > auch aus dem kritischen Bereich, wenn du die Übertragung einfach > erst einmal startest (durch Impuls an SLP_TR) und erst danach die > Daten runterfütterst. Der Chip muss nämlich erstmal die Präambel > rauspusten (4 + 1 Bytes, also 160 µs), bevor er deine Daten benötigt. > Du hast dann gewissermaßen alle Zeit der Welt, ihm die Daten > nachzureichen... So schnell wie möglich initialisieren, also Kanal, dB usw.. Ich weiß halt noch nicht, wie oft ich das brauch. Deswegen wär halt je schneller um so besser. Ist alles noch tlw im Kopf und nicht auf Papier ;)
Datum: 04.04.2008 23:35
Die PLL braucht vermutlich länger zum Abgleich der Mittenfrequenz als deine paar Takte. Nimm ruhig das SPIF-Flag, dann bist du immer auf der sicheren Seite.
Antwort schreiben
Die Angabe einer Email-Adresse ist freiwillig. Wenn Sie automatisch per Email über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.
Wichtige Regeln - erst lesen, dann posten!
- Suchfunktion und Betreffsuche benutzen - vielleicht gibt es schon einen ähnlichen Beitrag
- Aussagekräftigen Betreff wählen
- Im Betreff angeben um welchen Controllertyp es geht (AVR, PIC, ...)
- Groß- und Kleinschreibung verwenden
- Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang
- JPEG-Dateien (.jpg) nur für Fotos und Scans verwenden
- Schaltpläne, Screenshots usw. als PNG oder GIF anhängen
Formatierung (mehr Informationen...)
- [c]C-Code[/c]
- [avrasm]AVR-Assembler-Code[/avrasm]
- [pre]vorformatierter Text (z.B. Code in anderen Sprachen)[/pre]
- [math]Formel in LaTeX-Syntax[/math]
- [[Titel]] - Link zu Artikel