MECCANICA e DINTORNI

COSTRUIAMO LE NOSTRE CNC DIVERTENDOCI CON L'AIUTO DI TANTI PROFESSIONISTI ESPERTI
Oggi è sab apr 27, 2024 10:07

Tutti gli orari sono UTC +1 ora




Apri un nuovo argomento Rispondi all’argomento  [ 87 messaggi ]  Vai alla pagina Precedente  1, 2, 3, 4, 5, 6  Prossimo
Autore Messaggio
 Oggetto del messaggio: Re: "Nuova" ELS - Dubbi, chiarimenti, consigli
MessaggioInviato: dom gen 16, 2022 14:37 
Non connesso
FINALMENTE USO IL TORNIO

Iscritto il: mar nov 10, 2009 12:36
Messaggi: 564
Località: Udine
@ 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 :cool:
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).


Non hai i permessi necessari per visualizzare i file allegati in questo messaggio.


Top
 Profilo  
 
 Oggetto del messaggio: Re: "Nuova" ELS - Dubbi, chiarimenti, consigli
MessaggioInviato: lun gen 17, 2022 14:41 
Non connesso
FINALMENTE USO IL TORNIO

Iscritto il: mar nov 10, 2009 12:36
Messaggi: 564
Località: Udine
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 :ghgh:


Non hai i permessi necessari per visualizzare i file allegati in questo messaggio.


Top
 Profilo  
 
 Oggetto del messaggio: Re: "Nuova" ELS - Dubbi, chiarimenti, consigli
MessaggioInviato: lun gen 17, 2022 15:37 
Non connesso
TORNITORE E FRESATORE

Iscritto il: lun set 29, 2008 23:19
Messaggi: 1774
Località: Cologno Monzese
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

_________________
Ù.


Top
 Profilo  
 
 Oggetto del messaggio: Re: "Nuova" ELS - Dubbi, chiarimenti, consigli
MessaggioInviato: lun gen 17, 2022 18:22 
Non connesso
FINALMENTE USO IL TORNIO

Iscritto il: mar nov 10, 2009 12:36
Messaggi: 564
Località: Udine
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'.


Top
 Profilo  
 
 Oggetto del messaggio: Re: "Nuova" ELS - Dubbi, chiarimenti, consigli
MessaggioInviato: lun gen 17, 2022 19:52 
Non connesso
TORNITORE E FRESATORE

Iscritto il: lun set 29, 2008 23:19
Messaggi: 1774
Località: Cologno Monzese
meglio così se hai risolto tutto!

_________________
Ù.


Top
 Profilo  
 
 Oggetto del messaggio: Re: "Nuova" ELS - Dubbi, chiarimenti, consigli
MessaggioInviato: lun gen 17, 2022 22:27 
Non connesso
CAPO OFFICINA
Avatar utente

Iscritto il: dom gen 31, 2010 21:46
Messaggi: 8850
Località: Bussero (MI)
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.

_________________
McMax

“None of us can change the things we’ve done. But we can all change what we do next.” – Fred Johnson

fulminato in tenera età


Top
 Profilo  
 
 Oggetto del messaggio: Re: "Nuova" ELS - Dubbi, chiarimenti, consigli
MessaggioInviato: mar gen 18, 2022 03:25 
Non connesso
FINALMENTE USO IL TORNIO

Iscritto il: mar nov 10, 2009 12:36
Messaggi: 564
Località: Udine
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' :grin:

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 :roll: ).
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 :cry:
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++;
}


Non hai i permessi necessari per visualizzare i file allegati in questo messaggio.


Top
 Profilo  
 
 Oggetto del messaggio: Re: "Nuova" ELS - Dubbi, chiarimenti, consigli
MessaggioInviato: mar gen 18, 2022 04:06 
Non connesso
FINALMENTE USO IL TORNIO

Iscritto il: mar nov 10, 2009 12:36
Messaggi: 564
Località: Udine
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).


Non hai i permessi necessari per visualizzare i file allegati in questo messaggio.


Top
 Profilo  
 
 Oggetto del messaggio: Re: "Nuova" ELS - Dubbi, chiarimenti, consigli
MessaggioInviato: mar gen 18, 2022 04:33 
Non connesso
FINALMENTE USO IL TORNIO

Iscritto il: mar nov 10, 2009 12:36
Messaggi: 564
Località: Udine
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);
      }
   }
}




Non hai i permessi necessari per visualizzare i file allegati in questo messaggio.


Top
 Profilo  
 
 Oggetto del messaggio: Re: "Nuova" ELS - Dubbi, chiarimenti, consigli
MessaggioInviato: mar gen 18, 2022 08:24 
Non connesso
CAPO OFFICINA
Avatar utente

Iscritto il: lun feb 29, 2016 11:29
Messaggi: 13616
Località: Ustica & Dintorni saltuariamente Bologna o Pesaro
Matteou , che versione di proton stai usando per le simulazioni ?

_________________
Gli errori sono per i principianti, noi esperti puntiamo al disastro !!!
Le conoscenze acquisite, sono proporzionali al DANNO PRODOTTO !!! ( esperienza personale...)
youtube



Immagine 2°socio TIRATOSAURO CLUB ITALIAN


Top
 Profilo  
 
 Oggetto del messaggio: Re: "Nuova" ELS - Dubbi, chiarimenti, consigli
MessaggioInviato: mar gen 18, 2022 08:49 
Non connesso
CAPO OFFICINA
Avatar utente

Iscritto il: dom gen 31, 2010 21:46
Messaggi: 8850
Località: Bussero (MI)
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 :risatina:... 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!

_________________
McMax

“None of us can change the things we’ve done. But we can all change what we do next.” – Fred Johnson

fulminato in tenera età


Top
 Profilo  
 
 Oggetto del messaggio: Re: "Nuova" ELS - Dubbi, chiarimenti, consigli
MessaggioInviato: mar gen 18, 2022 12:25 
Non connesso
FINALMENTE USO IL TORNIO

Iscritto il: mar nov 10, 2009 12:36
Messaggi: 564
Località: Udine
@ 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 :risatina:... 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.


Top
 Profilo  
 
 Oggetto del messaggio: Re: "Nuova" ELS - Dubbi, chiarimenti, consigli
MessaggioInviato: mar gen 18, 2022 14:47 
Non connesso
CAPO OFFICINA
Avatar utente

Iscritto il: lun feb 29, 2016 11:29
Messaggi: 13616
Località: Ustica & Dintorni saltuariamente Bologna o Pesaro
grazie, io sono fermo alla versione 7 e qualcosa...

_________________
Gli errori sono per i principianti, noi esperti puntiamo al disastro !!!
Le conoscenze acquisite, sono proporzionali al DANNO PRODOTTO !!! ( esperienza personale...)
youtube



Immagine 2°socio TIRATOSAURO CLUB ITALIAN


Top
 Profilo  
 
 Oggetto del messaggio: Re: "Nuova" ELS - Dubbi, chiarimenti, consigli
MessaggioInviato: mar gen 18, 2022 17:05 
Non connesso
TORNITORE E FRESATORE

Iscritto il: lun set 29, 2008 23:19
Messaggi: 1774
Località: Cologno Monzese
Avide tu vai a carbone...

_________________
Ù.


Top
 Profilo  
 
 Oggetto del messaggio: Re: "Nuova" ELS - Dubbi, chiarimenti, consigli
MessaggioInviato: mar gen 18, 2022 18:03 
Non connesso
FINALMENTE USO IL TORNIO

Iscritto il: mar nov 10, 2009 12:36
Messaggi: 564
Località: Udine
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 :twisted:
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.


Non hai i permessi necessari per visualizzare i file allegati in questo messaggio.


Top
 Profilo  
 
Visualizza ultimi messaggi:  Ordina per  
Apri un nuovo argomento Rispondi all’argomento  [ 87 messaggi ]  Vai alla pagina Precedente  1, 2, 3, 4, 5, 6  Prossimo

Tutti gli orari sono UTC +1 ora


Chi c’è in linea

Visitano il forum: Nessuno e 35 ospiti


Non puoi aprire nuovi argomenti
Non puoi rispondere negli argomenti
Non puoi modificare i tuoi messaggi
Non puoi cancellare i tuoi messaggi
Non puoi inviare allegati

Cerca per:
Vai a:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Traduzione Italiana phpBB.it