Questo e' il codice assembly della tua routine di interrupt cosi' come la compila l'ide di arduino (ver. 1.8.1.13).
Uff... pur mettendolo come codice non mi mantiene la formattazione. Fa cacarissimo ma spero si capisca lo stesso (adesso non ho piu' tempo per riformattare).
Codice:
25c: 89 b1 in r24, 0x09 ; 9 1 ciclo
25e: 8c 70 andi r24, 0x0C ; 12 1 ciclo
260: 90 91 16 01 lds r25, 0x0116 ; 0x800116 <_ZZ4loopE12prev_encoder> 2 cicli
264: 89 2b or r24, r25 1 ciclo
266: 90 e0 ldi r25, 0x00 ; 0 1 ciclo
268: fc 01 movw r30, r24 1 ciclo
26a: e0 50 subi r30, 0x00 ; 0 1 ciclo
26c: ff 4f sbci r31, 0xFF ; 255 1 ciclo
26e: 20 81 ld r18, Z 2 cicli
270: 40 91 14 01 lds r20, 0x0114 ; 0x800114 <steps> 2 cicli
274: 50 91 15 01 lds r21, 0x0115 ; 0x800115 <steps+0x1> 2 cicli
278: 42 0f add r20, r18 1 ciclo
27a: 51 1d adc r21, r1 1 ciclo
27c: 27 fd sbrc r18, 7 1/2/3 cicli
27e: 5a 95 dec r21 1
280: 50 93 15 01 sts 0x0115, r21 ; 0x800115 <steps+0x1> 2
284: 40 93 14 01 sts 0x0114, r20 ; 0x800114 <steps> 2
288: c0 90 10 01 lds r12, 0x0110 ; 0x800110 <__data_end> 2
28c: d0 90 11 01 lds r13, 0x0111 ; 0x800111 <__data_end+0x1> 2
290: e0 90 12 01 lds r14, 0x0112 ; 0x800112 <__data_end+0x2> 2
294: f0 90 13 01 lds r15, 0x0113 ; 0x800113 <__data_end+0x3> 2
298: 42 2f mov r20, r18 1
29a: 22 0f add r18, r18 1
29c: 55 0b sbc r21, r21 1
29e: 66 0b sbc r22, r22 1
2a0: 77 0b sbc r23, r23 1
2a2: 4c 0d add r20, r12 1
2a4: 5d 1d adc r21, r13 1
2a6: 6e 1d adc r22, r14 1
2a8: 7f 1d adc r23, r15 1
2aa: 40 93 10 01 sts 0x0110, r20 ; 0x800110 <__data_end> 2
2ae: 50 93 11 01 sts 0x0111, r21 ; 0x800111 <__data_end+0x1> 2
2b2: 60 93 12 01 sts 0x0112, r22 ; 0x800112 <__data_end+0x2> 2
2b6: 70 93 13 01 sts 0x0113, r23 ; 0x800113 <__data_end+0x3> 2
2ba: 95 95 asr r25 1
2bc: 87 95 ror r24 1
2be: 95 95 asr r25 1
2c0: 87 95 ror r24 1
2c2: 80 93 16 01 sts 0x0116, r24 ; 0x800116 <_ZZ4loopE12prev_encoder> 2
=========================================================================================
totale 56 cicli
Dai 19 cicli che avevi ipotizzato tu ai 56 che ne fa effettivamente c'e' una bella differenza (sempre al netto della chiamata all'interrupt, degli spostamenti di memoria, ecc...).
Proteus e' piu' pessimista perche' non fa nessuna ottimizzazione del codice (colpa mia, me lo segnalava ma l'ho sempre ignorato).
Ma e' abbastanza intuitivo che se fai a+b=c non puoi contare solo il tempo dell'operazione addizione (1 ciclo) ma dovrai anche contare il tempo speso per andare a cercare a e b e per andare a scrivere c.
Per quanto riguarda sapere scrivere compilatori o meno, ti rimando a quello che avevi scritto il 24-11 (cito: "Ricordo che i linguaggi compilati come il C spesso sono fuorvianti perché il program... ecc..."). Se tu sei convinto che la tua routine di interrupt dura 1us quando, in realta', ne dura quasi 4 rischi di incorrere in problemi (perdi passi e non sai perche').
Io, conscio di aver scritto una routine di interrupt lunga, mi son posto il problema e ho misurato (empiricamente se vuoi ma tenendomi un bel margine, ricordo che ho fatto la prova simulando 3600 giri e passo 3, cosa che, nella realta', non succedera' mai) se il mio programma riuscisse a fare tutto in tempo.
McMax: "Questi non sono i miei canoni. Sono la tua interpretazione dei miei canoni parafrasando ciò che ho scritto."
Io l'ho voluta fare volutamente esagerata. Visto che non ci leggono solo programmatori professionisti, il programmatore della domenica che c'e' in me (che penso sia la granparte di quelli che leggono) appena ha letto la tua spiegazione ha pensato: "oibo', quindi se faccio una routine di interrupt veloce sono a posto, il resto e' secondario".
Poi, il programmatore un pochettino piu' svegli che c'e' in me ha pensato: "aspetta un momento, se non genero i passi in tempo mi interessa relativamente di riuscire ad intercettare tutti gli impulsi dell'encoder".
Quindi verissimo che la routine di interrupt deve essere veloce pero' anche quello che ne consegue deve essere altrettanto veloce. Insomma, tra un impulso di encoder e l'altro, io devo essere in grado di stabilire se sparare il segnale di step e, se e' il caso, sparare il segnale di step corretto e tener traccia della posizione del carro prima che mi arrivi un altro impulso di encoder.
Su questo siamo d'accordo? Spero di si.
Quindi, fin'ora, tu hai parlato della velocita' di esecuzione dell'interrupt ma, a logica, dovremmo guardare anche la velocita' di esecuzione della routine di gestione dello stepper (vedi il mio esempio volutamente esagerato). La somma della durata di entrambe deve stare all'interno dell'intervallo tra due impulsi di encoder.
Altrimenti il programma non fara' quello per cui e' stato pensato.
Io ho messo tutto all'interno della routine di interrupt per comodita' e per avere tutto cio' che e' rilevante in un'unica funzione.
Avrei potuto dividere le due cose, lasciare nell'interrupt la lettura dell'encoder e in un altra funzione la gestione dello stepper. Sarebbe stato concettualmente piu' corretto? Sicuramente si. Sarebbe stato piu' efficiente? Probabilmente no.