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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/field.cpp	Mon Jul 27 08:44:59 2020 +0000
@@ -0,0 +1,105 @@
+#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
+}
+