Con i pezzi recuperati da una vecchia stampante ad aghi cerco di realizzare uno spostamento x; la tecnologia è datata, la cinghia dentata non è azionata da un motore pp ma da un motore cc con encoder incorporato. Funziona benissimo uno scheth copiato dal tutorial, tanto che con sorpresa vedo che risolve 3 centesimi di mm di spostamento x ed inoltre è velocissimo e "felpato" (non potrebbe che essere così visto che non si muove a passi).
Il problema è che nel loop ho messo un if che compara la posizione dell'encoder con una variabile passi affinchè lo spostamento si fermi dopo tot passi, ma non funziona. Ho letto che gli interrupt "interrompono" il funzionamento di delay() e che le variabili conviene dichiararle "volatile"; non mi pare di aver visto limitazioni di "if".
Al netto dei soliti errori pacchiani che sono abituato a fare prima di giungere ad un funzionamento quasi corretto degli sketch, c'è qualcosa di cui bisogna tener conto nell'uso degli interrupt a carico dello statement "if"?
Grazie.
Pino
Pino_:
Ho letto che gli interrupt "interrompono" il funzionamento di delay() e che le variabili conviene dichiararle "volatile"; non mi pare di aver visto limitazioni di "if".
Non è che interrompono il delay() ... è che quando si entra in una ISR gli interrupt vengono disabilitati (... uno dei motivi per cui deve essere la più veloce possibile) e quindi, altre cose che basano il loro funzionamneto su interrupt, vengono ovviamente sospese (... e quindi non funzionanti) sino al termine della ISR.
Le variabili che vengono usare in una ISR non conviene, DEVONO essere dichiarate con attributo "volatile".
Capisci da solo che l'IF ha nulla a che vedere con tutto ciò.
Eccolo. Le indicazioni delle sezioni mi servono in fase di per ricordarmi le righe di codice aggiunte da me per non fare confusione con quelle del codice copiato.
Dovrebbe azionare lo spostamento X per 1000 passi e fermarsi, invece va a fine corsa.
Lo so che è rudimentale ma è una prova.
//--------------sezione encoder ------------------
int encoderPin1 = 2;
int encoderPin2 = 3;
int encoderPinZero = 4;
int led = 5;
volatile int lastEncoded = 0;
volatile long encoderValue = 0;
long lastencoderValue = 0;
int lastMSB = 0;
int lastLSB = 0;
//-----------------sezione X----------------------
int AD = 9;
int CB = 8;
int passi = 1000;
void setup() {
//--------------sezione encoder------------------
Serial.begin (9600);
pinMode(encoderPin1, INPUT);
pinMode(encoderPin2, INPUT);
pinMode(encoderPinZero, INPUT_PULLUP);
attachInterrupt(0, updateEncoder, CHANGE);
attachInterrupt(1, updateEncoder, CHANGE);
//--------------sezione X------------------------
pinMode(AD, OUTPUT);
pinMode(CB, OUTPUT);
pinMode(led, OUTPUT);
}
//--------------------sezione X----------------------
void sx(){
digitalWrite(AD, LOW);
delay(100);
digitalWrite(CB, HIGH);
}
void dx(){
digitalWrite(CB, LOW);
delay(100);
digitalWrite(AD, HIGH);
}
void loop(){
//-----------sezione encoder----------
Serial.print(encoderValue);
Serial.print(" - ");
Serial.println(passi);
int val = digitalRead(encoderPinZero);
if(val==LOW){encoderValue=0;}
//-----------sezione x---------------
if(encoderValue < passi){
sx();
}
}
//----------------sezione encoder ------------------
void updateEncoder(){
int MSB = digitalRead(encoderPin1); //MSB = most significant bit
int LSB = digitalRead(encoderPin2); //LSB = least significant bit
int encoded = (MSB << 1) |LSB; //converting the 2 pin value to single number
int sum = (lastEncoded << 2) | encoded; //adding it to the previous encoded value
if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue ++;
if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue --;
lastEncoded = encoded; //store this value for next time
}
Santo cielo, non c'è!
Nella funzione sx() rimane alto CB, ovvero il ramo dei mosfet del ponte H che aziona il motore in senso orario.
Se aggiungo alla sx() digitalWrite(CB, LOW); il motore si ferma, ma le prestazioni degradano drasticamente.
Ho introdurre un delay (2) altrimenti i passi si sentono ma non c'è avanzamento, ovvero il motore non ha il tempo per avviarsi. Inoltre ho ridotto a 1ms il delay tra lo spegnimento del ramo CB e l'accensione del ramo AD, che se vogliamo è inutile ma ho messo per le prove perchè mi scoccerebbe bruciare qualche mosfet.
In ogni caso capisco che l'approcio è sbagliato perchè richiamare la funzione sx() per ogni passo alla fine rallenta inaccettabilmente il movimento. Farò delle prove in modo che la funzione venga chiamata una sola volta.
SukkoPera:
Non credo di avere capito... Dove l'hai inserita?
PS: Per favore premite CTRL+T nell'IDE e riposta lo sketch com'è ora.
Ecco lo sketch. Non va bene perchè ora lo spostamento è così veloce che si ferma un bel po' dopo, invece di fermarsi a 100 passi si ferma oltre i 200
//--------------sezione encoder ------------------
int encoderPin1 = 2;
int encoderPin2 = 3;
int encoderPinZero = 4;
int led = 5;
volatile int lastEncoded = 0;
volatile long encoderValue = 0;
long lastencoderValue = 0;
int lastMSB = 0;
int lastLSB = 0;
//-----------------sezione X----------------------
int AD = 9;
int CB = 8;
int passi = 100;
void setup() {
//--------------sezione encoder------------------
Serial.begin (9600);
pinMode(encoderPin1, INPUT);
pinMode(encoderPin2, INPUT);
pinMode(encoderPinZero, INPUT_PULLUP);
attachInterrupt(0, updateEncoder, CHANGE);
attachInterrupt(1, updateEncoder, CHANGE);
//--------------sezione X------------------------
pinMode(AD, OUTPUT);
pinMode(CB, OUTPUT);
pinMode(led, OUTPUT);
}
//--------------------sezione X----------------------
void sx() {
digitalWrite(AD, LOW);
delay(1);
digitalWrite(CB, HIGH);
if (encoderValue > passi) {
digitalWrite(CB, LOW);
}
}
void dx() {
digitalWrite(CB, LOW);
delay(1);
digitalWrite(AD, HIGH);
}
void loop() {
//-----------sezione encoder----------
Serial.print(encoderValue);
Serial.print(" - ");
Serial.println(passi);
int val = digitalRead(encoderPinZero);
if (val == LOW) {
encoderValue = 0;
}
//-----------sezione x---------------
sx();
}
//----------------sezione encoder ------------------
void updateEncoder() {
int MSB = digitalRead(encoderPin1); //MSB = most significant bit
int LSB = digitalRead(encoderPin2); //LSB = least significant bit
int encoded = (MSB << 1) | LSB; //converting the 2 pin value to single number
int sum = (lastEncoded << 2) | encoded; //adding it to the previous encoded value
if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue ++;
if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue --;
lastEncoded = encoded; //store this value for next time
}
Non sono un esperto di encoder, ma mi sembra che il modo che usi per leggerlo sia eccessivamente macchinoso (e dunque lento). Prova a googlare, giurerei di avere trovato del codice molto più semplice qualche tempo fa.
Per coerenza con sx() e dx(), metti il codice per fermare il carrello in una funzione stop().
digitalWrite() è una funzione piuttosto lenta (4-5 ms), per cui il controllo sul superamento dei passi non viene effettuato sufficientemente spesso. Come già pensavi, è meglio che separi il codice per l'avviamento dello spostamento da quello per fermarlo, evitando di eseguire il primo quando non è necessario.
Non so bene dove vuoi arrivare, ma immagino ti convenga iniziare ad impostare il programma sotto forma di macchina a stati, se vuoi eseguire una determinata serie di movimenti consecutivi.
Io non sono certo un'esperto in materia, ma ... considerate le inerzie, non sarebbe meglio un PID con delle rampe "mirate" ? ... voglio dire, ricordatevi che un motore DC con motoriduttore non puo, materialmente, fermarsi come un passo-passo, in una precisa posizione di colpo, neppure mettendo in corto il motore per frenarlo la fermata sarebbe istantanea ... quindi non e' meglio prevedere accelerazioni e decelerazioni con un qualche tipo di PID ?
Non sono un esperto di encoder, ma mi sembra che il modo che usi per leggerlo sia eccessivamente macchinoso (e dunque lento). Prova a googlare, giurerei di avere trovato del codice molto più semplice qualche tempo fa.
cut 2 e 3, che ho capito.
Non so bene dove vuoi arrivare, ma immagino ti convenga iniziare ad impostare il programma sotto forma di macchina a stati, se vuoi eseguire una determinata serie di movimenti consecutivi.
Il codice per leggere l'encoder l'ho preso dal tutorial e mi sembra funzioni bene, se sposto il carrello a mano
più o meno alla velocità con la quale il motore lo aziona, ovvero piuttosto velocemente) la ripetitività mi sembra buona, sbaglia di uno o due passi ovvero di 0,03-0,06 mm; immagino che quando meccanicamente sarà più stabile migliorerà. Ho provato a girare il motore a mano di un giro e i passi rilevati sono circa 1200, che mi sembra una risoluzione elevata. Mi dispiace rinunciare a queste prestazioni.
Non so cosa significhi "macchina a strati"; immagino siano sottoprogrammi; ovvero blocchi dedicati ognuno ad una funzione specifica.
Veramente l'idea è codice più semplice = efficienza maggiore o uguale... Non stavo suggerendo di ridurre le prestazioni in alcun modo.
Per il concetto di macchina a STATI (non STRATI!) puoi fare ricerche sul forum, se ne è parlato 1000 volte e non c'entra granché con quanto immagini tu.
PS: Se chiedi dei consigli, poi dovresti anche seguirli o perlomeno cercare di capire cosa ti si sta dicendo, non supporre cose a caso.
Etemenanki:
Io non sono certo un'esperto in materia, ma ... considerate le inerzie, non sarebbe meglio un PID con delle rampe "mirate" ? ... voglio dire, ricordatevi che un motore DC con motoriduttore non puo, materialmente, fermarsi come un passo-passo, in una precisa posizione di colpo, neppure mettendo in corto il motore per frenarlo la fermata sarebbe istantanea ... quindi non e' meglio prevedere accelerazioni e decelerazioni con un qualche tipo di PID ?
Sì, se voglio utilizzare quel motore, che non ha alcun riduttore, immagino che anche se piccola devo risolvere il problema dell'inerzia.
E' stata la prima cosa a cui ho pensato quando l'ho preso in mano ... poi ho letto la marca e mi sono tranquillizzato. La marca è Minertia Motor
pensavo a qualcosa del genere: il motore parte alimentato CC e quando si trova vicino alla destinazione viene alimentato in PWM degradante.
SukkoPera:
Veramente l'idea è codice più semplice = efficienza maggiore o uguale... Non stavo suggerendo di ridurre le prestazioni in alcun modo.
Per il concetto di macchina a STATI (non STRATI!) puoi fare ricerche sul forum, se ne è parlato 1000 volte e non c'entra granché con quanto immagini tu.
PS: Se chiedi dei consigli, poi dovresti anche seguirli o perlomeno cercare di capire cosa ti si sta dicendo, non supporre cose a caso.
Beh, a parte che devo cambiare gli occhiali (avevo letto "Strati"), dammi il tempo di informarmi, no? E comunque ci sono anche le macchine a strati, per esempio i sistemi operativi.
Anchio sono dell'idea che codici semplici sono preferibili. E' che se io dovessi mettermi a scrivere un programma per l'encoder con quella risoluzione stiamo freschi, non credo di avere al momentoho le competenze.