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

Revision:
3:43cb067ecd00
Child:
5:6ca3e7ffc553
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rpm.cpp	Mon Jul 27 08:44:59 2020 +0000
@@ -0,0 +1,161 @@
+#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;
+    }
+}
+
+
+
+