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

Committer:
JonFreeman
Date:
Sat Dec 05 12:40:17 2020 +0000
Revision:
5:6ca3e7ffc553
Parent:
3:43cb067ecd00
Code for 'Smart Regulator' to August 2020, published as is. Basic low-level functions all thoroughly tested and debugged, top level algorithms have scope for further development - over to you! For help contact jon @ jons-workshop[.com

Who changed what in which revision?

UserRevisionLine numberNew contents of line
JonFreeman 3:43cb067ecd00 1 #include "mbed.h"
JonFreeman 3:43cb067ecd00 2 #include "rpm.h"
JonFreeman 3:43cb067ecd00 3 #include "Alternator.h"
JonFreeman 3:43cb067ecd00 4 //#include "BufferedSerial.h"
JonFreeman 3:43cb067ecd00 5 //extern BufferedSerial pc;
JonFreeman 3:43cb067ecd00 6 extern ee_settings_2020 user_settings;
JonFreeman 3:43cb067ecd00 7
JonFreeman 3:43cb067ecd00 8 Engine_Manager::Engine_Manager (PinName ms, PinName cm, PinName for_servo) : MagnetoSignal(ms), CleanedMagneto(cm), Speed_ControlServo(for_servo)
JonFreeman 3:43cb067ecd00 9 {
JonFreeman 3:43cb067ecd00 10 // Constructor
JonFreeman 3:43cb067ecd00 11 magneto_stretch = false;
JonFreeman 3:43cb067ecd00 12 rpm_in_run_range = false;
JonFreeman 3:43cb067ecd00 13 latest_RPM_reading = core_call_count = 0;
JonFreeman 3:43cb067ecd00 14 last_update_time = 0;
JonFreeman 3:43cb067ecd00 15 filtered_measured_RPM = requested_RPM = 0.0;
JonFreeman 3:43cb067ecd00 16 MagnetoSignal.rise (callback(this, &Engine_Manager::MagRise)); // Attach handler to the rising interruptIn edge
JonFreeman 3:43cb067ecd00 17 MagnetoSignal.fall (callback(this, &Engine_Manager::MagFall)); // Attach handler to the rising interruptIn edge
JonFreeman 3:43cb067ecd00 18 // microsecs.reset () ; // timer = 0
JonFreeman 3:43cb067ecd00 19 // microsecs.start () ; // 64 bit, counts micro seconds and times out in half million years
JonFreeman 3:43cb067ecd00 20 servo_position = MIN_WORKING_THROTTLE;
JonFreeman 5:6ca3e7ffc553 21 Set_Speed_Lever (MIN_WORKING_THROTTLE);
JonFreeman 3:43cb067ecd00 22 };
JonFreeman 3:43cb067ecd00 23
JonFreeman 3:43cb067ecd00 24 void Engine_Manager::magneto_timeoutC ()
JonFreeman 3:43cb067ecd00 25 {
JonFreeman 3:43cb067ecd00 26 magneto_stretch = false; // Magneto ringing finished by now, re-enable magneto pulse count
JonFreeman 3:43cb067ecd00 27 CleanedMagneto = 0; // Cleaned tacho output brought out to pin to look at with scope
JonFreeman 3:43cb067ecd00 28 }
JonFreeman 3:43cb067ecd00 29
JonFreeman 3:43cb067ecd00 30 void Engine_Manager::MagRise ()
JonFreeman 3:43cb067ecd00 31 {
JonFreeman 3:43cb067ecd00 32 if (!magneto_stretch) { // May get this interrupt more than once per magneto pulse, respond to first, lock out subsequent
JonFreeman 3:43cb067ecd00 33 // until magneto_timeout time has elapsed
JonFreeman 3:43cb067ecd00 34 magneto_stretch = true; // Set flag preventing repeat interrupts until after Timeout interrupt handler clears it 5ms later
JonFreeman 3:43cb067ecd00 35 new_time = microsecs.read_high_resolution_us();
JonFreeman 3:43cb067ecd00 36 magt0 = new_time - magt1; // microsecs between most recent two sparks
JonFreeman 3:43cb067ecd00 37 magt1 = new_time; // actual time microsecs of most recent spark
JonFreeman 3:43cb067ecd00 38 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
JonFreeman 3:43cb067ecd00 39 CleanedMagneto = 1; // Cleaned tacho output brought out to pin to look at with scope
JonFreeman 3:43cb067ecd00 40 }
JonFreeman 3:43cb067ecd00 41 }
JonFreeman 3:43cb067ecd00 42
JonFreeman 3:43cb067ecd00 43 void Engine_Manager::MagFall () // Need only rising or falling edge, not both
JonFreeman 3:43cb067ecd00 44 {
JonFreeman 3:43cb067ecd00 45 }
JonFreeman 3:43cb067ecd00 46
JonFreeman 3:43cb067ecd00 47 void Engine_Manager::RPM_update ()
JonFreeman 3:43cb067ecd00 48 {
JonFreeman 3:43cb067ecd00 49 last_update_time = microsecs.read_high_resolution_us();
JonFreeman 3:43cb067ecd00 50 time_since_last_spark = last_update_time - magt1;
JonFreeman 3:43cb067ecd00 51 if (time_since_last_spark > 500000) { // engine probably not running
JonFreeman 3:43cb067ecd00 52 latest_RPM_reading = 0;
JonFreeman 3:43cb067ecd00 53 running_flag = false;
JonFreeman 3:43cb067ecd00 54 }
JonFreeman 3:43cb067ecd00 55 else {
JonFreeman 3:43cb067ecd00 56 running_flag = true;
JonFreeman 3:43cb067ecd00 57 latest_RPM_reading = (60000000 / magt0);
JonFreeman 3:43cb067ecd00 58 }
JonFreeman 3:43cb067ecd00 59 filtered_measured_RPM *= (1.0 - RPM_FILTER_FACTOR);
JonFreeman 3:43cb067ecd00 60 filtered_measured_RPM += RPM_FILTER_FACTOR * (double)latest_RPM_reading;
JonFreeman 3:43cb067ecd00 61 }
JonFreeman 3:43cb067ecd00 62
JonFreeman 3:43cb067ecd00 63 bool Engine_Manager::running () // Answers question is engine running?
JonFreeman 3:43cb067ecd00 64 {
JonFreeman 3:43cb067ecd00 65 return running_flag;
JonFreeman 3:43cb067ecd00 66 }
JonFreeman 3:43cb067ecd00 67
JonFreeman 3:43cb067ecd00 68 uint32_t Engine_Manager::RPM_latest ()
JonFreeman 3:43cb067ecd00 69 {
JonFreeman 3:43cb067ecd00 70 return latest_RPM_reading;
JonFreeman 3:43cb067ecd00 71 }
JonFreeman 3:43cb067ecd00 72
JonFreeman 3:43cb067ecd00 73 uint32_t Engine_Manager::RPM_filtered ()
JonFreeman 3:43cb067ecd00 74 {
JonFreeman 3:43cb067ecd00 75 return (uint32_t)filtered_measured_RPM;
JonFreeman 3:43cb067ecd00 76 }
JonFreeman 3:43cb067ecd00 77
JonFreeman 5:6ca3e7ffc553 78 void Engine_Manager::Set_Speed_Lever (double th) // Make this private once all throttle refs local
JonFreeman 3:43cb067ecd00 79 {
JonFreeman 3:43cb067ecd00 80 if (user_settings.rd(SERVO_DIR) == 0)
JonFreeman 3:43cb067ecd00 81 Speed_ControlServo = th;
JonFreeman 3:43cb067ecd00 82 else
JonFreeman 3:43cb067ecd00 83 Speed_ControlServo = 1.0 - th;
JonFreeman 3:43cb067ecd00 84 }
JonFreeman 3:43cb067ecd00 85
JonFreeman 3:43cb067ecd00 86 uint32_t Engine_Manager::set_RPM_literal (uint32_t new_rpm) // Returns latest measured RPM
JonFreeman 3:43cb067ecd00 87 {
JonFreeman 3:43cb067ecd00 88 if (new_rpm > MAX_RPM)
JonFreeman 3:43cb067ecd00 89 new_rpm = MAX_RPM;
JonFreeman 3:43cb067ecd00 90 if (new_rpm >= MIN_RPM) { // Runnable or make runnable
JonFreeman 3:43cb067ecd00 91 if (!rpm_in_run_range) { // Transition from idle to working revs
JonFreeman 3:43cb067ecd00 92 rpm_in_run_range = true;
JonFreeman 3:43cb067ecd00 93 servo_position = MIN_WORKING_THROTTLE; // set throttle to probably somewhere near low useful revs
JonFreeman 5:6ca3e7ffc553 94 Set_Speed_Lever (MIN_WORKING_THROTTLE);
JonFreeman 3:43cb067ecd00 95 } // Endof transition from idle to working revs
JonFreeman 3:43cb067ecd00 96 } // Endof Runnable or make runnable
JonFreeman 3:43cb067ecd00 97 else { // requested rpm are below useful, so kill engine
JonFreeman 3:43cb067ecd00 98 if (rpm_in_run_range) { // detect transition from useful work to idle
JonFreeman 3:43cb067ecd00 99 servo_position = 0.0;
JonFreeman 5:6ca3e7ffc553 100 Set_Speed_Lever (0.0);
JonFreeman 3:43cb067ecd00 101 rpm_in_run_range = false;
JonFreeman 3:43cb067ecd00 102 }
JonFreeman 3:43cb067ecd00 103 }
JonFreeman 3:43cb067ecd00 104 requested_RPM = (double) new_rpm;
JonFreeman 3:43cb067ecd00 105 return latest_RPM_reading;
JonFreeman 3:43cb067ecd00 106 }
JonFreeman 3:43cb067ecd00 107
JonFreeman 3:43cb067ecd00 108 uint32_t Engine_Manager::set_RPM_percent (uint32_t new_percent)
JonFreeman 3:43cb067ecd00 109 { // Returns latest measured RPM
JonFreeman 3:43cb067ecd00 110 uint32_t temp = 0;
JonFreeman 3:43cb067ecd00 111 if (new_percent > 100) new_percent = 100;
JonFreeman 3:43cb067ecd00 112 if (new_percent != 0)
JonFreeman 3:43cb067ecd00 113 temp = MIN_RPM + (((MAX_RPM - MIN_RPM) * new_percent) / 100);
JonFreeman 3:43cb067ecd00 114 return set_RPM_literal (temp);
JonFreeman 3:43cb067ecd00 115 }
JonFreeman 3:43cb067ecd00 116
JonFreeman 3:43cb067ecd00 117 uint32_t Engine_Manager::RPM_percent_to_actual (uint32_t new_percent)
JonFreeman 3:43cb067ecd00 118 { // Returns RPM percentage converted to actual RPM
JonFreeman 3:43cb067ecd00 119 uint32_t temp = 0;
JonFreeman 3:43cb067ecd00 120 if (new_percent > 100) new_percent = 100;
JonFreeman 3:43cb067ecd00 121 if (new_percent != 0)
JonFreeman 3:43cb067ecd00 122 temp = MIN_RPM + (((MAX_RPM - MIN_RPM) * new_percent) / 100);
JonFreeman 3:43cb067ecd00 123 return temp;
JonFreeman 3:43cb067ecd00 124 }
JonFreeman 3:43cb067ecd00 125
JonFreeman 3:43cb067ecd00 126 uint32_t Engine_Manager::get_RPM_requested () // Returns latest requested RPM
JonFreeman 3:43cb067ecd00 127 {
JonFreeman 3:43cb067ecd00 128 return (uint32_t) requested_RPM;
JonFreeman 3:43cb067ecd00 129 }
JonFreeman 3:43cb067ecd00 130
JonFreeman 3:43cb067ecd00 131 double Engine_Manager::get_servo_position () // Only call from test printf in main
JonFreeman 3:43cb067ecd00 132 {
JonFreeman 3:43cb067ecd00 133 return servo_position;
JonFreeman 3:43cb067ecd00 134 }
JonFreeman 3:43cb067ecd00 135
JonFreeman 3:43cb067ecd00 136 void Engine_Manager::manager_core() // Call this at 100 Hz
JonFreeman 3:43cb067ecd00 137 {
JonFreeman 3:43cb067ecd00 138 uint32_t i = ++core_call_count;
JonFreeman 3:43cb067ecd00 139 int32_t rpm_error; // signed
JonFreeman 3:43cb067ecd00 140 RPM_update (); // Updates filtered and unfiltered RPM measured, 100 times per sec
JonFreeman 3:43cb067ecd00 141 i &= 7;
JonFreeman 3:43cb067ecd00 142 switch (i) {
JonFreeman 3:43cb067ecd00 143 case 1: // Doing this at 12.5 Hz
JonFreeman 3:43cb067ecd00 144 rpm_error = filtered_measured_RPM - requested_RPM; // all doubles. Speed error is positive
JonFreeman 3:43cb067ecd00 145 if (rpm_in_run_range && (abs(rpm_error) > RPM_DEADBAND)) { // Try to adjust speed if not within margin
JonFreeman 3:43cb067ecd00 146 servo_position -= rpm_error / RPM_ERR_DENOM; //Denominator sets gain, the 'P', Proportional as in PID
JonFreeman 3:43cb067ecd00 147 if (servo_position < MIN_WORKING_THROTTLE) // Keep servo_position within limits
JonFreeman 3:43cb067ecd00 148 servo_position = MIN_WORKING_THROTTLE;
JonFreeman 3:43cb067ecd00 149 if (servo_position > MAX_WORKING_THROTTLE)
JonFreeman 3:43cb067ecd00 150 servo_position = MAX_WORKING_THROTTLE;
JonFreeman 5:6ca3e7ffc553 151 Set_Speed_Lever (servo_position); // Update signal to engine throttle control servo
JonFreeman 3:43cb067ecd00 152 }
JonFreeman 3:43cb067ecd00 153 break;
JonFreeman 3:43cb067ecd00 154 default:
JonFreeman 3:43cb067ecd00 155 break;
JonFreeman 3:43cb067ecd00 156 }
JonFreeman 3:43cb067ecd00 157 }
JonFreeman 3:43cb067ecd00 158
JonFreeman 3:43cb067ecd00 159
JonFreeman 3:43cb067ecd00 160
JonFreeman 3:43cb067ecd00 161