Ciao a tutti! Mi chiamo Marco. Sono nuovo del forum e nel mondo di Arduino. Sono appassionato di motori e elettronica. Sto realizzando (ci sto provando più che altro) una accensione ad anticipo variabile per un motore due tempi. Ho già letto altri post esistenti da cui ho preso spunto per realizzare il mio codice, quindi dovrebbe essere tutta roba già vista.
Un sensore hall dovrà rilevare la posizione del volano (tramite un magnete incollato ad esso) e inviare il segnale ad Arduino, il quale calcolerà l'anticipo (al variare dei giri motore) e, tramite un mosfet, piloterà la bobina alta tensione.
Per ora sto semplicemente accendendo un led quando il magnete passa sul sensore. Allego il codice:
#include <StopWatch.h>
const byte led = 12; // bobina
const byte pin = 2; // sensore hall
volatile int hallstate = HIGH; // stato magnete
StopWatch sw_micros(StopWatch::MICROS); // contatore periodo
long time = 0; //periodo
long rpm = 0; //numero giri
void setup() {
Serial.begin(9600);
pinMode(led, OUTPUT); //bobina
pinMode(pin, INPUT_PULLUP); //pullup sensore
attachInterrupt(0, magnete, RISING); //interrupt passaggio magnete
sw_micros.start(); //inizia a contare periodo
}
void loop(){
if(hallstate == LOW){
time=sw_micros.elapsed(); //memorizza periodo
sw_micros.reset(); //azzera timer periodo
sw_micros.start(); //riavvia il contatore
hallstate = HIGH; //reimposta hallstate
scintilla(); //void scintilla
Serial.println(time); //stampa durata periodo
Serial.print("\t");
rpm=60000000/time; //calcolo rpm
Serial.println(rpm); //stampo rpm
}
}
void scintilla()
{
digitalWrite(led, HIGH); //accendi bobina
delayMicroseconds(1000);
digitalWrite(led, LOW); //spegni bobina
}
void magnete()
{
hallstate = !hallstate ; //passaggio magnete
}
Il problema è che nel monitor seriale io vedo valori assurdi. Ad un singolo passaggio del magnete compaiono almeno tre letture diverse, delle quali solo una è più o meno precisa. Ho pensato che il problema possa essere il sensore hall (che dovrebbe essere un KY-024) che all'avvicinarsi/allontanarsi del magnete fa delle false letture. Esiste un modo per "pulire" il segnale digitale? Sempre che non sia un errore nel codice.
Grazie mille a tutti.
Ciao, non sono sicurissimo che il problema si questo ma nella definizione dell'interrupt hai messo 0 e non 2 (pin al quale hai collegato il sensore hall)
Dipende da che board usa ... in realtà, con quel parametro, andrebbe proprio passato il numero di interrupt utilizzato e quindi, nel caso di Arduino UNO, il pin 2 è proprio equivalente all'interrupt 0.
Dato che però non sempre la cosa è così semplice, la cosa migliore è affidarsi ad un'apposita funzione che ritorna il numero di interrupt in funzione del piedino ed usare nella attachInterrupt() ovvero fare:
Grazie per le risposte. La scheda è una uno. Anche modificando il codice come dice Guglielmo il problema persiste.
A circa 900 giri mi appaiono questi valori: (a sinistra il periodo e a destra gli rpm)
Inoltre il periodo mi esce in millisecondi, ma nel codice io sto contando in microsecondi. Forse sbaglio a dichiarare la variabile come long?
Per quanto riguarda il gruppo rc, in teoria non vado a ritardare di alcuni microsecondi la lettura? Dovendo gestire differenze di anticipo anche nell'ordine dei 2/3 gradi ho bisogno della massima precisione.
Il sensore che sto utilizzando ha un trimmer, ho provato a regolarlo ma la situazione non cambia.
Grazie
Grazie per le risposte! Provvederò ad aggiornare le variabili.
Hai ragione, non sono millisecondi. La lettura è giusta, avevo sbagliato io il calcolo.
Cercando su internet ho scoperto che il sensore che uso fornisce un segnale digitale “penoso”. All’apertura e chiusura genera un’oscillazione (Lo spiego da ignorante, correggetemi se sbaglio) quindi arduino legge centinaia di passaggi. Con un condensatore e una resistenza dovrei risolvere? E nel caso mi sapete indicare che condensatore e resistenza comprare.
non e' che quel sensore fa schifo, è che si tratta di un sensore lineare e quindi non adatto a quello che vuoi farci tu.
su quale piedino lo hai collegato A0 o D0 ?
comunque ti conviene usare un sensore digitale.... ad esempio l' A3144
Per le prove con tutti quei Serial.print credo che ti convenga disattivare l'interrupt in magnete() e riattivarlo dopo l'ultimo Serial.print. In questo modo perderai alcuni impulsi, quindi puoi farlo solo per queste prove.
Anziché legarti a una libreria, però, con poco sforzo puoi usare micros(), semplificando anche il codice. A volte è più facile scrivere il codice direttamente che usare una libreria!
Avevo pensato anch’io all’inizio di usare micros(), ma mi era venuto il dubbio su cosa potesse succedere se il programma superasse i 70 minuti (circa mi pare) di esecuzione, ossia quando micros si azzera. In teoria avrei un valore di t0 estremamente grande e micros che è praticamente zero, quindi facendo micros()-t0 verrebbe un numero negativo. Ma sempre in teoria uint32_t non dovrebbe avere segno, quindi 0-(qualsiasi numero) farebbe sempre zero. Giusto? Il periodo però non può essere zero.
micros() è internamente usata dalla libreria, forse per questo ti suggerivano di usare micros() direttamente. Per il discorso degli interi senza segno, cioè uint e per esempio uint8_t.
Bene uint8_t è grande 8-bit e con tutti i bit accesi il valore decimale vale 255 (0xff esadecimale).
Se a 255 ci aggiungi una unità a causa dell'overflow ritorni a 0. L'overflow al contrario lo possiamo chiamare underflow e cioè 0 - 1 = 255. Sperimentali con Serial.println(). Fai attenzione anche alla promozione al tipo int quando si eseguono operazioni su due o più variabili di tipo uint8_t.
Occhio che la promozione è legata alla architettura e quindi ad esempio su esp32 la promozione è ad int32_t. Insomma il C/C++ ti può sorprendere fornendoti risultati non aspettati e quindi sarebbe tra le prime cose da sperimentare.
Ricorda che micros() ha risoluzione di 4us, quindi non otterrai mai 6us. Questo tipo di applicazioni
dovrebbero essere sviluppate facendo uso dei timer hardware e su arduino UNO ne trovi 3 di cui 2 a 8-bit (timer0 e timer2) e 1 a 16-bit (timer1). Purtroppo usare questi timer richiede la manipolazione dei registi interni mappati in memoria ram dall'indirizzo 0 a 256. Il framework di arduino non è adatto per questo tipo di applicazioni, basti solo pensare che per accendere un pin di una porta bastano 62.5n impiegando i registri, mentre con digitalWrite non ricordo con precisione ma è almeno 20 volte più lento. Classifico questa applicazione tra quelle dove il tempo è critico e infatti si chiamano time-critical. Si può arrivare al punto in cui aggiungi una istruzione e le tempistiche non vengono rispettate e l'applicazione fallisce.
Ad esempio la tua funzione scintilla impegna la cpu (impedendo di fatto di eseguire altro codice nel loop) per 1ms. C'è modo di evitare ciò impiegando i timer che possono essere configurati per generare segnali PWM su alcuni pin.
Scusa il post lungo, ma dovevo avvertirti, con ciò non ti sto dicendo di desistere ma considera
i limiti delle funzioni facilitate come digitalWrite, read ecc.
Prima di iniziare il progetto mi sono informato sulla velocità di lettura/scrittura di Arduino Uno. Ipotizzando che il mio motore riesca a raggiungere 20000rpm (e non li raggiunge, si ferma attorno ai 13/14000) il periodo è di 3ms, quindi per l’esecuzione del programma dovrei ancora rientrarci alla grande. (Mi sbaglio?) Con una risoluzione di 4us avrei (sempre a 20000rpm) una precisione di mezzo grado, che è più che sufficiente. Inoltre se conosco il ritardo tra il passaggio del magnete e lo scoccare della scintilla dovuto all’esecuzione del programma mi basta spostare il sensore hall in modo da compensare il ritardo. Questo lo verificherò sperimentalmente.