McMax, grazie per i complimenti e mi fa piacere che tu non te la sia presa se faccio le pulci al tuo programma (rileggendo certi miei post potrebbe sembrare che vada a cercare i problemi nel tuo codice. Come dicevo, studiare il tuo codice e' stato molto istruttivo e visto che l'ho studiato abbastanza a fondo lo uso come riferimento per il mio... anche perche' altri programmi di els che funzionino e siano ben commentati come il tuo non ne ho trovati. Ci sarebbe il lavoro del russo ma e' difficile da seguire).
Neanch'io avrei mai pensato di andare a leggere il codice assembly ma altrimenti non ne uscivo... poi mi faccio prendere e non mi fermo piu'
come l'hai fornito l'ingresso encoder al micro ? Hai fisicamente fatto girare un encoder alla velocità voluta o hai generato gli impulsi esternamente ?Ho usato un generatore di funzioni per avere un'onda quadra alla frequenza voluta. Poi con due flipflop ho ottenuto le due onde quadre sfasate di 90° che do' in pasto al micro. Nel mezzo ho messo uno commutatore a due vie in modo da poter scambiare le due onde quadre e simulare la rotazione inversa.
Qua lo schema:
https://www.pjrc.com/teensy/td_libs_Encoder.html- nella traccia relativa al mio codice che gira con impulsi a 25Khz vedo che il segnale "20 steps", che ritengo sia un contatore che incrementi tu (visto che io non lo ho previsto) ogni 20 passi. Ma se i 20 passi non li fa perché conta sbagliato, come fa quella variabile a cambiare di stato ?In realta' i "20 steps" dovevano essere 2000 steps (un giro completo, per verificare che il conteggio sia corretto). Pero' lo screenshot del grafico veniva troppo piccolo e non si vedevano bene gli impulsi (e non avevo voglia di contare fino a 2000 impulsi per vedere se i conti tornavano, sia nel mio che nel tuo
).
In allegato le sorgenti dei due programmi di test. Per semplicita' metto anche qua quello che fa nel main:
Codice:
if (step_flag)
{
step_flag=0;
PORTB^=_BV(0); // toggle on every enc step
if (steps>=20)
{
steps=0;
PORTB^=_BV(2);
}
}
Non sono stato preciso, il tuo programma conta giusto (forse, non sono stato a controllare tutti gli incrementi col simulatore). Il problema e' che a 25KHz hai un interrupt ogni 10uS. La tua isr (con la chiamata e l'uscita) impiega 9.625uS, quindi il programma esegue praticamente solo l'isr. Ogni tanto riesce ad eseguire un pezzo del main e va a cambiare lo stato dei pin 8 (quello che nel grafico e' segnato come enc_step) e pin10 (20 step).
- Sarebbe interessante vedere come conta corretto il mio ISR a frequenze più basse, se fai vedere solo dove conta sbagliato è difficile capire come hai predisposto il banco di testTu chiedi tutte le prove che vuoi e io le faccio. Tu impari qualcosa, io imparo qualcosa.
Il banco di test mi sembrava di averlo descritto nel post dove avevo allegato le immagini... vediamo se sto diventando vecchio prima del tempo... azz, si. Brutta cosa la vecchiaia a 40 anni ancora da fare
Dunque, ricapitolo come ho effettuato il test.
1. i programmi sono quelli in allegato, basta cambiare l'estensione in .ino per aprirli con arduino ide (i file .ino non me li lascia caricare).
2. ho caricato il programma (prima il mio e poi quello di McMax) su una scheda arduino uno originale, tramite arduino ide lasciando le ottimizzazioni del compilatore di default (impostate a 0s, un'ottimizzazione bilanciata tra velocita' e spazio).
3. genero un'onda quadra con un generatore victor vc2003 tramite l'uscita ttl
4. uso due flipflop per generare le due onde quadre sfasate di 90°, come da schema allegato (flipflop.jpg). Lo switch sw2 mi serve per scambiare le due onde quadre in modo da simulare un cambio di rotazione. Le uscite out1 e out2 andranno collegate alla scheda arduino (out1->pin2, out2->pin3 nel caso di McMax. out1->pin2, out2->pin9 nel mio caso).
Su out1 e out2 ho collegato un oscilloscopio leader 1100A in modo da verificare la frequenza e la bonta' delle due onde generate.
5. un analizzatore logico (saleae 8ch 24MHz) e' stato collegato ad out1, out2, pin8 e pin10 per ottenere i grafici.
Questo e' il setup. Se non sono stato chiaro, chiedete.
Dopo faccio la prova che mi ha chiesto McMax.
Il tuo interrupt in effetti, anche semplicemente contando le istruzioni, dura di più,No, qua ti sbagli. Leggendo il programma in c sembrerebbe che tu abbia ragione ma il codice assembly racconta un'altra storia.
La sola isr (quindi tolte la chiamata e l'uscita), nel tuo caso occupa 75 cicli. Nel mio caso ne occupa 68 incluso ingresso e uscita (e incluso il primo if che il compilatore ha segato via).
Nei post precedenti avevo sbagliato i conti, questi dovrebbero essere quelli corretti (portate pazienza, l'assembly e' ostico e mi perdo).
Se sono nel modo filettatura e il mandrino gira sempre nella stessa direzione l'isr in C si riduce a (ed e' il caso peggiore, quando ha effettuato un giro completo):
Codice:
ISR (INT0_vect)
{
Enc_state=DREAD_ENCB;
if (!flag_menu)
else
{
tick=1; // ho un impulso di encoder
if (Enc_state) // leggo il canale b, se basso il mandrino gira in senso antiorario
{
Enc_pos++; // incremento il conteggio degli impulsi
Spindle_dir=normale; // mandrino ruota antiorario
if (Enc_pos>=one_turn_mandrel_steps) // se ho fatto un giro completo
{
Enc_pos=0; // resetto il conteggio
if (passata) flag_step=1; // se sono in play (passata) parto con la filettatura.
}
}
}
}
tu però dici che il mio impiega più tempo perché ho usato la funzione attachInterrupt.... potresti spiegare meglio questa cosa? anche tu mi pare stia usando attachInterrupt... o forse non ho capito il senso di quello che stai dicendo.Andiamo con calma, io mi spiego da cani quindi se non e' chiaro dimmi.
E mettiti comodo che mi sa che e' lunga la storia...
La tua isr, gia' di suo, occupa piu' cicli di clock della mia. In particolar modo perdi tempo nel trattare absolute_encoder_steps.
Solo l'operazione di somma che fai su quel long ti occupa 25 cicli di clock (di cui 16 per caricare e scaricare i dati).
Oltre a questo, usi la funzione attachInterrupt che occupa altri 74 cicli facendo:
- scarica in memoria 13 registri (2 cicli l'uno sono 26 cicli), secondo me inutilmente
- scarica in memoria il registro r0, ci carica lo stato di SREG e lo salva in memoria (5 cicli in tutto), cosa giusta da fare
- carica due registri (4 cicli di clock), se non ho capito male contegono il nome della routine di interrupt che hai dato tramite la funzione attachInterrupt
- fa la chiamata alla routine (3 cicli)
- carica i 13 registri con i valori prelevati dalla memoria (26 cicli)
- ripristina il registro SREG (5 cicli)
- esce dalla routine di interrupt (4 cicli)
Nel mio programma, vado a chiamare l'isr direttamente (metto a 1 il bit INT0 nel registro EIMSK).
Si arrangia il compilatore a salvare in memoria solo i registri che usa e a salvare il registro SREG (nel mio caso salva e ripristina solo 4 registri piu' l'SREG).
E non dimenticare l'isr sull'overflow del timer0, che serve alla funzione millis(). Ogni millisecondo scatta l'interrupt che fa quanto segue (sembra poca cosa ma qua ci sono due long... L'isr dovrebbe durare 6uS ed e' quello che mi rovinava il conteggio):
Codice:
volatile unsigned long timer0_overflow_count = 0;
volatile unsigned long timer0_millis = 0;
static unsigned char timer0_fract = 0;
ISR(TIMER0_OVF_vect)
{
// copy these to local variables so they can be stored in registers
// (volatile variables must be read from memory on every access)
unsigned long m = timer0_millis;
unsigned char f = timer0_fract;
m += MILLIS_INC;
f += FRACT_INC;
if (f >= FRACT_MAX) {
f -= FRACT_MAX;
m += 1;
}
timer0_fract = f;
timer0_millis = m;
timer0_overflow_count++;
}