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
field.cpp
- Committer:
- JonFreeman
- Date:
- 2020-07-27
- Revision:
- 3:43cb067ecd00
File content as of revision 3:43cb067ecd00:
#include "mbed.h" #include "rpm.h" #include "field.h" #include "Alternator.h" #include "BufferedSerial.h" extern BufferedSerial pc; extern ee_settings_2020 user_settings; extern Timer microsecs; // 64 bit counter, rolls over in half million years FieldControl::FieldControl (PinName pwmoscin, PinName vext) : pwm_osc_in (pwmoscin), V_ext (vext) { pwm_osc_in.period_us (PWM_PERIOD_US); // about 313Hz * 2 pwm_osc_in.pulsewidth_us (PWM_PERIOD_US - 2); V_ext.rise (callback(this, &FieldControl::VextRise)); // Attach handler to the rising interruptIn edge V_ext.fall (callback(this, &FieldControl::VextFall)); // Attach handler to the falling interruptIn edge t_on = t_off = measured_pw_us = measured_period = rise_count = fall_count = 0L; // maketable () ; set_for_speed (0); old_percent = 1000; // i.e. out of percentage range to force exec on first pass } void FieldControl::VextRise () // InterruptIn interrupt service { // Here is possible to read back how regulator has controlled pwm - may or may not be useful uint64_t tmp = microsecs.read_high_resolution_us(); measured_period = tmp - t_on; t_on = tmp; rise_count++; } void FieldControl::VextFall () // InterruptIn interrupt service { fall_count++; t_off = microsecs.read_high_resolution_us(); measured_pw_us = t_off - t_on; } double FieldControl::get_duty_ratio () { if (measured_period == 0) { if (V_ext == 1) return 2.0; // Impossible value indicates error return -1.0; // Impossible value indicates error } return (double) measured_pw_us / (double) measured_period; } ; void FieldControl::maketable () { // Uses first 17 nums of user_settings relating to lim to be applied at 0, 500, 1000 --- 8000 RPM // Makes table of 400 entries to 8000 RPM regardless of engine type data. 20 RPM per fine step // 21 User input table entries 200 RPM apart for MAX_RPM_LIMIT up to 4000, 400 RPM for up to 8000 double tabvals[28]; double diff, val = 0.0; uint32_t tabptr = 0, user_entry_rpm_step, divor; user_entry_rpm_step = MAX_RPM_LIMIT > 4000 ? 400 : 200; divor = MAX_RPM_LIMIT > 4000 ? 20 : 20; for (int i = 0; i < 21; i++) { tabvals[i] = (double)user_settings.rd (i); pc.printf ("%d\t%.0f\r\n", i * user_entry_rpm_step, tabvals[i]); } for (int i = 1; i < 21; i++) { diff = tabvals[i] - tabvals[i - 1]; // Either 200 or 400 RPM apart, either 10 or 20 small steps to fill diff /= divor; // 50 entries 20RPM apart per kRPM for (int j = 0; j < divor; j++) { privatemadetab[tabptr++] = (uint8_t) val; val += diff; } } pc.printf ("Final tab entry posn %d, calculated = %d percent, divor = %d\r\n", tabptr, (uint8_t) val, divor); while (tabptr < sizeof(privatemadetab)) privatemadetab[tabptr++] = (uint8_t) val; } uint32_t FieldControl::set_for_speed (uint32_t rpm) { // May 2020 uint32_t index, pcent; double pwm = 0.0; if (rpm > MAX_RPM_LIMIT) rpm = MAX_RPM_LIMIT; // index = rpm / 25; // to fit lut spacing of 25rpm intervals, turns rpm into index index = rpm / 10; // to fit lut spacing of 10rpm intervals, turns rpm into index pcent = privatemadetab[index]; if (pcent != old_percent) { old_percent = pcent; pwm = (double)pcent; pwm /= 100.0; set_pwm (pwm); } return old_percent; } extern double Read_Field_Volts () ; void FieldControl::set_pwm (double d) { double t = Read_Field_Volts () ; if (t > ALTERNATOR_DESIGN_VOLTAGE) t = ALTERNATOR_DESIGN_VOLTAGE / t; else t = 1.0; uint32_t i; if (d < 0.0) d = 0.0; if (d > 1.0) d = 1.0; i = (uint32_t)(d * t * (double)PWM_PERIOD_US); // pwm_factor 0.5 when using 12v alternator in 24v system pwm_osc_in.pulsewidth_us (PWM_PERIOD_US - i); // Note PWM is inverted as MCP1630 uses inverted OSC_IN signal }