|
// Arduino PWM-based returnless fuel pressure regulator. |
|
// Copyright 2021 Jason Pepas. |
|
// Released under the terms of the MIT License, see https://opensource.org/licenses/MIT |
|
|
|
#include <stdint.h> |
|
typedef int pin_t; // an Arduino pin number. |
|
typedef int adc_t; // an Arduino 10-bit ADC value (0-1023). |
|
typedef int pwm_t; // an Arduino PWM output value (0-255). |
|
typedef int psi_t; // Pounds per square inch. |
|
|
|
// Pin assignments: |
|
pin_t g_transducerPin = 0; |
|
pin_t g_pumpPin = 1; |
|
pin_t g_errorLEDPin = 2; |
|
|
|
// Global state. These are initialized in setup(). |
|
adc_t g_targetPressureADC; |
|
pwm_t g_pumpPWM; |
|
|
|
void die() { |
|
while(1) { |
|
digitalWrite(g_errorLEDPin, HIGH); |
|
} |
|
} |
|
|
|
uint8_t shiftValueFor(uint8_t oversample) { |
|
switch (oversample) { |
|
case 2: return 1; |
|
case 4: return 2; |
|
case 8: return 3; |
|
case 16: return 4; |
|
case 32: return 5; |
|
case 64: return 6; |
|
default: die(); |
|
} |
|
} |
|
|
|
adc_t readFuelPressure(pin_t sensorPin) { |
|
// Note: arduino analogRead takes about 100us (10kHz) according to |
|
// http://yaab-arduino.blogspot.com/2015/02/fast-sampling-from-analog-input.html |
|
// An oversample rate of 64x fits 10-bit values neatly into a 16-bit unsigned accumulator, at about 150Hz. |
|
// Arduino PWM is typically 490Hz, so we will spit out at least three pulses before changing PWM value. |
|
uint8_t oversample = 64; |
|
uint16_t accumulator = 0; |
|
for (uint8_t i=0; i < oversample; i++) { |
|
int reading = analogRead(sensorPin); |
|
accumulator += reading; |
|
} |
|
accumulator = accumulator >> shiftValueFor(oversample); |
|
return (adc_t)accumulator; |
|
} |
|
|
|
int clamped(int value, int minimum, int maximum) { |
|
if (value < minimum) { |
|
return minimum; |
|
} else if (value > maximum) { |
|
return maximum; |
|
} else { |
|
return value; |
|
} |
|
} |
|
|
|
pwm_t calculatePumpPWM(pwm_t currentPWM, adc_t currentPressureADC, adc_t targetPressureADC) { |
|
pwm_t minimumPWM = 0; |
|
pwm_t maximumPWM = 255; |
|
adc_t pressureEStopADC = 600; |
|
if (currentPressureADC >= pressureEStopADC) { |
|
return 0; |
|
} else if ((currentPressureADC < targetPressureADC) && (currentPWM < maximumPWM)) { |
|
int delta = (targetPressureADC - currentPressureADC) > 1; |
|
pwm_t newPWM = currentPWM + delta; |
|
return clamped(newPWM, minimumPWM, maximumPWM); |
|
} else if ((currentPressureADC > targetPressureADC) && (currentPWM > minimumPWM)) { |
|
int delta = (currentPressureADC - targetPressureADC) > 1; |
|
pwm_t newPWM = currentPWM - delta; |
|
return clamped(newPWM, minimumPWM, maximumPWM); |
|
} else { |
|
return clamped(currentPWM, minimumPWM, maximumPWM); |
|
} |
|
} |
|
|
|
adc_t calculateTargetPressureADC(psi_t targetPSI) { |
|
float transducerScaleMin = 0.5; |
|
float transducerScaleMax = 4.5; |
|
float transducerFullScalePSI = 100; |
|
float targetPressureADC = ((transducerScaleMax - transducerScaleMin) / transducerFullScalePSI) * targetPSI; |
|
return (adc_t)targetPressureADC; |
|
} |
|
|
|
void setup() { |
|
g_transducerPin = 0; |
|
g_pumpPin = 1; |
|
g_pumpPWM = 0; |
|
|
|
psi_t targetPressurePSI = 45; |
|
g_targetPressureADC = calculateTargetPressureADC(targetPressurePSI); |
|
} |
|
|
|
void loop() { |
|
adc_t currentPressureADC = readFuelPressure(g_transducerPin); |
|
pwm_t pwm = calculatePumpPWM(g_pumpPWM, currentPressureADC, g_targetPressureADC); |
|
analogWrite(g_pumpPin, pwm); |
|
} |