Hallo,
ich habe eine SPI Interrupt Routine in C, die auch funktioniert. Da aber
der Stack im externem Speicher mit Waitstates liegt, ist die Routine
effektiv langsamer als wenn ich polling verwenden würde. Da recht viele
Daten über die Schnittstelle gehen, werden so durchaus 27% aller CPU
Zyklen nur für den Interrupt verbraucht.
Ich habe die Routine in Assembler umgeschrieben. Der Code kommt mit
weniger Registern aus und soll diese im schnellerem internem RAM sichern
und sollte somit insgesamt rund doppelt so schnell sein.
Leider hängt die Verwendung der Assembler Routine meinen ATMega128
komplett auf. Wo ist der Fehler? Das es nicht auf Anhieb komplett
funktioniert, habe ich irgendwie erwartet, aber nicht dass der MCU
hängt.
Der Code soll den Inhalt von fastbuff (32 Byte Größe) über SPI senden
und sobald der Buffer übertragen ist, Timer3 aktivieren.
Die funktionierende C Routine: 1 | /* a run takes approx: prologue: 39, calc: 17, epiloge: 39, sum: 95
| 2 | */
| 3 | SIGNAL(SIG_SPI) {
| 4 | uint8_t fbct = fastbuffcnt;
| 5 | if (fbct < fastbuffmax) {
| 6 | SPDR = fastbuff[fbct];
| 7 | fbct++;
| 8 | fastbuffcnt = fbct;
| 9 | } else {
| 10 | TCNT3 = -100; //overflow in 100 clocks -> current SPI must have finished
| 11 | TCCR3B = 1<<CS30;
| 12 | cbi(SPCR, SPIE);
| 13 | }
| 14 | }
|
der fehlerhafte Code:
1 | uint8_t regbuff[4]; //faster than the external RAM
| 2 | /* System clocks:
| 3 | prologue: 13
| 4 | code: 20
| 5 | epilogue: 13
| 6 | sum: 43
| 7 | */
| 8 | //SIGNAL(SIG_SPI) __attribute__((naked));
| 9 | ISR(SIG_SPI, ISR_NAKED) {
| 10 | asm volatile (
| 11 | //prologue
| 12 | "sts regbuff, r30""\n\t"
| 13 | "sts (regbuff)+1, r31""\n\t"
| 14 | "in r30, __SREG__""\n\t"
| 15 | "sts (regbuff)+2, r30""\n\t"
| 16 | "sts (regbuff)+3, r29""\n\t"
| 17 | //code
| 18 | "lds r29, fastbuffcnt""\n\t" //SPDR = fastbuff[fastbuffcnt];
| 19 | "ldi r30, lo8(fastbuff)""\n\t"
| 20 | "ldi r31, hi8(fastbuff)""\n\t"
| 21 | "add r30, r29""\n\t"
| 22 | "clr r29""\n\t"
| 23 | "adc r31, r29""\n\t"
| 24 | "ld r29, Z""\n\t"
| 25 | "out %0, r29""\n\t"
| 26 | "lds r30, fastbuffcnt""\n\t" // fastbuffcnt++;
| 27 | "inc r30""\n\t"
| 28 | "sts fastbuffcnt, r30""\n\t"
| 29 | "lds r31, fastbuffmax""\n\t" // if (fastbuffermax =< fastbuffcnt ) {
| 30 | "cp r30, r31""\n\t"
| 31 | "brlo 1f""\n\t"
| 32 | "ldi r30, lo8(-100)""\n\t" // TCNT3 = -100;
| 33 | "ldi r31, hi8(-100)""\n\t"
| 34 | "sts %1, r31""\n\t"
| 35 | "sts %2, r30""\n\t"
| 36 | "ldi r30,lo8(1)""\n\t" // TCCR3B = 1<<CS30;
| 37 | "sts %3, r30""\n\t"
| 38 | "cbi %4,7""\n\t" // cbi(SPCR, SPIE);
| 39 | "1:""\n\t" // }
| 40 | //epilogue
| 41 | "lds r29, (regbuff)+3""\n\t"
| 42 | "lds r30, (regbuff)+2""\n\t"
| 43 | "out __SREG__, r30""\n\t"
| 44 | "lds r31, (regbuff)+1""\n\t"
| 45 | "lds r30, (regbuff)+0"
| 46 | : : "M" (_SFR_IO_ADDR(SPDR)), "M" (_SFR_IO_ADDR(TCNT3H)),
| 47 | "M" (_SFR_IO_ADDR(TCNT3L)), "M" (_SFR_IO_ADDR(TCCR3B)),
| 48 | "M" (_SFR_IO_ADDR(SPCR)));
| 49 | reti();
| 50 | }
|
Malte __ schrieb:
> Da aber der Stack im externem Speicher mit Waitstates liegt
Das solltest du schleunigst ändern. Oder gibt es auch nur einen guten
Grund dafür?
> Leider hängt die Verwendung der Assembler Routine meinen ATMega128
> komplett auf.
Das dürfte an den falschen "_SFR_IO_ADDR" liegen.
Stefan Ernst schrieb:
> Das dürfte an den falschen "_SFR_IO_ADDR" liegen.
Um es ausführlicher zu machen: Nimmst Du STS/LDS usw., musst Du
Ram-Adressen verwenden, also Labels ohne __SFR_IO_ADDR(). Nimmst Du
I/O-Befehle wie IN/OUT/SBI etc., brauchst Du I/O-Adressen, also mit
_SFR_IO_ADDR().
Stefan Ernst schrieb:
> Malte __ schrieb:
>> Da aber der Stack im externem Speicher mit Waitstates liegt
>
> Das solltest du schleunigst ändern. Oder gibt es auch nur einen guten
> Grund dafür?
Ich verwende einen kooperativen Scheduler. Und alle Threads zusammen
benötigen mehr Stack (derzeit in der Summe ~8KB) als interner RAM
verfügbar ist.
>> Leider hängt die Verwendung der Assembler Routine meinen ATMega128
>> komplett auf.
>
> Das dürfte an den falschen "_SFR_IO_ADDR" liegen.
Hm, stimmt, danach hätte ich lange gesucht. Das generiert Adressen die
0x20 zu groß sind, wenn die Adresse nur per sts erreichbar ist.
Malte __ schrieb:
> Hm, stimmt, danach hätte ich lange gesucht. Das generiert Adressen die
> 0x20 zu groß sind, wenn die Adresse nur per sts erreichbar ist.
Nein, 0x20 zu klein. ;-)
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|