Jump to content
Sign in to follow this  
Arch_ETS

Tachometer. Resolution vs speed issues.

Recommended Posts

Hello everyone.

I am more of a hardware dude, so software always makes me stay up all nights while trying to understand why it doesn't work and why DOES it work.
Anyway, below is the code I am using. It works, kind of, but there is a problem.

If I decrease delay, resolution goes down pretty bad, with 300ms it is 100RPM. If I Increase, of course it gets slow, and that is not ok in my application. As I am a total noob, I hope someone here might give me advice, what can I possibly do.
Regards, Arch.

 

volatile unsigned int rpmcount;
float rpm;
float timeold;

const int carbswitch = P2_2; //switch from carburator, when accelerator is not pushed, switch is connected to GND. When accelerator is getting floored, disconnects.
const int outputswitch = P2_4;  //enables/disables gas valve, HIGH at >1400 RPM and carbswitch HIGH.
const int shiftlight = P2_5;  //enabled when RPM > 5000.
const int engineok = P2_3;  //enabled when RPM > 500.
int carbswitchstate = LOW;
int valvestate = LOW;
int mymilis = 300;
String ds;


#include <LiquidCrystal.h>

LiquidCrystal lcd(P1_2, P1_1, P1_4, P1_5, P1_6, P1_7);

void rpm_fun()
 {
   rpmcount++;
 }
//INPUT from ignition coil's switching GND is wired to P1_3, with protection from high voltage spikes.
void setup()
 {
   lcd.begin(16, 2);
   attachInterrupt(P1_3, rpm_fun, FALLING);
   rpmcount = 0.0;
   rpm = 0.0;
   timeold = 0.0;
   pinMode (outputswitch, OUTPUT);
   pinMode (shiftlight, OUTPUT);
   pinMode (engineok, OUTPUT);
   pinMode(carbswitch, INPUT_PULLUP);
 }

 void loop()
 {
   if(millis()-timeold>=mymilis){
   detachInterrupt(P1_3);

  const float freqHz = ((float) rpmcount / (float) (millis() - timeold)) * 1000.0;
  const float rpm = freqHz * 30.0;
   rpmcount = 0;

   carbswitchstate = digitalRead(carbswitch);
   if (rpm >= 1400.0 && carbswitchstate == HIGH) {
    digitalWrite(outputswitch, HIGH);
   }
   else {
    digitalWrite(outputswitch, LOW);
   }
  valvestate = digitalRead(outputswitch);
   if (valvestate == HIGH) {
    ds = ("ON");
   }
   else {
    ds = ("OFF");
   }
   if (rpm >= 5000.0) {
    digitalWrite(shiftlight, HIGH);
   }
   else {
    digitalWrite(shiftlight, LOW);
   }
   if (rpm >= 500.0) {
    digitalWrite(engineok, HIGH);
   }
   else {
    digitalWrite(engineok, LOW);
   }

  
   lcd.clear();
   lcd.print("RPM=");
   lcd.print(rpm);
   lcd.setCursor(0, 1);
   lcd.print ("ECON=");
   lcd.print(ds);
   
   attachInterrupt(P1_3, rpm_fun, FALLING);
   
  timeold = millis();
   }
  }

Share this post


Link to post
Share on other sites

Hi,

I think that in rpm_fun you should get timestamps to compute the time between sparks instead of counting pulses.

Anither ideea would be to use pulseIn to get the duration ( measure both high and low pulses). This method will do measurements on differen pulses, but as the engine is a slow system compared to the microcontroller, this should not be a problem.

 

Calin

Share this post


Link to post
Share on other sites

Okay, thanks, calinp.
 
I don't really know what did I do, but now it works a lot better, have not tested yet in real life, but with computer/audacity as signal generator it works fine, here is the new code.

#include <LiquidCrystal.h>

LiquidCrystal lcd(P1_2, P1_1, P1_4, P1_5, P1_6, P1_7); //RS,EN,D4,D5,D6,D7

const int coilpin = P1_3; //input/interrupt pin
const int sampletime = 300.0; //adjust for speed/accuracy
const int carbswitch = P2_2; //switch from carburator, when accelerator is not pushed, switch is connected to GND. When accelerator is getting floored, disconnects.
const int outputswitch = P2_4;  //enables/disables gas valve, HIGH at >1400 RPM and carbswitch LOW.
const int shiftlight = P2_5;  //enabled when RPM > 5000.
const int engineok = P2_3;  //enabled when RPM > 500.
int carbswitchstate = LOW;
int valvestate = LOW;
int mymilis = 300;
String ds;

volatile unsigned long timeold;
volatile unsigned long timenow;
volatile unsigned long duration;

unsigned long disp_timeold = 0.0;
unsigned long timetemp = 0.0;   

void setup()
{
 lcd.begin(16, 2);
 attachInterrupt(P1_3, RPM_fun, FALLING);
 pinMode (outputswitch, OUTPUT);
 pinMode (shiftlight, OUTPUT);
 pinMode (engineok, OUTPUT);
 pinMode(carbswitch, INPUT_PULLUP);
}

void loop()
{
  timetemp = millis() - disp_timeold;
  if( timetemp >= sampletime)
  {
    detachInterrupt(P1_3);  
    disp_timeold += timetemp;
    float RPMtemp = 30000000.0/duration;  //2 pulses per revolution
    float RPM = RPMtemp - RPMtemp/100.0*3.84; //there was some error, so this is pretty dumb way to remove it, but it works. Kind of.
    
    //switches//
    carbswitchstate = digitalRead(carbswitch);
   if (RPM >= 1400.0 && carbswitchstate == HIGH) {
    digitalWrite(outputswitch, HIGH);
   }
   else {
    digitalWrite(outputswitch, LOW);
   }
    valvestate = digitalRead(outputswitch);
   if (valvestate == HIGH) {
    ds = ("ON");
   }
   else {
    ds = ("OFF");
   }
   if (RPM >= 5000.0) {
    digitalWrite(shiftlight, HIGH);
   }
   else {
    digitalWrite(shiftlight, LOW);
   }
   if (RPM >= 500.0) {
    digitalWrite(engineok, HIGH);
   }
   else {
    digitalWrite(engineok, LOW);
   }
   //end of switches//
   
    lcd.clear();
    lcd.print(" RPM=");
    lcd.print(RPM);
    lcd.setCursor(0, 1);
    lcd.print ("  ECON=");
    lcd.print(ds);

    
  attachInterrupt(P1_3, RPM_fun, FALLING);
  }
}

void RPM_fun()
{
  timeold = timenow;
  timenow = micros();
  duration = timenow-timeold;
}

Share this post


Link to post
Share on other sites

Try using int instead of float. 

 

Ex.: float RPM = RPMtemp - RPMtemp/100.0*3.84;

Try:

 

int RPM = (RPMtemp - RPMtemp/100.0*3.84)*1000;

 

And returns to float only on display values if necessary.

 

float RPM_float;

RPM_float = RPM/1000;

 

lcd.print(" RPM=");

    lcd.print(RPM_float);

 

Share this post


Link to post
Share on other sites

Unfortunately I destroyed my only G2553 chip by connecting VccMSP to VccLCD (5v). (Yes, stupid mistake). But I will definitely try your suggestion, when I will receive parts and MCU chip. Thanks for response, Maldrax.

Share this post


Link to post
Share on other sites

Here is the latest code, cannot check if it works properly, but I already have questions.
Are there any benefits from detaching interrupt while calculations, lcd stuff, adc and so on are going on, and then re-attaching it?
What are benefits using float over double? Isn't double more friendlier data type to MCU?
Also, is it worth doing less important stuff less frequently by doing as in following code? Is that even "legal"? /Sorry for stupid questions./
 

EDIT: Edited all code, for better clarity.

#include <LiquidCrystal.h>

LiquidCrystal lcd(P2_0, P2_1, P2_2, P2_3, P2_4, P2_5); //RS,EN,D4,D5,D6,D7

const byte coilpin = P1_3; /*input/interrupt pin.*/
const byte sampletime = 300.0; /*adjust for speed/accuracy.*/
const byte carbswitch = P1_0; /*switch from carburator, when accelerator is not pushed, switch is connected to GND.
When accelerator is getting floored, disconnects from GND. Internal PULLUP enabled for this pin.*/
const byte outputswitch = P1_1;  /*enables/disables gas valve, HIGH at >1400 RPM and carbswitch HIGH. 
MSP430 -> 2N7000 -> Power Mosfet -> Valve(s). [Accelerator swich LOW -> MSP430 Output HIGH (if RPM > 1400)-> 2N7000 output LOW, Power FET off, valve closed]*/
const byte shiftlight = P1_2;  /*enabled when RPM > 4900.*/
const byte engineok = P1_4;  /*enabled when 5000 > RPM > 500.*/
const byte heateroutput = P1_5;  /*if engine (manifold) temperature is lower than defined one, intake manifold heater is turned on. I am not sure yet if I will use 
it with time delay circuit (like 10 seconds on, then off) or control it only with lm36 readings.*/
const byte lm36engine = P1_6;  /*lm36 analogue temp sensor, measures engine temperature.*/
const byte voltagepin = P1_7;  /*monitors battery voltage through voltage divider 10k/47k.*/
int carbswitchstate; /*P1_0 state.*/
int valvestate = LOW;  /*P1_1 state, also determines econstr value.*/
int RPM;
String econstr;

volatile unsigned long timeold;
volatile unsigned long timenow;
volatile unsigned long duration;

unsigned long disp_timeold = 0.0;
unsigned long timetemp = 0.0;   

void setup() 
{
  lcd.begin(16, 2);
  attachInterrupt(coilpin, RPM_fun, FALLING);
  pinMode(outputswitch, OUTPUT);
  pinMode(heateroutput, OUTPUT);
  pinMode(shiftlight, OUTPUT);
  pinMode(engineok, OUTPUT);
  pinMode(carbswitch, INPUT_PULLUP);
  pinMode(lm36engine, INPUT);
  pinMode(voltagepin, INPUT);
} 

void loop() 
{
  timetemp = millis() - disp_timeold;
  if( timetemp >= sampletime) 
  { 
    detachInterrupt(coilpin);  
    disp_timeold += timetemp;
    float RPMtemp = 30000000.0/duration; /*2 pulses per revolution*/
    int RPM = (RPMtemp - RPMtemp/100.0*3.84)*1000; /*there was some error, so this is pretty dumb way to remove it, but it works. Kind of.*/
    
    /*switches*/
    carbswitchstate = digitalRead(carbswitch);
    if (RPM > 1400.0 && carbswitchstate == LOW) {
      digitalWrite(outputswitch, HIGH);
    }
    else {
      digitalWrite(outputswitch, LOW);
    }
    valvestate = digitalRead(outputswitch);
    if (valvestate == HIGH) {
      econstr = ("ON");
    }
    else {
      econstr = ("OFF");
    }
    
    /*LCD*/

    lcd.clear();
    lcd.print("RPM=");
    lcd.print(RPM,0);
    lcd.setCursor(0, 1);
    lcd.print ("ECON=");
    lcd.print(econstr);

    attachInterrupt(coilpin, RPM_fun, FALLING);
  }
  if( timetemp >= 10*sampletime)
  {
    int ADCValueEng = analogRead(lm36engine);
    float VEng = (ADCValueEng * 3.3)/1024;
    float TempEng = (VEng - 0.5) * 100;
    int ADCVoltage = analogRead (voltagepin);
    float tmpvoltage = (ADCVoltage * 3.3)/1024;
    float voltage = tmpvoltage * 5.7;
      
    if (RPM > 4900.0) {
      digitalWrite(shiftlight, HIGH);
    }
    else {
      digitalWrite(shiftlight, LOW);
    }
    if (RPM > 500.0 && RPM < 5000) {
      digitalWrite(engineok, HIGH);
    }
    else {
      digitalWrite(engineok, LOW);
    }
    if (TempEng < 15.0) {
      digitalWrite(heateroutput, HIGH);
    }
    else {
      digitalWrite(heateroutput, LOW);
    }
    lcd.setCursor(9, 0);
    lcd.print (" U=");
    lcd.print(voltage,1);
    lcd.print("V");
    lcd.setCursor(9, 1);
    lcd.print (" T=");
    lcd.print(TempEng,0);
    lcd.print("C");
  }
}

void RPM_fun() 
{
  timeold = timenow;
  timenow = micros();
  duration = timenow-timeold;
}

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...