MECCANICA e DINTORNI http://meccanicaedintorni.morpel.it/phpbb/ |
|
"Nuova" ELS - Dubbi, chiarimenti, consigli http://meccanicaedintorni.morpel.it/phpbb/viewtopic.php?f=16&t=29864 |
Pagina 5 di 6 |
Autore: | matteou [ dom gen 16, 2022 14:37 ] |
Oggetto del messaggio: | Re: "Nuova" ELS - Dubbi, chiarimenti, consigli |
@ Umbez, posso essere d'accordo col tuo discorso ma solo se son sicuro che sia colpa del micro e non del mio programma. Se e' il programma ad essere scritto male, mi sembra una porcata usare un micro piu' potente per "nascondere" le pecche di un codice mal scritto. Poi aggiungi che lo faccio per imparare e per divertimento (altrimenti avrei usato il programma di mimoletti o quello di McMax che, seppur con cose che non mi piacciono, funzionano bene entrambi) quindi tutta conoscenza in piu' per imparare a scrivere un codice efficiente. Ho fatto un po' di prove e sono riuscito a risolvere il problema. Ho rispolverato il buon vecchio analizzatore logico per testare su un arduino uno vero e non solo al simulatore. Risolto il mio problema, ho voluto testare la mia isr e quella di McMax, le riporto qua sotto: Codice: ISR McMax: { static byte prev_encoder; // this variable stores the value of the input port register of the previous encoder state byte port; // this variable stores the value of the input port register of the current encoder state port = PIND & B00001100; //reads input pins 2 and 3 of the Arduino uno PORTD D and puts the binary image on the "port" byte variable port |= prev_encoder; //makes the logic "OR" between the current "port" byte and the previous steps = steps + encoder[port]; //increment/decrement the encoder couter according to the previous and current encoder values absolute_encoder_steps = absolute_encoder_steps + encoder[port]; //increment the absolute encoder counter port >>= 2; //shift the port image by 2 bits on right prev_encoder = port; //assign the current port image to the "prev_encoder" byte by keeping just the bits n. 2 and 3 step_flag = true; } //END of the Interrupt Service Routine ISR io: { Enc_state=DREAD_ENCB; if (!flag_menu) { Enc++; // incremento il contatore degli impulsi encoder if (Enc==one_turn_mandrel_steps) // se il mandrino ha fatto un giro competo, 2000 impulsi encoder { Enc=0; // resetto il contatore degli impulsi tacho++; //incremento la variabile per il calcolo degli rpm } /***************************************************** valutare se portare fuori dall'interrupt ****************************************************/ if (RUN_feed) { Feed_Count++; // se l'avanzamento e' attivo conto gli impulsi di encoder per poter sparare lo step if (Feed_Count >= Feed_Divisor) // se ho raggiunto il valore di impulsi da attendere { Feed_Count=0; // resetto il contatore FireStep(); //sparo un impulso } } /******************************************************** ***********************************************************/ } 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. } } else // mandrino gira in senso orario { Spindle_dir=contrario; Enc_pos--; // diminuisco il conteggio degli impulsi if (Enc_pos<0) // se il conteggio e' <0 { Enc_pos=one_turn_mandrel_steps-1; // riparto col conteggio da 1999 if (passata) flag_step=1; // se sono in play parto } } } } In allegato le sorgenti complete (quasi complete-manca la funzione void setup(), per non svelare subito il mistero). Guardando le due routine di interrupt, a prima vista, sembrerebbe che quella di McMax sia piu' efficiente (piu' snella e veloce). La mia inutilmente pesante (pur avendola snellita rispetto alla prima versione). Ma le apparenze, a volte, ingannano La mia isr conta correttamente fino a 100KHz (oltre non ho provato), quella di McMax a 25KHz (la sua routine legge x4) conta sbagliato. Sempre in allegato le immagini prese dall'analizzatore logico. Faccio un po' il prezioso per stimolare la vostra curiosita' e magari insegnarvi qualcosa (visto che io ho imparato molto qua dentro cerco di restituire il piacere). |
Autore: | matteou [ lun gen 17, 2022 14:41 ] |
Oggetto del messaggio: | Re: "Nuova" ELS - Dubbi, chiarimenti, consigli |
In allegato i codici assembly dei due programmi. Nel mio, il compilatore ha segato l'if all'interno dell'irs perche' sarebbe stato sempre vero (quindi ha lasciato solo l'else). In totale, nel mio caso, sono 53 cicli di clock (contando anche l'if tagliato sarebbero 58), in quello di McMax sono 154. In entrambi i casi ho conteggiato anche la chiamata e l'uscita (con i relativi carichi e scarichi dei registri). Da dove viene la differenza? Principalmente dal fatto che McMax ha usato la funziona di arduino attachInterrupt() per chiamare l'isr. Qua la definizione di attachInterrupt(): https://bit.ly/interruptarduino e di seguito il codice assembly: Codice: 238: 1f 92 push r1 23a: 0f 92 push r0 23c: 0f b6 in r0, 0x3f ; 63 23e: 0f 92 push r0 240: 11 24 eor r1, r1 242: 2f 93 push r18 244: 3f 93 push r19 246: 4f 93 push r20 248: 5f 93 push r21 24a: 6f 93 push r22 24c: 7f 93 push r23 24e: 8f 93 push r24 250: 9f 93 push r25 252: af 93 push r26 254: bf 93 push r27 256: ef 93 push r30 258: ff 93 push r31 25a: e0 91 02 01 lds r30, 0x0102 ; 0x800102 <__data_start+0x2> 25e: f0 91 03 01 lds r31, 0x0103 ; 0x800103 <__data_start+0x3> 262: 09 95 icall 264: ff 91 pop r31 266: ef 91 pop r30 268: bf 91 pop r27 26a: af 91 pop r26 26c: 9f 91 pop r25 26e: 8f 91 pop r24 270: 7f 91 pop r23 272: 6f 91 pop r22 274: 5f 91 pop r21 276: 4f 91 pop r20 278: 3f 91 pop r19 27a: 2f 91 pop r18 27c: 0f 90 pop r0 27e: 0f be out 0x3f, r0 ; 63 280: 0f 90 pop r0 282: 1f 90 pop r1 284: 18 95 reti Qua si va un po' oltre le mie conoscenze, ma provo a spiegare quello che ho capito. L'indirizzo 0x3f e' l'indirizzo del registro SREG, quindi giustamente, salva lo stato di SREG per ripristinarlo alla fine (comandi in r0, 0x3f e out 0x3f, r0). Poi cancella il contenuto di r1 facendo lo xor con se stesso (da una lettura veloce sembra che sia una specie di convenzione ma potrei sbagliarmi). Salva i registri dal 18 al 31 e carica due byte nei registri r30 e r31 (non ho idea perche' lo faccia, a nasometro direi che ha a che fare con l'array intFunc[] che dovrebbe contenere il nome dato all'isr). E dopo 34 cicli di clock finalmente carica la routine di interrupt (icall che occupa altri 3 cicli). Il resto dopo che ogni tanto tocca anche lavorare |
Autore: | umbez [ lun gen 17, 2022 15:37 ] |
Oggetto del messaggio: | Re: "Nuova" ELS - Dubbi, chiarimenti, consigli |
matteou : hai scoperto il context switch? SREG è lo status register, in arduino viene eseguito un interrupt alla volta quindi vengono messi in coda gli altri. quindi prima salva lo status di sreg in una variabile di appoggio, disabilita le interruzioni esegue la isr e poi riporta tutto come prima con sreg=variabile di appoggio. ma non capisco il punto di tutto questo?? Max ha usato gli interrupt hw per garantire che gli stati dell'encoder siano sempre tracciati, e questo è coerente con la sua scelta implementativa di un sistema real-time. Nel mio post precedente non ti ho messo in discussione il tuo codice, che è tuo e devi valutare tu i miglioramenti, ti ho chiesto se stavi facendo tutto questo perchè necessitavi di un interrupt hw in più... se necessiti di più interrupt hw meglio passare su una piattaforma che te li fornisce, senza complicare il codice perchè non ha senso alzare la complessità lato sw. tutto qui |
Autore: | matteou [ lun gen 17, 2022 18:22 ] |
Oggetto del messaggio: | Re: "Nuova" ELS - Dubbi, chiarimenti, consigli |
Non ho scoperto il context switch ma ho scoperto come viene fatto nel c di arduino e come poterlo fare in modo piu' efficace. Per capirsi, questo e' il codice assembly della chiamata all'isr del mio programma: Codice: 124: 1f 92 push r1 126: 0f 92 push r0 128: 0f b6 in r0, 0x3f ; 63 12a: 0f 92 push r0 12c: 11 24 eor r1, r1 12e: 2f 93 push r18 130: 3f 93 push r19 132: 8f 93 push r24 134: 9f 93 push r25 14 cicli di clock. Sono 23 cicli in meno rispetto a quello di McMax (a proposito, uso McMax come riferimento non per denigrare il suo lavoro, tutto l'opposto. Visto che il suo programma viene usato da molti e funziona, uso McMax come benchmark. Se riesco a fare meglio del suo programma vuol dire che il mio funzionera' altrettanto bene. Se faccio peggio vuol dire che posso ancora migliorare. E, ripeto, lo sto facendo per hobby-soddisfazione personale-esperienza-divertimento. Altrimenti avrei continuato a usare l'els di mimoletti che era gia' montata sul tornio da diversi anni e il suo lavoro lo faceva). Perche' devo sprecare 23 cicli in piu' per fare lo stesso lavoro? Per quanto riguarda il resto, parto dal fondo del tuo post e vado a ritroso, cosi' ti sara' chiaro il perche' di tutta questa analisi. Non mi servono ulteriori interrupt hw. A me interessa avere un canale sotto interrupt, l'altro non ha senso che sia sotto interrupt. Anche fossero tutti e due sotto interrupt, non avrei risolto il mio problema. Avrei dovuto, in ogni caso, andare a leggere il canale che non ha fatto scattare l'interrupt per capire da che parte sto girando. Solo che, mettendo entrambi sotto interrupt, avrei raddoppiato la risoluzione e la frequenza con cui scattano gli interrupt peggiorando il problema. Avrei potuto fare come McMax, cioe' andare a leggere entrambi i canali all'interno dell'isr. Giacche' proprio stupidino non sono ci avevo provato senza successo (se hai pazienza di leggere fino in fondo ti sara' chiaro perche' non risolveva il problema). Per ultimo, non e' vero che mettendo entrambi i canali sotto interrupt ho la garanzia che gli stati dell'encoder siano sempre tracciati. E' vero finche' la frequenza degli impulsi dell'encoder e' bassa da permettere il regolare funzionamento del programma. Se la frequenza si alza, nel mentre che eseguo una isr (e non ci sono solo le isr scritte dall'utente) posso avere n cambi di stato sui canali dell'encoder senza poterne tenere traccia. E arriviamo al nodo della questione e capirai perche' tutta questa analisi. Il mio programma contava sbagliato in modo ciclico (visto grazie al simulatore). Se fosse stato un errore del programma o il limite del micro, avrebbe contato sbagliato sempre. Quindi mi son chiesto cosa diavolo possa interferire, visto che nel mio programma (quello di test che avevo allegato) non c'era niente. Col simulatore e' stato facile scoprire il colpevole. Senza dire niente a nessuno, la sovrastruttura di arduino aggiunge un bel po' di cose al programma scritto dall'utente (basta compilare uno sketch vuoto e vedere quanto occupa). Tra queste, quella che turbava il mio programma era l'isr associata all'overflow del timer0 che viene usata per la funzione millis(). Ogni millisecondo veniva chiamata l'isr che ha una durata di 6uS (se ricordo bene) e li' mi e' stato d'aiuto il codice disassemblato per non stare a cercare le definizioni disperse nei vari file di arduino. Ovviamente il mio interrupt avveniva durante l'esecuzione dell'isr collegata al timer0. La mia isr veniva accodata. Al momento di eseguirla, i canali dell'encoder potevano aver cambiato di stato portandomi ad un conteggio errato. Ed ecco il perche' di questa analisi. Per capire a fondo quello che sta facendo il micro. Poi gia' che avevo il codice assembly in mano mi son chiesto il perche' di tutta quella manfrina per chiamare un interrupt. E questo solo per il discorso legato all'interrupt. Il resto del programma l'ho volutamente tralasciato dalla discussione altrimenti non ne usciamo piu'. |
Autore: | umbez [ lun gen 17, 2022 19:52 ] |
Oggetto del messaggio: | Re: "Nuova" ELS - Dubbi, chiarimenti, consigli |
meglio così se hai risolto tutto! |
Autore: | McMax [ lun gen 17, 2022 22:27 ] |
Oggetto del messaggio: | Re: "Nuova" ELS - Dubbi, chiarimenti, consigli |
Matteou, mi sembra un ottimo risultato, bravo. Io quando l'ho scritto non mi sono minimamente preoccupato di analizzare l'assembly e misurare effettivamente le durate, l'ho provato in modo empirico ed ho deciso che per l'applicazione poteva andare bene, e così in effetti è. Ora però mi hai messo la curiosità di approfondire, ed avrei qualche domanda: - come l'hai fornito l'ingresso encoder al micro ? Hai fisicamente fatto girare un encoder alla velocità voluta o hai generato gli impulsi esternamente ? - 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 ? - 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 test Ti dirò non sono sorpreso di scoprire che a 25Khz non ce la fa, in effetti io ho previsto velocità molto inferiori. Con encoder da 250 passi/giro a 25Khz stai andando a velocità che il mio tornio non si sogna di vedere mai. Il tuo interrupt in effetti, anche semplicemente contando le istruzioni, dura di più, e questo è certo: 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. |
Autore: | matteou [ mar gen 18, 2022 03:25 ] |
Oggetto del messaggio: | Re: "Nuova" ELS - Dubbi, chiarimenti, consigli |
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 test Tu 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++; } |
Autore: | matteou [ mar gen 18, 2022 04:06 ] |
Oggetto del messaggio: | Re: "Nuova" ELS - Dubbi, chiarimenti, consigli |
Nel file hardware/arduino/avr/cores/arduino/wiring.c e' definita la routine di interrupt per l'ovf del timer0. Io ho aggiunto il classico PORTB^=_BV(3); per cambiare di stato il pin11 ogni volta che viene eseguita l'isr. In allegato l'immagine. Solito setup solo che adesso ho aggiunto un altro canale all'analizzatore logico. L'ho collegato al pin11 e l'ho chiamato timer0 ovf. Cambia di stato ogni millisecondo come deve fare. Il problema sorge quando scatta l'isr dell'ovf del timer0 e va a ritardare l'isr dell'encoder. Il segnale enc_step e' spaziato regolarmente salvo quando viene attivata l'isr del timer0. Siamo a 15KHz (circa 700rpm con encoder da 1200ppr). A 4KHz (200rpm sempre con encoder da 1200 passi) la isr di McMax conta perfettamente anche con l'intrusione dell'isr del timer0 (c'e' tempo sufficiente per fare tutto). |
Autore: | matteou [ mar gen 18, 2022 04:33 ] |
Oggetto del messaggio: | Re: "Nuova" ELS - Dubbi, chiarimenti, consigli |
Per conferma, ho provato a modificare il codice di prova di McMax, non usando attachinterrupt e spegnendo il timer0. Adesso conta perfettamente i 25KHz che prima lo mettevano in difficolta'. Questo il codice modificato: Codice: //variabili relative alla lettura dell'encoder
int steps = 0; //passi encoder relativi - variabile usata come appoggio in filettatura e avanzamento volatile long absolute_encoder_steps = 0; //passi encoder assoluti boolean step_flag; //Flag per detarminare se il passo encode avvenuto (usata nella rountine di interrupt di lettura dell'nencoder) int passi_sequenza = 0; char encoder[] = {0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1, 1, 0}; //arrray da 16 valori usato per "muovere" i passi enoder nella rountine interrupt di filettatura boolean sviluppo_filetto = true; //Flag che detrmina il verso id filettatura: TRUE = DESTRO; FALSE = SINISTRO //fine variabili relative alla lettura dell'encoder ISR (INT0_vect) { static byte prev_encoder; // this variable stores the value of the input port register of the previous encoder state byte port; // this variable stores the value of the input port register of the current encoder state port = PIND & B00001100; //reads input pins 2 and 3 of the Arduino uno PORTD D and puts the binary image on the "port" byte variable port |= prev_encoder; //makes the logic "OR" between the current "port" byte and the previous steps = steps + encoder[port]; //increment/decrement the encoder couter according to the previous and current encoder values absolute_encoder_steps = absolute_encoder_steps + encoder[port]; //increment the absolute encoder counter port >>= 2; //shift the port image by 2 bits on right prev_encoder = port; //assign the current port image to the "prev_encoder" byte by keeping just the bits n. 2 and 3 step_flag = true; } //END of the Interrupt Service Routine ISR(INT1_vect, ISR_ALIASOF(INT0_vect)); // int1_vect=int0_vect void setup() { pinMode(8,OUTPUT); pinMode(10, OUTPUT); cli(); TCCR0A=0; TCCR0B=0; EICRA=_BV(ISC10) | _BV(ISC00); // on change edge EIMSK=_BV(INT0) | _BV(INT1); // enable int0 and int1 sei(); } void loop () { if (step_flag) { step_flag=0; PORTB^=_BV(0); // toggle on every enc step if (steps>=20) { steps=0; PORTB^=_BV(2); } } } |
Autore: | Davide Resca [ mar gen 18, 2022 08:24 ] |
Oggetto del messaggio: | Re: "Nuova" ELS - Dubbi, chiarimenti, consigli |
Matteou , che versione di proton stai usando per le simulazioni ? |
Autore: | McMax [ mar gen 18, 2022 08:49 ] |
Oggetto del messaggio: | Re: "Nuova" ELS - Dubbi, chiarimenti, consigli |
Matteo, innanzi tutto grazie per il tempo che ci hai dedicato e per il lavoro che hai fatto. Molto interessante questa cosa della gestione dell'interrupt senza usare la attachInterrupt ma attivandolo sui registri, non avevo pensato che potessero esserci delle differenze tra i due modi. Anche la questione millis() sapevo che attivava un interruppt sul timer0 ma non pensavo (stupidamente) che potesse far perdere così tanto tempo. Direi che si può tranquillamente disabilitare visto che mentre filetta (e anche mentre avanza) non ci serve. "absolute_encoder_steps" in effetti potrebbe anche non servirci, non ricordo perché avevo previsto una variabile che tenesse un conto assoluto. Si potrebbe risolvere con una sorta di variabile index che si incrementa ogni giro completo di encoder, è da pensare. A questo punto hai fatto 30 ... prova a vedere qual'è il limite della mia ISR con l'ultima configurazione che hai fatto, sia lasciando l'incremento di absolute_encoder_steps che provando a toglierlo. Grazie! |
Autore: | matteou [ mar gen 18, 2022 12:25 ] |
Oggetto del messaggio: | Re: "Nuova" ELS - Dubbi, chiarimenti, consigli |
@ Davide: uso proteus 8.11 (e grazie a mimoletti che me lo ha fatto conoscere. Mi ci e' voluto un po' per usarlo come debugger perche' non avevo capito le potenzialita' ma e' una figata e ti fa capire che un debugger hw sarebbe tanto tanto utile. Comunque e' molto sincero, ad esempio nel mio programma originale modificavo un long nell'isr e la leggevo nel main con risultati, a volte, imprevedibili. Proteus ha simulato correttamente anche quel problema. @McMax: Molto interessante questa cosa della gestione dell'interrupt senza usare la attachInterrupt ma attivandolo sui registri, non avevo pensato che potessero esserci delle differenze tra i due modi. Idem. Pensavo fosse una cosa fatta una tantum (un po' come pinMode che occupa parecchi cicli ma una volta sola e all'inizio del programma) invece lo fa ogni chiamata all'isr ed e' seccante. Anche la questione millis() sapevo che attivava un interruppt sul timer0 ma non pensavo (stupidamente) che potesse far perdere così tanto tempo. Direi che si può tranquillamente disabilitare visto che mentre filetta (e anche mentre avanza) non ci serve. Io (ancora piu' stupidamente) pensavo venisse lanciato l'isr solo quando veniva usato millis(). Ovviamente la funzione delay() si appoggia a millis(). Pero' io ho spento il timer0 all'inizio e lo tengo spento per tutto il programma (e, adesso che ci penso, si e' liberato un timer per poter gestire un eventuale secondo asse). Dove mi serviva fare un delay (l'ho usato come antirimbalzo dei poveri), ho usato la funzione _delay_ms() che, se non ricordo male, fa una serie di NOP (quindi non si appoggia a nessun timer). "absolute_encoder_steps" in effetti potrebbe anche non servirci, non ricordo perché avevo previsto una variabile che tenesse un conto assoluto. Si potrebbe risolvere con una sorta di variabile index che si incrementa ogni giro completo di encoder, è da pensare. Io ho azzerato (e non mi sembra una idea malvagia) il conteggio arrivato al giro intero. Alla fine non ci serve tener traccia di tutti gli steps, quando fa un giro completo si puo' anche resettare il conteggio e mettere un flag che indichi che ha fatto il giro completo. Anche tenere traccia di quanti giri completi ha compiuto non mi pare serva neanche a te. Poi la variabile steps perche' l'hai messa int? Ti converrebbe metterla char. Tanto la resetti ogni volta che hai fatto un passo di stepper e difficilmente dovrai aspettare piu' di 127 passi encoder per fare un passo stepper. A questo punto hai fatto 30 ... prova a vedere qual'è il limite della mia ISR con l'ultima configurazione che hai fatto, sia lasciando l'incremento di absolute_encoder_steps che provando a toglierlo. Con le modifiche (tolto attachinterrupt e spento il timer0) l'isr e' al limite a 25KHz (potrebbe salire ancora un pochino ma poi eseguirebbe solo l'isr). Comunque 25KHz a 1200ppr sono 1250rpm che non e' male, ben oltre quello che serve (inutile andare piu' su che poi il limite diventa lo stepper. Nel mio caso, oltre 1m/min non fa. Con vite passo 3, posso filettare al massimo a 780rpm passo 1.5 e probabilmente sono molto vicino al limite dello stepper. Cambiando absolure_encoder_steps da long a int, guadagni qualcosina ma non tanto come pensavo. Dopo (stasera) approfondisco il discorso. |
Autore: | Davide Resca [ mar gen 18, 2022 14:47 ] |
Oggetto del messaggio: | Re: "Nuova" ELS - Dubbi, chiarimenti, consigli |
grazie, io sono fermo alla versione 7 e qualcosa... |
Autore: | umbez [ mar gen 18, 2022 17:05 ] |
Oggetto del messaggio: | Re: "Nuova" ELS - Dubbi, chiarimenti, consigli |
Avide tu vai a carbone... |
Autore: | matteou [ mar gen 18, 2022 18:03 ] |
Oggetto del messaggio: | Re: "Nuova" ELS - Dubbi, chiarimenti, consigli |
Metto un po' di numeri. I cicli di clock che ho contato dovrebbero essere giusti e includono entrata e uscita dall'isr. 1. isr originale con attachinterrupt: 150 cicli - 9.375uS 2. isr originale senza attachinterrupt: 127 cicli - 7.937uS 3. isr senza attachinterrupt, con le variabili modificate: 102 cicli - 6.375uS questo il codice modificato, steps diventa un char e il long diventa un int, col reset al giro completo (ho reso globali port e prev_encoder perche', in teoria, si dovrebbe guadagnare qualcosa in velocita'): Codice: //variabili relative alla lettura dell'encoder [color=#0000FF]volatile char steps = 0;[/color] //passi encoder relativi - variabile usata come appoggio in filettatura e avanzamento [color=#0000FF]int absolute_encoder_steps = 0; //passi encoder assoluti[/color] volatile boolean step_flag=0; //Flag per detarminare se il passo encode avvenuto (usata nella rountine di interrupt di lettura dell'nencoder) int passi_sequenza = 0; char encoder[] = {0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1, 1, 0}; //arrray da 16 valori usato per "muovere" i passi enoder nella rountine interrupt di filettatura boolean sviluppo_filetto = true; //Flag che detrmina il verso id filettatura: TRUE = DESTRO; FALSE = SINISTRO //fine variabili relative alla lettura dell'encoder #define one_turn_mandrel_steps 1200 byte prev_encoder=0; // this variable stores the value of the input port register of the previous encoder state byte port=0; // this variable stores the value of the input port register of the current encoder state [color=#0000FF]volatile bool one_turn=0;[/color] ISR (INT0_vect) { port = PIND & B00001100; //reads input pins 2 and 3 of the Arduino uno PORTD D and puts the binary image on the "port" byte variable port |= prev_encoder; //makes the logic "OR" between the current "port" byte and the previous steps = steps + encoder[port]; //increment/decrement the encoder couter according to the previous and current encoder values absolute_encoder_steps = absolute_encoder_steps + encoder[port]; //increment the absolute encoder counter [color=#0000FF] if (absolute_encoder_steps==one_turn_mandrel_steps) { absolute_encoder_steps=0; one_turn=1; } [/color] prev_encoder = (port>>2); //assign the current port image to the "prev_encoder" byte by keeping just the bits n. 2 and 3 step_flag = true; } //END of the Interrupt Service Routine ISR(INT1_vect, ISR_ALIASOF(INT0_vect)); // int1_vect=int0_vect void setup() { pinMode(8,OUTPUT); pinMode(10, OUTPUT); cli(); TCCR0A=0; TCCR0B=0; EICRA=_BV(ISC10) | _BV(ISC00); // on change edge EIMSK=_BV(INT0) | _BV(INT1); // enable int0 and int1 sei(); } void loop () { if (step_flag) { step_flag=0; PORTB^=_BV(0); // toggle on every enc step if (steps>=20) { steps=0; PORTB^=_BV(2); } } } 4. isr modificata, usando il concetto di McMax (quello che ha messo nell'array) ma direttamente nell'isr con una serie di if. In questo caso, si hanno: 77 cicli - 4.812uS nel caso peggiore (dove l'espressione vera e' l'ultima dell'ultimo if, quindi deve passarsi tutte le condizioni) 56 cicli - 3.5uS nel caso migliore (dove l'espressione vera e' la prima del primo if, quindi salta tutti gli altri controlli). Questo il codice, mi son basato sulla tabella che c'e' all'inizio e che replica la sequenza di McMax: Codice: // new new old old // pin2 pin1 pin2 pin1 Result // ---- ---- ---- ---- ------ // 0 0 0 0 no movement // 0 0 0 1 +1 // 0 0 1 0 -1 // 0 1 0 0 -1 // 0 1 0 1 no movement // 0 1 1 1 +1 // 1 0 0 0 +1 // 1 0 1 0 no movement // 1 0 1 1 -1 // 1 1 0 1 -1 // 1 1 1 0 +1 // 1 1 1 1 no movement //variabili relative alla lettura dell'encoder volatile char steps = 0; //passi encoder relativi - variabile usata come appoggio in filettatura e avanzamento volatile int absolute_encoder_steps = 0; //passi encoder assoluti volatile boolean step_flag=0; //Flag per detarminare se il passo encode avvenuto (usata nella rountine di interrupt di lettura dell'nencoder) int passi_sequenza = 0; boolean sviluppo_filetto = true; //Flag che detrmina il verso id filettatura: TRUE = DESTRO; FALSE = SINISTRO //fine variabili relative alla lettura dell'encoder #define one_turn_mandrel_steps 1200 byte port_old=0; // this variable stores the value of the input port register of the previous encoder state byte port=0; // this variable stores the value of the input port register of the current encoder state volatile bool one_turn=0; char inc=0; ISR (INT0_vect) { port = (PIND & B00001100); //reads input pins 2 and 3 of the Arduino uno PORTD D and puts the binary image on the "port" byte variable if (((port|port_old)==0) || ((port|port_old)==0b0101) || ((port|port_old)==0b1010) || ((port|port_old)==0b1111)) inc=0; else if (((port|port_old)==0b1) || ((port|port_old)==0b0111) || ((port|port_old)==0b1000) || ((port|port_old)==0b1110)) inc=1; else if (((port|port_old)==0b0010) || ((port|port_old)==0b0100) || ((port|port_old)==0b1011) || ((port|port_old)==0b1101)) inc=-1; port_old=(port>>2); step_flag = true; } //END of the Interrupt Service Routine ISR(INT1_vect, ISR_ALIASOF(INT0_vect)); // int1_vect=int0_vect void setup() { pinMode(8,OUTPUT); pinMode(10, OUTPUT); cli(); TCCR0A=0; TCCR0B=0; EICRA=_BV(ISC10) | _BV(ISC00); // on change edge EIMSK=_BV(INT0) | _BV(INT1); // enable int0 and int1 sei(); } void loop () { if (step_flag) { step_flag=0; steps+=inc; PORTB^=_BV(0); // toggle on every enc step if (steps>=20) { steps=0; PORTB^=_BV(2); } } } Due parole sul mio programma. Dopo aver fatto questi ragionamenti e aver modificato il programma, lo provo su proteus. Continua a contare sbagliato Alche', ad un passo dal lanciare tutto alle ortiche, mi leggo l'assembly del codice completo (fino a quel momento mi ero concentrato solo sull'isr) scoprendo che c'e' la libreria del display (collegato tramite i2c) che va a raccogliere i fiori mentre io dovrei filettare (durante la filettatura non vado a modificare il display, lo modifico solo quando comincia il ritorno e quando riparto con la filettatura. In c sono due righe di codice, lcd.setCursor() e lcd.print() ma la libreria ci mette una vita). Ho risolto il problema brutalmente, quando inizio la filettatura, prima di attivare l'interrupt, spengo il bus i2c e lo riattivo solo a filettatura finita (dopo aver spento l'interrupt). Riprovando sembrerebbe che la libreria non intervenga quando non ci sono comandi ma nel dubbio meglio spegnere tutto. Per sapere in che stato sono (play-pausa-return) ho previsto, nel programma, l'accensione di tre led corrispondenti ai tre stati. Nella realta' non si sente la necessita' (tanto si guarda il carro e non il display che resta congelato fino a fine filettatura). In allegato tre prove del mio programma. Fatto una decina di passate ogni prova, sempre pelando un decimo. Passo 1 e 1.5 a 800rpm. Passo 3 a 330rpm. Il limite e' il motore stepper, non ce la fa a girare piu' veloce. |
Pagina 5 di 6 | Tutti gli orari sono UTC +1 ora |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |