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; } }