Lettura RPM

Ciao a tutti, ho un problema:
Sto cercando di leggere i giri motore da un 2 tempi (monocilindrico) e, fino a che ho potuto usare la libreria FreqMeasure andava tutto a meraviglia, però poi ho dovuto inserire nel progetto anche un cronometro (di cui ho trovato lo sketch qui sul forum). Il problema è che sia il cronometro sia la libreria del frequenzimetro utilizzano il timer1 dell'arduino, quindi vanno in conflitto. Non c'è un modo per leggere gli RPM in modo preciso (visto che l'esempio sul playground sbaglia di un buon 10 o anche 20%) e con un refresh "decente".
Grazie in anticipo a tutti!

Ciao! Ma il frequenzimetro che stai usando ha come input il cavo della candela? Perchè molti usano gli impulsi della candela per contare i giri!!
Posta il codice e diamo un occhio :grin:

Puoi usare la pulseIn e soccessivamente confrontarla con millis per restituire un valore espresso in RPM :wink:

cexco:
Ciao a tutti, ho un problema:
Sto cercando di leggere i giri motore da un 2 tempi (monocilindrico) e, fino a che ho potuto usare la libreria FreqMeasure andava tutto a meraviglia, però poi ho dovuto inserire nel progetto anche un cronometro (di cui ho trovato lo sketch qui sul forum). Il problema è che sia il cronometro sia la libreria del frequenzimetro utilizzano il timer1 dell'arduino, quindi vanno in conflitto. Non c'è un modo per leggere gli RPM in modo preciso (visto che l'esempio sul playground sbaglia di un buon 10 o anche 20%) e con un refresh "decente".
Grazie in anticipo a tutti!

Secondo me devi scriverti una versione privata di una delle due librerie in modo che usa uno dei altri due timer.
Ciao Uwe

Innanzi tutto grazie a tutti delle risposte :slight_smile:
Questo è il codice del cronometro:

#include <LiquidCrystal.h>
LiquidCrystal lcd (13,12,11,10,9,8);

uint16_t microseconds = 0, milliseconds = 0, Rmicroseconds =0, Rmilliseconds = 0;
uint8_t seconds = 0, minutes = 0, Rseconds = 0, Rminutes = 0;




ISR(TIMER1_COMPA_vect) 
{
  milliseconds += 100;
  if (milliseconds == 1000)
  {
    milliseconds = 0;
    seconds++;
  }
  if (seconds == 60)
  {
    seconds = 0;
    minutes++;
  }
  if (minutes == 60)
  {
    minutes = 0;

  }
}



void reset_chrono(void)
{
  unsigned char sreg;
  sreg = SREG;    
  cli();  
  TCNT1 = 0;
  SREG = sreg;
  milliseconds = 0;
  seconds = 0;
  minutes = 0;
 
}



uint32_t read_microseconds(void)
{
  uint32_t microseconds;
  unsigned char sreg;
  sreg = SREG;
  cli();  
  microseconds = TCNT1*4;  
  SREG = sreg;  
  return microseconds;
}

void resetmemory(){
  Rminutes=0;
  Rseconds=0;
  Rmilliseconds=0;
}

void setup(void)
{
  
  lcd.begin(20,4);
  TCCR1A = 1<<WGM10 | 1<<WGM11; 
  TCCR1B = 1<<CS10 | 1<<CS11 | 1<<WGM12 | 1<<WGM13;  
  OCR1A = 24999;  
  TIMSK1 = 1<<OCIE1A;  
  sei();  
}


void chrono(void)
{
  uint32_t microseconds_count = 0;
  uint16_t milliseconds_display = 0, microseconds_display = 0, milliseconds_rest = 0;
  microseconds_count = read_microseconds();
  milliseconds_rest = (uint16_t)(microseconds_count/1000);  
  milliseconds_display = milliseconds + milliseconds_rest;  
  microseconds_display = (uint16_t)(microseconds_count-(uint32_t)(milliseconds_rest*1000)); 
  lcd.setCursor(8,2);
  lcd.print(minutes, DEC);
  lcd.print("'");
  lcd.setCursor(12,2);
  lcd.print(".");
  lcd.setCursor(13,2);
  lcd.print(milliseconds_display, DEC);
   if(seconds<10){
    lcd.setCursor(10,2);
    lcd.print("0");
    lcd.setCursor(11,2);  
    lcd.print(seconds, DEC);
  }else{
    lcd.setCursor(10,2);
    lcd.print(seconds, DEC);
  }
    
  if (digitalRead(5)==LOW)  
  {
    delay(100);
    resetmemory();
    Rminutes=minutes;
    Rseconds=seconds;
    Rmilliseconds=milliseconds_display;
    reset_chrono();
  }
  lcd.setCursor(8,3);
  lcd.print(Rminutes, DEC);
  lcd.print("'");
  lcd.setCursor(12,3);
  lcd.print(".");
  lcd.setCursor(13,3);
  lcd.print(Rmilliseconds, DEC);
  if(Rseconds<10){
    lcd.setCursor(10,3);
    lcd.print("0");
    lcd.setCursor(11,3);  
    lcd.print(Rseconds, DEC);
  }else{
    lcd.setCursor(10,3);
    lcd.print(Rseconds, DEC);
  }
  if(Rmilliseconds==0){
    lcd.setCursor(14,3);
    lcd.print("00");
  }
  
}

Questo invece è il link della libreria:
http://www.pjrc.com/teensy/td_libs_FreqMeasure.html

@ratto93 ho gia provato con pulseIn ma sembra dare dei valori a caso, non ho capito il perchè.
@uwefed sapresti dirmi come si fa? perchè di programmazione sono decisamente scarso..

comunque per adesso sto usando un generatore di funzioni, poi sul motore probabilmente prenderò il segnale dal primario della bobina (quello a bassa tensione)

con pulse in funziona sicuro, è quello che uso io

io prelevo il segnale squadrando l'alternata dell'alternatore.
tramite cavo della bobina è possibile basta usare un comparatore qualsiasi e un pò di filo avvolto attorno al cavo candela, funziona ma a me no piaceva avere cavi in giro e così prelevo alimentazione e dati tutto dallo stasso cavo.

Anche a me funziona il PulseIn, ma solo se nel codice c'è quello e basta, appena lo metto assieme al resto (n°marce, cronometro, ecc) legge 0 RPM e da problemi sul cronometro

Perché anche PulseIn usa un interrupt per controllare l'arrivo dell'impulso sul pin monitorato. Quando si usano questo genere di funzioni del micro (timer ed interrupt) è facile che vadano in conflitto le librerie che le utilizzano.

quindi cosa mi consigli di fare leo?

Quello che ti ha consigliato Uwe, usare 2 timer differenti.

Questo spezzone di codice io l'ho usato per un contamillisecondi. Adattandolo (devi rifarti i calcoli sulla frequenza quindi trovare un altro prescaler [=devi leggere e studiare il datasheet]) puoi ricavarci un cronometro preciso.

unsigned int secondi; //usato per contare internamente i secondi ed evitare di interrogare l'RTC ogni momento
int contatore;

void setup() {
    //inizializza le variabili temporali
    secondi=0;
    contatore=0;
    attivaTimer(); //attiva il timer
}

//routine di attivazione del timer con clock interno
void attivaTimer() {
    /*imposto il timer 2 per usare un contatore interno per contare i singoli secondi
    Per ottenere la precisione di 1s, prendiamo un prescaler di 32
    ed inizializziamo il contatore ad 8 bit a 6 così da avere un overflow
    ogni 250 colpi: con clock a 16 MHz sono esattamente 1 millisecondo*/
    
    //disattiva tutti gli interrupt sul timer 2 durante il setup iniziale
    TIMSK2 &= ~(1<<TOIE2); 
    TIMSK2 &= ~((1<<OCIE2A) | (1<<OCIE2B));
    // Usa il clock intero dell'Atmega come fonte di clock per il prescaler
    ASSR &= ~(1<<AS2);
    //Prescaler/64 -
    TCCR2B |= (1<<CS22); 
    TCCR2B &= ~(1<<CS21);
    TCCR2B &= ~(1<<CS20);
    // Modalità normale: incrementa il contatore fino all'overflow
    TCCR2A &= ~((1<<WGM21) | (1<<WGM20)); 
    TCCR2B &= ~(1<<WGM22);
    
    /*valore iniziale del contatore: 6. Questo perché 1/(CLOCK_MICRO_IN_HZ/PRESCALER/(VALORE_CONTATORE))
    restituisce i ms prima che il contatore vada in overflow, quindi 1/(16000000/32/(256-6)=0,001s ossia 
    1 ms esatto. */
    TCNT2=6;
    // Attiva un segnale di interrupt all'overflow del contatore
    TIMSK2 |= (1<<TOIE2); 
    //attiva gli interrupt
    SREG |= 1<<SREG_I;
}

/*Routine eseguita dall'interrupt collegato all'overflow del timer2
Aggiorna il contatore e controlla se è trascorso il tempo impostato: 
in caso affermativo, esegue una lettura dei sensori*/
ISR(TIMER2_OVF_vect) {
    TCNT2=6;
    contatore++;
    if (contatore>999) { //sono trascorsi 1000 ms->1 s
        contatore=0;
        secondi++;
        //qui puoi aggiungere anche la gestione dei minuti, usando un'altra variabile
        //da incrementare ogni 60 secondi... e così per le ore
    }
}

Nel tuo codice basta a questo punto leggere secondi e contatore ed ha i secondi passati ed i millisecondi.
per reinizializzare il contatore basta fare una funzione così:

void reimpostaTimer() {
    TIMSK2 &= ~(1<<TOIE2); //ferma il timer 2
    TCNT2=6;              //reimposta il counter del timer
    secondi=0;
    contatore=0;
    TIMSK2 |= (1<<TOIE2); //riattiva timer 2 
}