Forum: Mikrocontroller und Digitale Elektronik AVR - SPI im MasterMode auch ohne Int-Flag erlaubt?


von Joan P. (joan)


Angehängte Dateien:

Lesenswert?

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:
1
  // SPI Config: Interrupt OFF, SPI ON, MSB first, MasterMode, SPI-Mode 0, Clock Rate F_OSC/2
2
  SPCR = (0<<SPIE)|(1<<SPE)|(0<<DORD)|(1<<MSTR)|(0<<CPOL)|(0<<CPHA)|(0<<SPR1)|(0<<SPR0);
3
  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):
1
PORTB &= ~(1<<PB0); // SEL low
2
SPDR = 0x8B; // SPI - byte #1
3
4
__asm__ __volatile__ (" nop \n" :: ); // NOP 1
5
__asm__ __volatile__ (" nop \n" :: ); // NOP 2
6
__asm__ __volatile__ (" nop \n" :: ); // NOP 3
7
__asm__ __volatile__ (" nop \n" :: ); // NOP 4
8
__asm__ __volatile__ (" nop \n" :: ); // NOP 5
9
__asm__ __volatile__ (" nop \n" :: ); // NOP 6
10
__asm__ __volatile__ (" nop \n" :: ); // NOP 7
11
__asm__ __volatile__ (" nop \n" :: ); // NOP 8
12
__asm__ __volatile__ (" nop \n" :: ); // NOP 9
13
__asm__ __volatile__ (" nop \n" :: ); // NOP 10
14
__asm__ __volatile__ (" nop \n" :: ); // NOP 11
15
__asm__ __volatile__ (" nop \n" :: ); // NOP 12
16
__asm__ __volatile__ (" nop \n" :: ); // NOP 13
17
__asm__ __volatile__ (" nop \n" :: ); // NOP 14
18
__asm__ __volatile__ (" nop \n" :: ); // NOP 15
19
__asm__ __volatile__ (" nop \n" :: ); // NOP 16
20
21
SPDR = 0xAA; // SPI - byte #2..
22
23
__asm__ __volatile__ (" nop \n" :: ); // NOP 1
24
__asm__ __volatile__ (" nop \n" :: ); // NOP 2
25
__asm__ __volatile__ (" nop \n" :: ); // NOP 3
26
__asm__ __volatile__ (" nop \n" :: ); // NOP 4
27
__asm__ __volatile__ (" nop \n" :: ); // NOP 5
28
__asm__ __volatile__ (" nop \n" :: ); // NOP 6
29
__asm__ __volatile__ (" nop \n" :: ); // NOP 7
30
__asm__ __volatile__ (" nop \n" :: ); // NOP 8
31
__asm__ __volatile__ (" nop \n" :: ); // NOP 9
32
__asm__ __volatile__ (" nop \n" :: ); // NOP 10
33
__asm__ __volatile__ (" nop \n" :: ); // NOP 11
34
__asm__ __volatile__ (" nop \n" :: ); // NOP 12
35
__asm__ __volatile__ (" nop \n" :: ); // NOP 13
36
__asm__ __volatile__ (" nop \n" :: ); // NOP 14
37
__asm__ __volatile__ (" nop \n" :: ); // NOP 15
38
__asm__ __volatile__ (" nop \n" :: ); // NOP 16
39
40
SPDR = 0x44; // SPI - byte #3
41
42
__asm__ __volatile__ (" nop \n" :: ); // NOP 1
43
__asm__ __volatile__ (" nop \n" :: ); // NOP 2
44
__asm__ __volatile__ (" nop \n" :: ); // NOP 3
45
__asm__ __volatile__ (" nop \n" :: ); // NOP 4
46
__asm__ __volatile__ (" nop \n" :: ); // NOP 5
47
__asm__ __volatile__ (" nop \n" :: ); // NOP 6
48
__asm__ __volatile__ (" nop \n" :: ); // NOP 7
49
__asm__ __volatile__ (" nop \n" :: ); // NOP 8
50
__asm__ __volatile__ (" nop \n" :: ); // NOP 9
51
__asm__ __volatile__ (" nop \n" :: ); // NOP 10
52
__asm__ __volatile__ (" nop \n" :: ); // NOP 11
53
__asm__ __volatile__ (" nop \n" :: ); // NOP 12
54
__asm__ __volatile__ (" nop \n" :: ); // NOP 13
55
__asm__ __volatile__ (" nop \n" :: ); // NOP 14
56
__asm__ __volatile__ (" nop \n" :: ); // NOP 15
57
__asm__ __volatile__ (" nop \n" :: ); // NOP 16
58
59
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).

von holger (Gast)


Lesenswert?

>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.

von Joan P. (joan)


Lesenswert?

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.

von holger (Gast)


Lesenswert?

>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 !

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Joan P. (joan)


Lesenswert?

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

von Joan P. (joan)


Lesenswert?

btw: hab Revision A

von Udo S. (udo)


Lesenswert?

hier haste schon mal geschaut?
http://www.matuschek.net/atmega-spi/

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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...

von Joan P. (joan)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

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.