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-12-05
Revision:
5:6ca3e7ffc553
Parent:
3:43cb067ecd00

File content as of revision 5:6ca3e7ffc553:

#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;
    Set_Speed_Lever    (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::Set_Speed_Lever  (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
            Set_Speed_Lever    (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;
            Set_Speed_Lever    (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;
                Set_Speed_Lever    (servo_position);   //  Update signal to engine throttle control servo
            }
            break;
        default:
            break;
    }
}