I could use some help/Thoughts/Guidance on a project i am working on.
Project description:
I have an secondary automotive fuel injection system that i am trying to monitor. The system works off of the car's boost pressure to initiate a progressive fuel injection system. In my specific case, once the car starts making 12 LBS of boost a progressive controller uses a 12V 300-PSI UHO pump to inject fuel through nozzles mounted into the intake manifold. This adds additional fuel to increase the car's performance, Horsepower. My system is setup to start pumping at 12lbs of boost and progressively increase until 20lbs of boost. Once it detects 20lbs of boost the pump/system is at 100% .
The limitation of the system, is it is a 2ndary system not monitored by the car's ecu, so failure of the system often will lead to major internal engine damage, up to full engine failure and rapid destructive disassembly of the engine.
So the idea is to monitor the signal that is being sent to the pump via the injection systems controller, and compare that signal to a pressure sensor mounted just before the injection nozzle(s). If the nozzle pressure is too low or high, send a 5 volt signal out. (to be used to tell the car to reduce the boost pressure in a kill circuit i am working on).
System Information:
The signal the fuel system controller sends to the pump is a 12v DC PWM signal @ 16khz.
The approximant PSI readings at the nozzle are 160 +/- PSI @ 100% duty cycle.
I am using a 0-200 PSI 0-5V (.5-4.5V) pressure transducer/sensor.
Arduino Uno (hoping nano in future to reduce size).
DataLogger sheild to capture data. This will be used later on to decide fault limits/calculation.
Optocoupler circuit to duplicate the 12v signal to a 5 volt signal in order to safely read the signal with the aurdrino.
I2C LCD screen, to display readings.
Couple led's, to display status.
Current Project Status:
I actually have a couple "working" versions of this system. But each one has its "issues". I am looking for some help, or direction i should persue.
All of the issues are related to capturing the 16khz DC PWM signal.
Version and Issues:
V1: Note this is a stripped down code for debugging. This works great as a bench test jumping pin through the optocoupler into pin 2. Pin 6 is about 976 HZ.
But once i attempt it on the 16khz signal it reads 100% duty cycle. (i think this method just cant ready a signal that fast??)
:
// Libraries
#include <LiquidCrystal_I2C.h> // LCD Screen
#include <SPI.h> // Communicate with SDI devices
#include <SD.h> // SD Card
#include <Wire.h> // must be included here so that Arduino library object file references work
#include "RTClib.h" // Real Time Clock
//LCD Address
LiquidCrystal_I2C lcd(0x27, 20, 4); // set the LCD address to 0x3F for a 16 chars and 2 line display
byte copyrightSymbol[] = {
B01110,
B10001,
B11111,
B11001,
B11001,
B11111,
B10001,
B01110
};
// Interrupt / Pump Variables
unsigned long fall_Time; // Placeholder for microsecond time when last falling edge occured.
unsigned long rise_Time; // Placeholder for microsecond time when last rising edge occured.
volatile int frequency = 0; // Calculated frequency.
volatile byte dutyCycle = 0; // Duty Cycle %
volatile byte isr0Tick = 0; // Increments every ISR call (used to know if ISR fired since last read)
const int pumpInPin = 2, pumpLEDPin = 7; // Pump in Pin, LED output Pin
float dCR, fR;
//Pump Signal Test System
const int pWMOutPin = 6;
float pWMOut = 125;
void PinChangeISR0()
{
unsigned long total_Time;
unsigned long on_Time;
unsigned long lastRead = micros(); // Get current time
isr0Tick++; // Kick the tires to say we have been here
if (digitalRead(pumpInPin) == LOW) // Falling edge
{
fall_Time = lastRead; // Just store falling edge and calculate on rising edge
} else { // Rising edge
total_Time = lastRead - rise_Time; // Get total cycle time
frequency = 1000000 / total_Time; // Calulate frequency and store
on_Time = fall_Time - rise_Time; // Get on time during this cycle
dutyCycle = 100 / ((float)total_Time / on_Time); // Convert to a percentage
rise_Time = lastRead; // Store rise time
}
}
void showPressure()
{
lcd.setCursor(3, 2);
lcd.print("System Status=");
lcd.setCursor(0, 3);
lcd.print("DC:");
lcd.setCursor(3, 3);
lcd.print(dutyCycle, 1);
lcd.print(" ");
lcd.setCursor(8, 3);
lcd.print("%");
lcd.setCursor(10, 3);
lcd.print("HZ:");
lcd.setCursor(13, 3);
lcd.print(frequency, 1);
lcd.print(" ");
}
//Setup
void setup()
{
//LCD Setup
lcd.init();
lcd.clear();
lcd.backlight();
lcd.createChar(0, copyrightSymbol);
//Serial Setup
Serial.begin(9600);
Serial.println(F("Sensor is Starting Up"));
//Pump_Interrupt Setup
pinMode(pumpInPin, INPUT); // Interrupt Pin
attachInterrupt(0, PinChangeISR0, CHANGE); // Attach interrupt handler 0 = Pin 2
pinMode(pumpLEDPin, OUTPUT);
//Pump test System
pinMode(pWMOutPin, OUTPUT);
showPressure();
}
void loop()
{
showPressure();
Serial.print("Frequency:");
Serial.print(frequency);
Serial.print("dutycycle");
Serial.println(dutyCycle);
//PWM for Pump Signal Simulation
analogWrite(pWMOutPin, pWMOut);
//Frequency and Duty Cycle
static byte oldisr0Tick = isr0Tick;
if (oldisr0Tick != isr0Tick) // ISR has fired so use it's values
{
oldisr0Tick = isr0Tick;
} else { // No interrupt since last read so must be 0% or 100%
if (digitalRead(2) == LOW)
{
frequency = 0.00;
dutyCycle = 0.00;
} else {
frequency = 0;
dutyCycle = 100;
}
}
if (dutyCycle > 0)
{
digitalWrite(pumpLEDPin, HIGH);
} else {
digitalWrite(pumpLEDPin, LOW);
}
}
V2. This one actually works with the 16khz signal. But its just too slow capture the signal. It also seems to be affected when i start adding additional functions to the program/loop (which i would could be ok with).
:
// Libraries
#include <LiquidCrystal_I2C.h> // LCD Screen
#include <SPI.h> // Communicate with SDI devices
#include <SD.h> // SD Card
#include <Wire.h> // must be included here so that Arduino library object file references work
#include "RTClib.h" // Real Time Clock
//LCD Address
LiquidCrystal_I2C lcd(0x27, 20, 4); // set the LCD address to 0x3F for a 16 chars and 2 line display
byte copyrightSymbol[] = {
B01110,
B10001,
B11111,
B11001,
B11001,
B11111,
B10001,
B01110
};
/* Pump DC setup
* "PWMIn" is Pulse in
* DC is Calculated Duty Cycle %
* Will be used on "REAL" Circuit*/
#define DISPLAY_INTERVAL 1000000L
#define IRQPIN 2
unsigned long DCRCurrent;
volatile unsigned int count = 0;
volatile unsigned int ovf = 0;
unsigned long frequency = 0;
unsigned long highCount = 0;
unsigned long totalCount = 0;
float dutyCycle = 0;
unsigned long lastDisplay = 0;
float PWMIn, DC, dCR;
unsigned long DCRPrevious;
float HZ, fR;
float DCSignal;
//Pump Signal Test System
const int pWMOutPin = 6;
float pWMOut = 125;
void showPressure()
{
lcd.setCursor(3, 2);
lcd.print("System Status=");
lcd.setCursor(0, 3);
lcd.print("DC:");
lcd.setCursor(3, 3);
lcd.print(dutyCycle, 1);
lcd.print(" ");
lcd.setCursor(8, 3);
lcd.print("%");
lcd.setCursor(10, 3);
lcd.print("HZ:");
lcd.setCursor(13, 3);
lcd.print(frequency, 1);
lcd.print(" ");
}
void pulse()
{
if (PIND & 0x04)
{
TCNT1 = 0x0000;
ovf = 0;
}
else
{
highCount += TCNT1;
highCount += ovf*65536L;
}
count++;
}
//Setup
void setup()
{
//LCD Setup
lcd.init();
lcd.clear();
lcd.backlight();
lcd.createChar(0, copyrightSymbol);
//Serial Setup
Serial.begin(9600);
Serial.println(F("Sensor is Starting Up"));
//Pump_Interrupt Setup
pinMode(IRQPIN, INPUT);
attachInterrupt(0, pulse, CHANGE);
TIMSK1 = (1 << TOIE1); // timer overflow interrupt enabled
TCCR1A = 0x00; //
TCCR1B = 1; // prescaler == 1 => timer runs at clock speed: 16 MHz
//Pump test System
pinMode(pWMOutPin, OUTPUT);
showPressure();
}
void loop()
{
showPressure();
Serial.print("Frequency:");
Serial.print(frequency);
Serial.print("dutycycle");
Serial.println(dutyCycle);
//PWM for Pump Signal Simulation
analogWrite(pWMOutPin, pWMOut);
// Pump DC Simulation
// 0-255 is 0-100% DC (use PWM var. in setup to adjust and test as needed)
DCRCurrent= millis();
DCSignal= digitalRead(2);
uint32_t now = micros();
if (now - lastDisplay > DISPLAY_INTERVAL)
{
lastDisplay = now;
cli();
{
frequency = count;
count = 0;
totalCount = highCount;
highCount = 0;
}
sei();
frequency /= 2;
dutyCycle = totalCount/160000.0;
}
}
ISR(TIMER1_OVF_vect)
{
ovf++;
}
So my last thought, was instead of measuring the duty cycle directly. Why not just convert the digital signal into an "analog" signal? Then read the signal from an analog pin. Obviously that comes with its own "issues". I started working on "making" a low-pass filter to clean the digital pulse into a constant voltage. But i have no idea how to calculate the correct resistors, to use and the only capacitors i have are the Ceramic 104's and 10uf 50v capacitor's.
I am a mechanical engineer by trade, and electronics are outside my expertise. Also new to programming/self taught for about 4 months now. So please excuse my ignorance on this. I am still learning how to properly use the internal timers for a project like this.
Unfortunaly i also do not have a signal generator to bench test the system.
Thanks in advance.
Chris
