Code for 'Smart Regulator' featured in 'Model Engineer', November 2020 on. Contains all work to August 2020 including all code described. Top level algorithm development is quite spares, leaving some work for you! Any questions - jon@jons-workshop.com
Dependencies: mbed BufferedSerial Servo2 PCT2075 I2CEeprom FastPWM
rpm.cpp
- Committer:
- JonFreeman
- Date:
- 2020-07-27
- Revision:
- 3:43cb067ecd00
- Child:
- 5:6ca3e7ffc553
File content as of revision 3:43cb067ecd00:
#include "mbed.h"
#include "rpm.h"
#include "Alternator.h"
//#include "BufferedSerial.h"
//extern BufferedSerial pc;
extern ee_settings_2020 user_settings;
Engine_Manager::Engine_Manager (PinName ms, PinName cm, PinName for_servo) : MagnetoSignal(ms), CleanedMagneto(cm), Speed_ControlServo(for_servo)
{
// Constructor
magneto_stretch = false;
rpm_in_run_range = false;
latest_RPM_reading = core_call_count = 0;
last_update_time = 0;
filtered_measured_RPM = requested_RPM = 0.0;
MagnetoSignal.rise (callback(this, &Engine_Manager::MagRise)); // Attach handler to the rising interruptIn edge
MagnetoSignal.fall (callback(this, &Engine_Manager::MagFall)); // Attach handler to the rising interruptIn edge
// microsecs.reset () ; // timer = 0
// microsecs.start () ; // 64 bit, counts micro seconds and times out in half million years
servo_position = MIN_WORKING_THROTTLE;
Speed_Control (MIN_WORKING_THROTTLE);
};
void Engine_Manager::magneto_timeoutC ()
{
magneto_stretch = false; // Magneto ringing finished by now, re-enable magneto pulse count
CleanedMagneto = 0; // Cleaned tacho output brought out to pin to look at with scope
}
void Engine_Manager::MagRise ()
{
if (!magneto_stretch) { // May get this interrupt more than once per magneto pulse, respond to first, lock out subsequent
// until magneto_timeout time has elapsed
magneto_stretch = true; // Set flag preventing repeat interrupts until after Timeout interrupt handler clears it 5ms later
new_time = microsecs.read_high_resolution_us();
magt0 = new_time - magt1; // microsecs between most recent two sparks
magt1 = new_time; // actual time microsecs of most recent spark
magneto_timoC.attach_us (callback(this, &Engine_Manager::magneto_timeoutC), DEBOUNCE_US); // To ignore ringing and multiple counts on magneto output, all settled within about 5ms
CleanedMagneto = 1; // Cleaned tacho output brought out to pin to look at with scope
}
}
void Engine_Manager::MagFall () // Need only rising or falling edge, not both
{
}
void Engine_Manager::RPM_update ()
{
last_update_time = microsecs.read_high_resolution_us();
time_since_last_spark = last_update_time - magt1;
if (time_since_last_spark > 500000) { // engine probably not running
latest_RPM_reading = 0;
running_flag = false;
}
else {
running_flag = true;
latest_RPM_reading = (60000000 / magt0);
}
filtered_measured_RPM *= (1.0 - RPM_FILTER_FACTOR);
filtered_measured_RPM += RPM_FILTER_FACTOR * (double)latest_RPM_reading;
}
bool Engine_Manager::running () // Answers question is engine running?
{
return running_flag;
}
uint32_t Engine_Manager::RPM_latest ()
{
return latest_RPM_reading;
}
uint32_t Engine_Manager::RPM_filtered ()
{
return (uint32_t)filtered_measured_RPM;
}
void Engine_Manager::Speed_Control (double th) // Make this private once all throttle refs local
{
if (user_settings.rd(SERVO_DIR) == 0)
Speed_ControlServo = th;
else
Speed_ControlServo = 1.0 - th;
}
uint32_t Engine_Manager::set_RPM_literal (uint32_t new_rpm) // Returns latest measured RPM
{
if (new_rpm > MAX_RPM)
new_rpm = MAX_RPM;
if (new_rpm >= MIN_RPM) { // Runnable or make runnable
if (!rpm_in_run_range) { // Transition from idle to working revs
rpm_in_run_range = true;
servo_position = MIN_WORKING_THROTTLE; // set throttle to probably somewhere near low useful revs
Speed_Control (MIN_WORKING_THROTTLE);
} // Endof transition from idle to working revs
} // Endof Runnable or make runnable
else { // requested rpm are below useful, so kill engine
if (rpm_in_run_range) { // detect transition from useful work to idle
servo_position = 0.0;
Speed_Control (0.0);
rpm_in_run_range = false;
}
}
requested_RPM = (double) new_rpm;
return latest_RPM_reading;
}
uint32_t Engine_Manager::set_RPM_percent (uint32_t new_percent)
{ // Returns latest measured RPM
uint32_t temp = 0;
if (new_percent > 100) new_percent = 100;
if (new_percent != 0)
temp = MIN_RPM + (((MAX_RPM - MIN_RPM) * new_percent) / 100);
return set_RPM_literal (temp);
}
uint32_t Engine_Manager::RPM_percent_to_actual (uint32_t new_percent)
{ // Returns RPM percentage converted to actual RPM
uint32_t temp = 0;
if (new_percent > 100) new_percent = 100;
if (new_percent != 0)
temp = MIN_RPM + (((MAX_RPM - MIN_RPM) * new_percent) / 100);
return temp;
}
uint32_t Engine_Manager::get_RPM_requested () // Returns latest requested RPM
{
return (uint32_t) requested_RPM;
}
double Engine_Manager::get_servo_position () // Only call from test printf in main
{
return servo_position;
}
void Engine_Manager::manager_core() // Call this at 100 Hz
{
uint32_t i = ++core_call_count;
int32_t rpm_error; // signed
RPM_update (); // Updates filtered and unfiltered RPM measured, 100 times per sec
i &= 7;
switch (i) {
case 1: // Doing this at 12.5 Hz
rpm_error = filtered_measured_RPM - requested_RPM; // all doubles. Speed error is positive
if (rpm_in_run_range && (abs(rpm_error) > RPM_DEADBAND)) { // Try to adjust speed if not within margin
servo_position -= rpm_error / RPM_ERR_DENOM; //Denominator sets gain, the 'P', Proportional as in PID
if (servo_position < MIN_WORKING_THROTTLE) // Keep servo_position within limits
servo_position = MIN_WORKING_THROTTLE;
if (servo_position > MAX_WORKING_THROTTLE)
servo_position = MAX_WORKING_THROTTLE;
Speed_Control (servo_position); // Update signal to engine throttle control servo
}
break;
default:
break;
}
}