@oldmax:
Und wie hältst Du einen Microcontroller an, wenn das komplette Programm beendet ist? Das ist nichts anderes als 'ne leere Hauptprogrammschleife. Ob ich das Label da Main, Hauptprogramm oder schnurzdidudel nenne, ist doch vollkommen egal.
Und wo ist Dir die ISR der ersten Version zu lang? Es steht ein einziger Befehl drin (nebenbei gesagt ein ein-Takt-Befehl).
Das Trennen ist es ja, was ihm zuviel Zeit kostet - deswegen sucht er ja 'ne alternative zum üblichen Weg in Bascom. Er will ja (warum auch immer) keine Pause zwischen den beiden Bytes haben. Deswegen habe ich eben kurz skizziert, wie man das SPI zum versenden von words verwenden könnte (es geht als gar nicht um das ganze Programm,
sondern nur um das Senden der beiden Bytes). Als erstes die Version mit verwendung des SPI-Interruptes. Diese hat nämlich die längste Reaktionszeit
(Byte fertig=IRQ Befehl beenden->Programmadresse auf den Stack und Sprung in die IVT->Sprung zur ISR ->Arbeitscode). Bei einem Controller mit 2-word-IVT hätte man hier(!) das OUT und das RET sogar in die IVT schreiben können, aber selbst dann wäre die Pause noch da.
In der 2ten Version wird das SPI-fertig-Flag gepollt. hier ist die Pause etwas kleiner, aber natürlich immer noch vorhanden. Logisch, der Controller wartet ja nicht wirklich auf das Flag, sondern hopst mehr oder weniger auf der Stelle. Somit verbraucht er also zwischen dem letzten Bit von Byte1 (wo das Flag gesetzt wird) und dem beginn des 2ten Bytes noch Zeit für ein SBIS. Wenn das Flag gerade beim RJMP kommt, kommt der auch noch dazu.
Da die Taktanzahl, die das Senden eines Bytes dauert konstant ist, könnte man auch ausrechnen, wo das Flag in der Schleife fällt (wenns auf das Tempo ankommt, dürfen eh im Hintergrund keine Interrupts dazwischenfunken) - ich hab mir die Mühe gespart.
Und damit steht auch die dritte Version fest. Die SPI-Hardware sendet byteweise, also 8 bits. Da wir diese "was haste-was kannste" laufen lassen, also der SPI mit halber Systemtaktfrequenz läuft, benötigt das versenden eines Bytes im Hintergrund 16 Takte. Zwischendurch muß eventuell noch das zweite Byte aus dem SRAM geladen werden (falls das letztendlich in Bascom eingebunden werden soll). Die restlichen Takte würde oldmax jetzt vielleicht noch mit sinnigem code füllen wollen (+ dem dazu nötigen Verwaltungsoverhead) - zum testen reichen NOPs. damit sollte sich die Pause eigentlich eliminieren lassen. Natürlich muß das ganze Senden dann atomar erfolgen (zumindest bis zum Start des 2ten Bytes, wobei man sich dann irgendwann um das SPIF kümmern müßte (spätestens vor dem Senden des nächsten Words)).
@Uwe:
Du kannst ja auch bei BASCOM bleiben, und den (dann) entscheidenden Teil in Assembler in Bascom einsetzen. Ob sich die hier konkret angewendete Initialisierung des SPI in Bascom mit einem irgendwie gearteten Config-Befehl bewirken läßt, hab ich noch nicht nachgesehen... im Zweifelsfall geht das aber auch wie hier direkt über die Register...
Zu Deinen Anmerkungen:
Code:
LDI R16, (1<<SPE)|(1<<MSTR)
OUT SPCR, R16
hier wird das Byte 0b01010000 in das SPI-Kontrollregister geschrieben [SPIE|SPE|DORD|MSTR|CPOL|CPHA|SPR1|SPR0]
SPI-Interrupt aus
SPI enabled
MSB first
Master-Mode
CPOL=0
CPHA=0->hab ich willkürlich so gewählt - CPOL, CPHA und DORD müssen natürlich auf den konkreten Empfänger angepaßt werden. Die Reihenfolge der Bytes ebenso
SPR1..0=00(binär) -> SPI-Prescaler=4
Code:
SBI SPSR, SPI2X ;F(sck)=F(osc)/2 Osc/2 OK vorgehensweise ??
Hier wird direkt (SPSR erlaubt das) das "Double SPI Speed Bit" (SPI2X in SPSR) gesetzt, welches (oh welch Überraschung) den SPI-Takt verdoppelt. oder eben den Teiler halbiert.
Code:
endlos:
rjmp endlos muss es hier nicht [rjmp warten] stehen??
Nein - hier ist schon alles vorbei - hier hopst er nur noch auf der Stelle, damit er nicht Flashtechnisch ins Nirvana rennt.
Der letzte Block ist das eigentliche Senden.
-am Anfang wird CS runtergesetzt, am Ende wieder rauf. sollte klar sein
-die beiden zu senden den Bytes hatten wir vorher in R16 und R17 geparkt.
-als erstes wird (OUT SPDR, R16) das erste Byte ins SPI-Datenregister geschrieben. Dieser Schreibzugriff löst in der HArdware den Transfer aus (siehe Datenblatt zu SPDR.
-Danach wird mit dem label, dem SBIS (skip if bit in IO-register is set) und dem RJMP (relative jump) eine Warteschleife aufgebaut (RJMP läßt immer wieder zurück zum label springen, erst wenn des SPIF im SPI-Statusregister gesetzt ist, wird der RJMP übersprungen, wodurch die Schleife verlassen wird)
-jetzt wird analog das 2te byte aus R17 ins SPDR geschrieben (und damit das Senden gestartet) - wir behalten aber im Kopf, daß das SPIF weiterhin gesetzt ist/bleibt - darum kümmern wir uns jetzt
-liest man das SPDR, wird der Inhalt des Recieve-Puffers ausgelesen - wir schreiben diesen nach R16. (siehe SPDR im Datenblatt). Wie man der Erklärung zum SPIF (in SPSR) aus dem Datenblatt weiterhin entnehmen kann, wird dadurch außerdem das SPIF gelöscht (SPSR bei gesetzem SPIF lesen ist ja quasi die Abbruchbedingung unserer Schleife da oben gewesen)
-somit können wir also wie beim ersten mal das SPIF pollen (Schleife mit label, SBIS und RJMP), danach wollen wir kein weiteres Byte senden (also hier kein OUT nach SPDR), wir lesen aber noch das empfangene 2te Byte nach R17 (IN), wodurch auch hier das SPIF gelöscht wird.
-das hochsetzen von CS hatte ich ja oben angesprochen
-RET sollte klar sein (return - das ganze wird ja mit rcall als subroutine/Prozedur aufgerufen - die muß natürlich irgendwann beendet werden bzw die Rückkehr ins Hauptprogramm auslösen)