Leggere bene tre encoder con Arduino UNO

Sezione dedicata all'elettronica di controllo cnc.
Rispondi
tecno67
Member
Member
Messaggi: 391
Iscritto il: lunedì 26 febbraio 2007, 14:25
Località: Prov. di Milano

Leggere bene tre encoder con Arduino UNO

Messaggio da tecno67 » martedì 12 maggio 2020, 13:30

Smentendo in parte ciò che sostenevo in un mio post precedente, Vi invio questo sketch scritto per Arduino UNO con una funzione scritta in Assembly che esegue la lettura continua di tre encoder collegati ai pin da A0 ad A5 sotto forma di 3 variabili long a 32bit e li trasmette al PC tramite la porta USB in modo che possano essere visualizzati tramite il monitor seriale dell'IDE di arduino.
triplo_encoder_HFx4.rar
L'ho testato positivamente con degli encoder Hohner NPN da 500 Imp/giro a 5Vdc, ma Volendo si possono usare anche encoder a 12V o 24 Volt sempre NPN o Push-Pull. In questo caso si deve inserire in serie su ciascun ingresso un diodo col catodo rivolto verso l'ingresso e l'anodo verso l'encoder e fornire una alimentazione separata. (in pratica l'encoder può solo abbassare l'ingresso (che è di tipo PULL-UP) a 0V, ma eventuali tensioni superiori non possono entrare e danneggiare l'Arduino - Occhio a non sbagliare il verso!

Ho cercato di commentarlo il più possibile (al limite dell'irritante per chi ne sa qualcosa di più) per renderlo quanto più comprensibile ed utilizzabile anche a chi non conosce l'assembly ed i registri del processore. In particolare ho spiegato cosa modificare volendo collegare gli encoder ad altri ingressi.
Il suo limite è che gli encoder devono necessariamente essere connessi su due pin adiacenti es. A0 ed A1 per l'encoder1, A2 ed A3 per l'encoder2, A4 ed A5 per l'encoder3.
Quello che mi piace molto è il sistema usato per decodificare le transizioni di stato sugli ingressi, dato che la cosa viene fatta con una sola istruzione in contemporanea per tutti e tre gli encoder.
In fine listato ci sono alcune mie considerazioni circa la possibilità di modificarlo per arrivare a leggere fino a 9 encoder e circa la frequenza massima di lavoro che potrebbe avere, che mi farebbe piacere fossero confermate e/o confutate da altri utenti ferrati con Arduino.
Non hai i permessi necessari per visualizzare i file e le foto allegati in questo messaggio. Per visualizzare tali file devi registrarti ed effettuare il Login

tecno67
Member
Member
Messaggi: 391
Iscritto il: lunedì 26 febbraio 2007, 14:25
Località: Prov. di Milano

Re: Leggere bene tre encoder con Arduino UNO

Messaggio da tecno67 » giovedì 14 maggio 2020, 13:22

Mi sono esaltato! :) :)
Questa volta lo faccio proprio sudare il povero Arduino!

Provato, Funge...
Eccoti la lettura di 9 Encoder.
nove_encoder_HFx4_v1.rar
Gli unici Pin rimasti inutilizzati (si fa per dire perché leggo tramite il serial monitor) sono il pin 1 ed il 2.

Il prossimo passo come detto è integrarlo con la libreria simple Modbus Slave che ho già trovato, dopodiché con 20€ di arduino e uno shield RS485 ti faccio la lettura di 9 encoder da qualsiasi PLC col Modbus.

Quello che mi fa schifo invece è aver fatto una ASM() che sembra un poema.

Ho tentato di usare un array, scrivendo di volta in volta nell'Assembly il singolo elemento, ma non funge, o almeno non so come fare.
In particolare non mi riesce di capire la sintassi per aver i due byte che compongono l'indirizzo della variabile. Ora scrivo semplicemente il nome variabile ed il compilatore me lo sostituisce con l'indirizzo.
Come faccio a farglielo caricare ad es. nel registro Z? Di li poi non sarebbe poi un gran casino lavorare con indirizzamento indiretto e riuscire magari a spostare l'incremento ed il decremento in una routine unica da chiamare dopo aver caricato nel registro Z l'indirizzo della variabile che di volta in volta devo manipolare. Mi sono letto più volte https://www.nongnu.org/avr-libc/user-ma ... e_asm.html ma non mi riesce di capire.

Mi date una mano?
Non hai i permessi necessari per visualizzare i file e le foto allegati in questo messaggio. Per visualizzare tali file devi registrarti ed effettuare il Login

Avatar utente
hellfire39
God
God
Messaggi: 3416
Iscritto il: domenica 16 dicembre 2012, 9:04
Località: AN

Re: Leggere bene tre encoder con Arduino UNO

Messaggio da hellfire39 » giovedì 14 maggio 2020, 13:28

Evitare l'assembler e lasciare che il compilatore faccia il suo lavoro?

tecno67
Member
Member
Messaggi: 391
Iscritto il: lunedì 26 febbraio 2007, 14:25
Località: Prov. di Milano

Re: Leggere bene tre encoder con Arduino UNO

Messaggio da tecno67 » giovedì 14 maggio 2020, 13:57

Sono solo un trafficone! Lo so! :D :D

L'Assembler l'ho scomodato cercando la massima performance, se fossi riuscito a scriverlo altrettanto veloce senza usarlo, sarei stato felice.
Non ne sono stato capace. :(

Avatar utente
hellfire39
God
God
Messaggi: 3416
Iscritto il: domenica 16 dicembre 2012, 9:04
Località: AN

Re: Leggere bene tre encoder con Arduino UNO

Messaggio da hellfire39 » giovedì 14 maggio 2020, 16:41

Non sono molto d'accordo.
Penso che il compilatore sia abbastanza bravo a scrivere codice ottimizzato

Mplabx non è bravissimo a questo riguardo, perché le ottimizzazioni le hanno lasciate in gran parte nella versione a pagamento, ma questa è un'altra storia.

E' mia opinione personalissima che strizzare un limone secco non sia una scelta vincente. Io avrei percorso un'altra strada, cercando soluzioni hardware. Me ne vengono in mente due:
- utilizzare dsPic33 che hanno fino a 2 QEI (quadrature encoder interface) interne
- utilizzare chip come l'Avago HTCL-2022, che è un QEI esterno

La sfiga del HTCL-2022 è che ha l'uscita parallela e quindi è necessario mettere in piedi un meccanismo di multiplexing tipo bus per accedere a più contatori.

Di contro, queste soluzioni non hanno limiti (ragionevoli) di frequenza.

La soluzione SW diventa problematica quando si supera una certa frequenza per cui il tempo passato in interrupt dal processore diventa rilevante rispetto al tempo totale (diciamo 10%? è un valore che dipende molto dal codice, ovviamente)

Inoltre, se qualche altra routine disattiva gli interrupt, magari perché deve rispettare tempistiche molto stringenti, si rischia di perdere conteggi, sfasando la macchina a stati.

---------------

Io, per il singolo encoder, ho utilizzato la seguente funzione (in questo caso leggevo un selettore, quindi: velocità prossima a zero).
Inoltre, leggevo l'encoder 1x e non 4x (vedi array enc_states commentato)

Codice: Seleziona tutto

int8_t read_encoder()
{
  //static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
  // 0111 -1 (7)
  // 0010 +1 (2)
  static int8_t unoper_states[] = {0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0};
  static uint8_t old_AB = 0;

  int p1 = digitalRead(rotEncPin1);
  int p2 = digitalRead(rotEncPin2);
  
  // stato precedente
  old_AB <<= 2;
  // stato attuale
  old_AB |= ( (p1 << 1) | p2 );
  return ( unoper_states[( old_AB & 0x0f )]);
}
Concentrandosi sui calcoli, il codice generato è il seguente:

Codice: Seleziona tutto

// stato precedente
  old_AB <<= 2;
    1b7e:	e0 91 3a 02 	lds	r30, 0x023A	; 0x80023a <_ZZ12read_encodervE6old_AB>
    1b82:	ee 0f       	add	r30, r30
    1b84:	ee 0f       	add	r30, r30
  // stato attuale
  old_AB |= ( (p1 << 1) | p2 );
    1b86:	e8 2b       	or	r30, r24
    1b88:	00 0f       	add	r16, r16
    1b8a:	11 1f       	adc	r17, r17
    1b8c:	e0 2b       	or	r30, r16
    1b8e:	e0 93 3a 02 	sts	0x023A, r30	; 0x80023a <_ZZ12read_encodervE6old_AB>
  return ( unoper_states[( old_AB & 0x0f )]);
    1b92:	ef 70       	andi	r30, 0x0F	; 15
    1b94:	f0 e0       	ldi	r31, 0x00	; 0
    1b96:	eb 5c       	subi	r30, 0xCB	; 203
    1b98:	fe 4f       	sbci	r31, 0xFE	; 254
  }
P.S. la funzione è chiamata una sola volta ed il compilatore l'ha messa in-line nel codice, se non ho visto male.
Sarebbe interessante verificare come viene compilato lo stesso codice, esteso a più encoder e con una lettura più efficiente degli ingressi.

tecno67
Member
Member
Messaggi: 391
Iscritto il: lunedì 26 febbraio 2007, 14:25
Località: Prov. di Milano

Re: Leggere bene tre encoder con Arduino UNO

Messaggio da tecno67 » giovedì 14 maggio 2020, 17:47

Premesso che ogni soluzione in HW è migliore ma anche che è sempre per puro divertimento che faccio queste cose e premesso anche che il mio stile di scrittura è quello di un praticone, quale in effetti sono, la particolarità della soluzione che ho proposto, sta in due cose essenzialmente:

1) La ISR (sempre quella) è attivata da un qualsiasi cambio di stato su un qualsiasi encoder. Di fatto poi faccio il polling di tutti gli encoder una volta entrato nella ISR, solo che questo non è scatenato ad intervalli di tempo fissi, ma appunto ad evento. Il fatto è che lavorando in questo modo se metti caso durante due interventi successivi della ISR cambiano anche altri encoder oltre a quello che ha originato per primo l'interrupt, io servo pure quelli.
Quindi per assurdo potrei arrivare a servire (nel caso più fortunato ovviamente) 9 encoder su 2 sole chiamate alla ISR. Paradossalmente la seconda potrebbe anche essere eseguita a vuoto solo perché era ormai pendente.
Se invece conta un encoder solo, il micro è poco oberato e c'è più tempo per il resto, ma comunque non perdo impulsi anche ad alte frequenze.
Tieni conto che i PCINT0,1 e 2 come priorita sono i primi dato che non uso Ext_Int.
Certo se nel resto del programma qualcuno scrive una ISR che dura più della mia, le prestazioni si riducono ad un valore che sarà 1/(tempo_altra_ISR +tempo_mia_ISR). Tra l'altro anche io maschero gli interrupt quando nel loop leggo i valori nell'Atomic_block, ma lo faccio per un tempo molto breve.

2) Essendo la discriminazione sul cambio degli stati fatta con una operazione di XOR su un intero byte, in pratica decodifico in parallelo 3 encoder alla volta. Teoricamente con questo meccanismo avendo 8 bit, ne potrei decodificare quattro, risparmiando pure la mascheratura.

Quello che allunga molto il codice, in effetti, sono le routine di incremento su 32bit, che in ogni caso però sono richiamate solo per gli encoder che hanno avuto un cambiamento e non per tutti gli encoder. (nell'esempio che hai postato tu viene gestito un counter a 8bit, se facessi lo stesso mi basterebbero 3 sole istruzioni assembly e forse potrei pure fare qualche salto in meno)

Comunque in definitiva la ISR impiega all'incirca un 140 cicli di clock fissi più 22 cicli per ogni encoder che cambia di stato. Quindi nella peggiore delle ipotesi dura poco meno di 350 cicli. Ovvero dato che ogni secondo ne ho a disposizione 16,000,000 se tutti e nove gli encoder contano alla massima frequenza il micro è saturato completamente ad una frequenza teorica superiore ai 45Khz, che per una soluzione software su 9 oggetti non mi sembra una cosa poi così misera.

Ovviamente io per primo non userei mai un aggeggio simile per compiti importanti.
Era solo un esercizio accademico per divertirsi, per quello che sto facendo ora, me ne bastano 3, ed i PORT B e D mi servono per altro, è solo che mi è presa la mano e volevo vedere dove arrivavo.

Avatar utente
hellfire39
God
God
Messaggi: 3416
Iscritto il: domenica 16 dicembre 2012, 9:04
Località: AN

Re: Leggere bene tre encoder con Arduino UNO

Messaggio da hellfire39 » giovedì 14 maggio 2020, 20:31

Quello che non sono riuscito a spiegare è che non puoi saturare il micro di interrupt: non riuscirebe a fare altro, oltre una certa percentuale di tempo, al processore non rimarrebbe tempo di fare nulla.
Oltre al fatto che alcune funzioni potrebbero essere compromesse.

Sarà che io ho una "tara" mentale ingegneristica, ma, in progettazione considero sempre il caso peggiore.
Che è quando tutti e nove gli encoder sono sfasati tra loro.

In tal caso, utilizzando i tuoi numeri, avresti 140+22 cicli per ogni encoder. Quindi circa 11.000 conteggi teorici al secondo per i nove encoder.
Mantenendo gli interrupt al 10% di tempo di occupazione della cpu siamo a circa 1000 conteggi al secondo.


-------------
P.S. Volendo proseguire per questa strada, sceglierei il Teensy 4, che è a 32 bit ed ha un clock sopra i 200MHz, se non ricordo male.

tecno67
Member
Member
Messaggi: 391
Iscritto il: lunedì 26 febbraio 2007, 14:25
Località: Prov. di Milano

Re: Leggere bene tre encoder con Arduino UNO

Messaggio da tecno67 » venerdì 15 maggio 2020, 8:48

In tal caso, utilizzando i tuoi numeri, avresti 140+22 cicli per ogni encoder. Quindi circa 11.000 conteggi teorici al secondo per i nove encoder.
Mantenendo gli interrupt al 10% di tempo di occupazione della cpu siamo a circa 1000 conteggi al secondo.


Matematicamente parlando non fa una piega quello che dici, ma se ad esempio dicessimo che ci prendiamo solo un rapporto 1/4 sul coefficiente di utilizzo delle risorse del micro già parleremmo di 4KHz comunque garantiti su tutti e 9 gli encoder anche nella peggiore condizione, peraltro statisticamente molto improbabile, ed in questo caso, dei 16Mcicli/sec avremmo comunque la garanzia di averne sempre a disposizione almeno 6,4Mcicli/sec per fare quello che vogliamo. In tutti gli altri casi di più. Non è una Ferrari, ma nemmeno un triciclo.

P.S. Volendo proseguire per questa strada, sceglierei il Teensy 4, che è a 32 bit ed ha un clock sopra i 200MHz, se non ricordo male.
Sono d'accordo, oltretutto essendo a 32bit, dopo aver trasferito le letture delle porte coinvolte, che saranno comunque ad 8 bit (immagino, non conosco la tensy) in una unica doppia-word, si potrebbe fare la decodifica contemporanea di fino a 16 encoder in un solo XOR a 32 bit ed anche le operazioni di incremento/decremento sarebbe eseguibile in una sola (caricamento, inc/dec e ripristino) 3 istruzioni contro le 12 che uso io su Arduino. In effetti 32/8Bit fa appunto 4 volte.
Quindi direi che la metodologia di lettura da me proposta è valida, se si devono leggere più encoder e sarebbe ancor più redditizia su di un micro a 32 bit.

Anche se non mi serve, posso dire di aver partorito un pensiero positivo in una notte primaverile col Coronavirus! :) :)

PS. Non l'ho ancora testato, ma ieri sera ho provato a modificare l'ASM che avevo scritto per counter a 32bit per elaborare solo counter a 8bit. Ne è venuta fuori una cosa da 171 cicli di clock fissi, sia che ci sia incremento/decremento o che non ci sia. Facendo un rapido calcolo 16000000/171=93567 se la si eseguisse a tempo con una frequenza di 10KHZ, non saremmo molto lontani da quel 10% di tempo massimo impiegato nelle interruzioni di cui parlavi tu. Ma stiamo sempre parlando di esercizi accademici, 9 counter ad 8bit probabilmente potrebbero servire a qualche robottinaro ma non per impieghi pratici.

Avatar utente
hellfire39
God
God
Messaggi: 3416
Iscritto il: domenica 16 dicembre 2012, 9:04
Località: AN

Re: Leggere bene tre encoder con Arduino UNO

Messaggio da hellfire39 » venerdì 15 maggio 2020, 10:53

Non mi sono spiegato molto bene.
Il problema non è solo del tempo che lasci, ma anche e soprattutto nella latenza del segnale. Spesso e volentieri, se leggi un encoder devi fare anche altre operazioni che hanno bisogno di risposte ad bassa latenza.
E questo diventa praticamente impossibile anche con una occupazione di cpu dovuta agli ISR medio bassa.

Quindi, funziona? si. D'altra parte esistono già librerie che fanno la stessa cosa, sebbene su singolo encoder.

Tanto per citarne due:
https://github.com/jnk0le/AVR-FAST-ENCODER
https://www.pjrc.com/teensy/td_libs_Encoder.html

Utile? Poco. Anche perché visto il basso costo dei processori, è irrazionale gestire N encoder/motori con un singolo micro per qualsiasi applicazione un po' spinta.

Attenzione alle frequenze: 1000 rpm = 16,67 giri/secondo
con un encoder da 1024 impulsi/giro e lettura della posizione 4x si hanno già più di 68266 interrupt al secondo

4000 interrupt/s con l'encoder a 1024 impulsi/giro e codifica 4x corrispondono a circa 1 giro/secondo, quindi 60 rpm

tecno67
Member
Member
Messaggi: 391
Iscritto il: lunedì 26 febbraio 2007, 14:25
Località: Prov. di Milano

Re: Leggere bene tre encoder con Arduino UNO

Messaggio da tecno67 » venerdì 15 maggio 2020, 14:50

Non c'è bisogno che ti tu ti riesca a spiegare, sono già cosciente dei limiti di ciò che sto facendo e che potrei fare. Non ho mai detto di voler usare un Arduino per fare un controllo di 9 encoder e relativi motori, che non so con quali pin potrei comandare, dato che a parte l'1 ed il 2 che sono impegnati dalla seriale non ne rimangono altri, non sarebbe ne una cosa saggia, ne una cosa furba. Poi scusa un motor-control di 9 motori lo faccio leggendo gli encoder nell'Arduino e spedendo poi il tutto in ModBus (dato che ne ho parlato, ma era solo un esempio) ad un PLC che poi mi comanda i motori ? :evil: :evil:

Mi sembra ovvio che con una cosa del genere ci fai al più una lettura di posizione a scopo di semplice indicazione di massimo 9 quote di posizione che ti basta vengano aggiornate un paio di volte al secondo, e qui il rapporto 1/10 può benissimo essere ribaltato 9 volte il tempo a leggere gli encoder e 1 volta a trasmetterlo.

I limoni se proprio dobbiamo spremerli, facciamolo prima che si secchino, per aggiungerli ad una bibita fresca, che nostro malgrado, visti i tempi non potremo berci in compagnia.
Evitando di finire nuovamente in polemica, che non è il caso. Ho solo condiviso qui sul forum i risultati di un paio di sere passate al PC in periodo di Corona-Vairus. Sai come capita... ti frulla un'idea, provi a buttarla giù, ti piace... e poi finisce che ti fai prendere la mano.
Mi sembrava originale, l'idea di usare un XOR fatto parallelamente sui tre encoder per la decodifica degli stati e fine li. Non vuole essere assolutamente nessuna proposta di fare alcunché. Se uno ne ricava uno spunto o un'idea per fare qualcos'altro che ha in mente lui, magari con solo 3 e non 9 encoder, sono felice di averglielo proposto, diversamente va bene uguale.

Il mio è stato un mero esercizio di programmazione, fatto una sera in cui non avevo altro da fare. Nel mentre, mi sono scontrato (non mi era capitato prima, come pure di scrivere un blocco in Assembly) con l'esigenza di passare il contenuto di variabili dell'ambiente 'C' di Arduino al blocco ASM() e siccome ho capito solo in parte ciò che è descritto nel sito che ho menzionato, ho umilmente chiesto se qualcuno che l'ha compreso meglio e prima di me, potesse spiegarmelo.

M'è sembrato simpatico chiedendo, condividere ciò che mi ha originato questo rovello mentale. Thats'all!

Avatar utente
hellfire39
God
God
Messaggi: 3416
Iscritto il: domenica 16 dicembre 2012, 9:04
Località: AN

Re: Leggere bene tre encoder con Arduino UNO

Messaggio da hellfire39 » venerdì 15 maggio 2020, 15:39

OK.



Per curiosità, questo weekend, se ho tempo (o se piove :evil: ) voglio provare a vedere dove si può spingere Arduino (con un solo encoder) buttandogli dentro un treno di impulsi numerato per vedere quando inizia a perdere colpi.
Probabilmente seguirò le indicazioni del sito Teensy:
https://www.pjrc.com/teensy/td_libs_Encoder.html

tecno67
Member
Member
Messaggi: 391
Iscritto il: lunedì 26 febbraio 2007, 14:25
Località: Prov. di Milano

Re: Leggere bene tre encoder con Arduino UNO

Messaggio da tecno67 » venerdì 15 maggio 2020, 17:59

Esatto! Hai scoperto le mie fonti.

https://github.com/PaulStoffregen/Encod ... /Encoder.h

Se ti apri il file encoder.h vedrai che il suo incremento a 32bit ed il mio sono identici tranne per il fatto che lui usa 4 diversi registri, mentre io ri-uso sempre quello sui 4 byte.
La differenza è invece nello schema di decodifica, lui alla fine ha implementato il classico algoritmo che trovi anche in altri siti dove la lettura precedente è shiftata di due posizioni a sinistra ed unita in OR con la lettura attuale a formare un codice a quattro bit con 16 combinazioni possibili.

- 4 Che hanno i bit 1 e 2, 3 e 4 uguali a due a due, che sono in pratica 'Nessuna variazione'
- 4 Che sempre a coppie a due a due sono diversi su entrambi i canali, che sono gli stati nel caso l'encoder sia avanzato di due posizioni dall'ultimo passo. Non ho ben capito la decisione di incrementare/decrementare di 2 in questo caso. Dal mio punto di vista è un errore di conteggio. Se accade ho semplicemente superato la frequenza max leggibile e lo devo evitare a priori sapendo i limiti del mio programma. Così rischi di illuderti di contare lo stesso, ma prima o poi ti frega.
-4 Che sono i quattro possibili casi di incremento e
-4 Che sono i quattro possibili casi di decremento.

Lui poi con un uso particolare del codice di decodifica crea un offset su un puntatore e ricava da una tabella in memoria il valore da sommare/sottrarre alla variabile.

Scrivendo in una tabella di quattro righe quei valori, ho notato 'per caso' che facendo l'XOR tra due stati successivi avevo sempre:

00 - (0) con nessuna variazione
11 - (3) con doppio step sia avanti che indietro
01 - (1) in due casi di incremento ed in due casi di decremento
10 - (2) in due casi di incremento ed in due casi di decremento

ma se scambiavo i bit dei canali A e B nella nuova lettura la situazione diventava sempre:

00 - (0) con nessuna variazione
11 - (3) con doppio step sia avanti che indietro
01 - (1) nei casi di decremento
10 - (2) nei casi di incremento

Di li è nata l'idea di fare la decodifica con una prima operazione preparatoria di scambio bit, quindi l'XOR e poi con due confronti successivi ai valori 1 o 2 per decidere se chiamare l'incremento, il decremento o nessuno dei due e ne è nato un primo programma per la lettura di un solo encoder.

Il fatto è che fatta la preparazione dei dati con mascheratura swap dei bit XOR e confronti, non è che ci fosse poi sto gran vantaggio.

Il vantaggio c'è quando invece di farlo per un solo encoder lo devi fare per quattro, perché lo fai in parallello per tutti e quattro. E come detto in post precedente sarebbe ancor più vantaggioso con 16 encoder su un micro a 32bit, dato che a quel punto, essendo l'incremento una sola istruzione, puoi invece di fare una mascheratura, due confronti e due salti condizionati alle somme/sottrazioni, puoi:

Partendo dal bit di ordine zero nella doppia word generata dall'XOR:

-Caricare in un registro dalla variabile counter in memoria

-Con un semplice salto condizionato sul test del bit pari decidere se saltare l'istruzione di incremento del registro

-Fare lo stesso col bit di ordine dispari per decidere se decrementare

-Ricaricare il counter col registro. (con l'Arduino, ma su byte, 8 cicli di clock. Col Teensy non lo so non lo conosco)

.... Procedere così per gli encoder successivi. Velocemente e con poche istruzioni.

Il contesto lo salvi e lo ripristini una sola volta.

Poi se preferisci avere un tempo dedicato alle interruzioni magari sempre il maggiore ma costante e determinato, fai il richiamo di questa routine con un int a tempo, se invece preferisci, per il tipo di gestioni che fai nel loop principale avere un tempo dedicato agli encoder che potrebbe arrivare al max , ma potrebbe anche essere minore quando questi non contano tutti alla massima velocità, la ISR la richiami sul pin-change. Lo valuti tu che sai cosa ti serve.

Ecco quale è stato il mio percorso. Poi dato che è un po il mio carattere, parte la fantasia... ci metti il ModBus e quant'altro, ma alla fine non ho bisogno di fare nulla. Solo il piacere di allenare un po i neuroni.

Avatar utente
hellfire39
God
God
Messaggi: 3416
Iscritto il: domenica 16 dicembre 2012, 9:04
Località: AN

Re: Leggere bene tre encoder con Arduino UNO

Messaggio da hellfire39 » venerdì 15 maggio 2020, 21:15

Esatto! Hai scoperto le mie fonti.
:D sarebbe carino citarle!

Il classico algoritmo è in effetti una macchina a stati.

Contare +2, -2 è in assoluto una porchetta. Un mostro logico nella speranza di recuperare un errore. Una cosa che odio. Se c'è questa possibilità è perché sono andato oltre le capacità del sistema. La soluzione è cambiare il sistema, non nascondere la polvere sotto il tappeto.
Chi si è inventato questo stratagemma ha tutto il mio disprezzo. :cry:


Il codice del teensy non l'ho guardato con attenzione. Ma il teensy 4.0 è talmente veloce (su un benchmark basato su calcoli, è 7000 volte più veloce di Arduino), che anche il semplice codice C va alla grande. La macchina a stati funziona bene sia 1x, 2x che 4x ed è estremamente leggibile. Che è un gran vantaggio per chi ci deve mettere le mani. Uno shift, un or, un accesso alla tabella con successiva addizione/sottrazione sono comunque molto rapidi. Tanto più in un processore a 32 bit dove il contatore a 32 bit richiede una istruzione atomica.

Nel guardare il disassemblato della funzione su Arduino sono comunque rimasto sorpreso che lo shift a sinistra di 2 bit sia fatto con una somma.
Non pensavo che atmel non avesse lo shift (o non capisco perché il compilatore non l'ha usato).

Comunque domani provo a vedere come si comportano Arduino ed il Teensy con il treno di impulsi.

tecno67
Member
Member
Messaggi: 391
Iscritto il: lunedì 26 febbraio 2007, 14:25
Località: Prov. di Milano

Re: Leggere bene tre encoder con Arduino UNO

Messaggio da tecno67 » venerdì 15 maggio 2020, 22:07

Dal mio punto di vista è un errore di conteggio. Se accade ho semplicemente superato la frequenza max leggibile e lo devo evitare a priori sapendo i limiti del mio programma. Così rischi di illuderti di contare lo stesso, ma prima o poi ti frega.
Una precisazione doverosa, prima che qualcuno me lo faccia notare. Non è che PaulStoffregen, che è tuttaltro che uno sprovveduto, visto che se non vado errato fa parte o ha fatto parte del team di sviluppatori di Arduino commetta errori o voglia rifilarci furbate, Oltretutto spiega pure perchè lo fà.
In breve avendo scritto una libreria che a seconda delle dichiarazioni fatte in fase iniziale utilizza per la lettura, o entrambi i canali encoder controllati in interrupt, o un canale gestito in interrupt e l'altro no, oppure entrambi i canali controllati in poling, per mantenere una uniformità di risoluzione apparente tra i vari casi, ha scelto quando usa un solo interrupt, ovvero conta in modalità x2 e non x4 di incrementare/decrementare ad ogni fronte non di 1 ma di due. Una sorta di modo (x2) raddoppiato se mi si passa la semplificazione. In effetti se analizzassimo un conteggio partito da zero, avrebbe solo valori pari.
In sostanza quel conteggio x2 con la modalità a due canali in interrupt non si verifica mai. Se sono nei limiti di frequenza, Si verifica solo col singolo.

Lo avevo letto, a suo tempo, non ricordo se in quella pagina o altrove, ma nella foga di scrivere il messaggio prcedente me ne ero scordato. C :oops: :oops: chiedo umilmente scusa al buon Paul. Spero per me, non per lui, che non legga CncItalia.

La scelta di orientarsi verso l'assembly invece è stata fatta dopo aver letto un articolo sulla latenza di interrupt (che ora non ritrovo purtroppo) dove veniva spiegato che da prove fatte, il compilatore standard di Arduino, usando AttachInterrupt per definire un vettore di servizio interruzione, nel codice che produce, prima di lasciare il controllo alla routine di servizio salva tutti quanti i registri del processore, che poi io li impieghi o meno, introducento in definitiva un uso di circa una cinquantina di cicli di clock tra entrata ed uscita a prescindere anche quando è inutile. Questo, se vero, già di per se spiegherebbe perchè qualsiasi sequenza scritta con i comandi di Arduino sia perdente. Sempre in quel sito viene detto che se invece si usa ISR() che non è di Arduino ma nativa dell'ambiente AVR questo non avviene. Con l'uso della direttiva NAKED il compilatore omette completamente il salvataggio/ripristino del contesto che a quel punto può essere scritto nella maniera più idonea ed efficiente.
Sempre sul sito che ho postato all'inizio viene spiegato che il compilatore non analizza quanto fatto nel blocco ASM() lo passa all'assemblatore così com'è e basta. Quindi in effetti, ci stà che non produca un preambolo ed un ripristino ottimizzato ed anche altri guai. Come io stesso ho potuto sperimentare facendo una svista nel programma Assembly al ripristino del contesto. Al primo accenno di rotazione dell'encoder l'Arduino mi impazziva. Con l'encoder fermo tutto era normale.
Ultima modifica di tecno67 il venerdì 15 maggio 2020, 22:14, modificato 1 volta in totale.

tecno67
Member
Member
Messaggi: 391
Iscritto il: lunedì 26 febbraio 2007, 14:25
Località: Prov. di Milano

Re: Leggere bene tre encoder con Arduino UNO

Messaggio da tecno67 » venerdì 15 maggio 2020, 22:10

Opps!

Stavamo scrivendo assieme, ho letto solo ora dopo l'invio il tuo messaggio.

Rispondi

Torna a “Elettronica CNC”