Jon Freeman / Mbed 2 deprecated Alternator2020_06

Dependencies:   mbed BufferedSerial Servo2 PCT2075 I2CEeprom FastPWM

Files at this revision

API Documentation at this revision

Comitter:
JonFreeman
Date:
Sat Apr 25 15:35:58 2020 +0000
Parent:
0:77803b3ee157
Child:
2:8e7b51353f32
Commit message:
About to pick this up again late Apr 2020.;

Changed in this revision

Alternator.h Show annotated file Show diff for this revision Revisions of this file
BufferedSerial.lib Show annotated file Show diff for this revision Revisions of this file
Servo.lib Show annotated file Show diff for this revision Revisions of this file
cli.cpp Show annotated file Show diff for this revision Revisions of this file
i2c_bit_banged.cpp Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/Alternator.h	Fri Jun 28 19:32:51 2019 +0000
+++ b/Alternator.h	Sat Apr 25 15:35:58 2020 +0000
@@ -1,13 +1,30 @@
 #include    "Servo.h"
 #include    "BufferedSerial.h"
+
+#define SPEED_CONTROL_ENABLE    //  Includes engine revs servo control loop
+
+const   int TICKOVER_RPM = 2500;
+const   int MAX_RPM_LIMIT = 7500;
+const   double SERVO_MAX = 0.5;
 const   int eeprom_page = 17;   //  Determines where in eeprom 'settings' reside
 
 const   int lut_seg_size    =   60; //  steps per thousand RPM
 const   int lut_size = lut_seg_size * 8;    //  8 segments - 0-1, 1-2, 2-3, 3-4 etc 000 rpm
 
+class   VEXT_Data   {
+    public:
+    uint32_t    t_on, t_off, measured_pw_us, measured_period, rise_count, fall_count;
+    double      duty_cycle  ()  {
+        return  (double) measured_pw_us / (double) measured_period;
+    }   ;
+    VEXT_Data   ()  {   //  constructor
+        t_on = t_off = measured_pw_us =  measured_period = rise_count = fall_count = 0;
+    }   ;
+}   ;
+
 class   eeprom_settings {
     char        settings    [36];
-    int         max_pwm_lut [lut_size + 4];
+    double      max_pwm_lut [lut_size + 4];
 //    bool        rd_24LC64  (int start_addr, char * dest, int length)    ;
 //    bool        wr_24LC64  (int start_addr, char * dest, int length)    ;
 //    bool        set_24LC64_internal_address (int    start_addr) ;
@@ -15,8 +32,9 @@
     void        build_lut   ()  ;
   public:
     eeprom_settings (); //  Constructor
-    int         get_pwm (int)   ;
+    double      get_pwm (int)   ;
     char        rd  (uint32_t)  ;           //  Read one setup char value from private buffer 'settings'
+    bool        rd  (char *, uint32_t)  ;           //  Read one setup char value from private buffer 'settings'
     bool        wr  (char, uint32_t)  ;     //  Write one setup char value to private buffer 'settings'
     bool        save    ()  ;               //  Write 'settings' buffer to EEPROM
     bool        load    ()  ;               //  Get 'settings' from EEPROM
@@ -32,5 +50,12 @@
     const char * t;     //  description
 }   ;
 
-const   int PWM_PERIOD_US = 3200    ;
+const   int MAX_PARAMS = 10;
+struct  parameters  {
+    int32_t times[50];
+    int32_t position_in_list, last_time, numof_dbls;
+    double  dbl[MAX_PARAMS];
+}   ;
 
+const   int PWM_PERIOD_US = 2400    ;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BufferedSerial.lib	Sat Apr 25 15:35:58 2020 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/sam_grove/code/BufferedSerial/#7e5e866edd3d
--- a/Servo.lib	Fri Jun 28 19:32:51 2019 +0000
+++ b/Servo.lib	Sat Apr 25 15:35:58 2020 +0000
@@ -1,1 +1,1 @@
-https://os.mbed.com/users/simon/code/Servo/#4e3d7cb4d0a2
+https://os.mbed.com/users/simon/code/Servo/#94980819591d
--- a/cli.cpp	Fri Jun 28 19:32:51 2019 +0000
+++ b/cli.cpp	Sat Apr 25 15:35:58 2020 +0000
@@ -10,29 +10,30 @@
 #include <cctype>
 using namespace std;
 
-extern  eeprom_settings     mode     ;
+extern  eeprom_settings     user_settings     ;
 //eeprom_settings     mode     ;
 
-extern  int ver, vef, measured_pw_us;
+//extern  int ver, vef, measured_pw_us;
+extern  void    set_throttle_limit    (struct parameters & a)    ;
+extern  void    speed_control_factor_set    (struct parameters & a)    ;
+extern  void    query_system    (struct parameters & a)    ;
 extern  uint32_t    ReadEngineRPM  ()   ;
 extern  double  Read_BatteryVolts   ()  ;
+extern  void Read_Ammeter   (double *)  ;
 
 
 
 
 
-const   int MAX_PARAMS = 10;
-struct  parameters  {
-    int32_t times[50];
-    int32_t position_in_list, last_time, numof_dbls;
-    double  dbl[MAX_PARAMS];
-}   ;
-
 //  WithOUT RTOS
 //extern  BufferedSerial pc;
+
+#ifdef  TARGET_NUCLEO_L432KC    //
 extern  Serial pc;
-//extern  BufferedSerial pc;
-extern  double  test_pot;    //  These used in knifeandfork code testing only
+#else
+extern  BufferedSerial pc;
+#endif
+//extern  double  test_pot;    //  These used in knifeandfork code testing only
 
 //extern  int numof_eeprom_options2    ;
 //extern  struct  optpar const option_list2[]  ;
@@ -46,6 +47,7 @@
 */
 void    mode19_cmd (struct parameters & a)       //  With no params, reads eeprom contents. With params sets eeprom contents
 {
+
     char            temps[36];
     int i;
     pc.printf   ("\r\nmode - Set system data in EEPROM - Jan 2019\r\nSyntax 'mode' with no parameters lists current state.\r\n");
@@ -59,17 +61,17 @@
             case    0:    case    1:    case    2:    case    3:    case    4:
             case    5:    case    6:    case    7:    case    8:
                 if  (temps[1] >= option_list2[i].min && temps[1] <= option_list2[i].max)
-                    mode.wr(temps[1], RPM0 + i);
+                    user_settings.wr(temps[1], RPM0 + i);
                 break;
             case    37: //  set pwm scale factor
                 if  (temps[1] >= option_list2[PWM_SCALE].min && temps[1] <= option_list2[PWM_SCALE].max)
-                    mode.wr(temps[1], PWM_SCALE);
+                    user_settings.wr(temps[1], PWM_SCALE);
                 break;
             case    83: //  set to defaults
-                mode.set_defaults   ();
+                user_settings.set_defaults   ();
                 break;
             case    9:      //  9 Save settings
-                mode.save   ();
+                user_settings.save   ();
                 pc.printf   ("Saving settings to EEPROM\r\n");
                 break;
             default:
@@ -79,31 +81,32 @@
     else    {
         pc.printf   ("No Changes\r\n");
     }
-    pc.printf   ("mode 0\t%s, [%d]\r\n", option_list2[0].t, mode.rd(RPM0));
-    pc.printf   ("mode 1\t%s, [%d]\r\n", option_list2[1].t, mode.rd(RPM1));
-    pc.printf   ("mode 2\t%s, [%d]\r\n", option_list2[2].t, mode.rd(RPM2));
-    pc.printf   ("mode 3\t%s, [%d]\r\n", option_list2[3].t, mode.rd(RPM3));
-    pc.printf   ("mode 4\t%s, [%d]\r\n", option_list2[4].t, mode.rd(RPM4));
-    pc.printf   ("mode 5\t%s, [%d]\r\n", option_list2[5].t, mode.rd(RPM5));
-    pc.printf   ("mode 6\t%s, [%d]\r\n", option_list2[6].t, mode.rd(RPM6));
-    pc.printf   ("mode 7\t%s, [%d]\r\n", option_list2[7].t, mode.rd(RPM7));
-    pc.printf   ("mode 8\t%s, [%d]\r\n", option_list2[8].t, mode.rd(RPM8));
+    pc.printf   ("mode 0\t%s, [%d]\r\n", option_list2[0].t, user_settings.rd(RPM0));
+    pc.printf   ("mode 1\t%s, [%d]\r\n", option_list2[1].t, user_settings.rd(RPM1));
+    pc.printf   ("mode 2\t%s, [%d]\r\n", option_list2[2].t, user_settings.rd(RPM2));
+    pc.printf   ("mode 3\t%s, [%d]\r\n", option_list2[3].t, user_settings.rd(RPM3));
+    pc.printf   ("mode 4\t%s, [%d]\r\n", option_list2[4].t, user_settings.rd(RPM4));
+    pc.printf   ("mode 5\t%s, [%d]\r\n", option_list2[5].t, user_settings.rd(RPM5));
+    pc.printf   ("mode 6\t%s, [%d]\r\n", option_list2[6].t, user_settings.rd(RPM6));
+    pc.printf   ("mode 7\t%s, [%d]\r\n", option_list2[7].t, user_settings.rd(RPM7));
+    pc.printf   ("mode 8\t%s, [%d]\r\n", option_list2[8].t, user_settings.rd(RPM8));
 
-    pc.printf   ("mode 37\t%s, [%d]\r\n", option_list2[PWM_SCALE].t, mode.rd(PWM_SCALE));
+    pc.printf   ("mode 37\t%s, [%d]\r\n", option_list2[PWM_SCALE].t, user_settings.rd(PWM_SCALE));
     pc.printf   ("mode 83\tSet to defaults\r\n");
     pc.printf   ("mode 9\tSave settings\r\r\n");
 
 }
 
 void    gpcmd   (struct parameters & a)   {
-    pc.printf   ("pwm=%d\r\n", mode.get_pwm    ((int)a.dbl[0]));
+    pc.printf   ("pwm=%.3f\r\n", user_settings.get_pwm    ((int)a.dbl[0]));
 }
 
+extern  VEXT_Data   Field;
+
 void    rfcmd   (struct parameters & a)   {
-    pc.printf   ("ver = %d, vef = %d, measured_pw_us = %d\r\n", ver, vef, measured_pw_us);
+    pc.printf   ("Field.measured_period = %d, Field.measured_pw_us = %d, duty_cycle = %.3f\r\n", Field.measured_period, Field.measured_pw_us, Field.duty_cycle());
 }
 
-extern  double  glob_rpm;
 extern  void    set_RPM_demand  (uint32_t   d)  ;
 
 void    set_rpm_cmd   (struct parameters & a)   {
@@ -113,13 +116,20 @@
 
 void    speedcmd   (struct parameters & a)   {
     int s = ReadEngineRPM  ();
-    pc.printf   ("speed %d, %.2f, pwm %d\r\n", s, glob_rpm, mode.get_pwm(s));
+    pc.printf   ("speed %d, pwm %.3f\r\n", s, user_settings.get_pwm(s));
 }
 
 void    vcmd    (struct parameters & a)   {
     pc.printf   ("volts %.2f\r\n", Read_BatteryVolts());
 }
 
+void    icmd    (struct parameters & a)   {
+    double  results[4];
+    //double * ampsptr = 
+    Read_Ammeter(results)   ;
+    pc.printf   ("amps %.3f, offset %.3f\r\n", results[0], results[1]);
+}
+
 extern  void    set_servo   (double p)  ;   //  Only for test, called from cli
 
 void    set_servo_cmd    (struct parameters & a)   {
@@ -128,6 +138,13 @@
     set_servo   (p);
 }
 
+extern  bool    set_pwm (double)   ;    //  Range 0.0 to 1.0
+void    p_cmd (struct parameters & a)   {
+//    int32_t    i = (int32_t)a.dbl[0];
+    pc.printf   ("Setting PWM to %d percent\r\n", (int)(a.dbl[0] * 100.0));
+    set_pwm (a.dbl[0]);
+}
+
 void    null_cmd (struct parameters & a)   {
     pc.printf   ("At null_cmd, parameters : First %.3f, second %.3f\r\n", a.dbl[0], a.dbl[1]);
 }
@@ -142,21 +159,27 @@
 
 struct  kb_command const command_list[] = {
     {"?", "Lists available commands, same as ls", menucmd},
-    {"ls", "Lists available commands, same as menu", menucmd},
     {"rf", "Check rise and fall on VEXT", rfcmd},
     {"s", "Speed, RPM", speedcmd},
     {"v", "Read Battery volts", vcmd},
+    {"i", "Read Ammeter", icmd},
+    {"p", "Set PWM 0 to 2400???", p_cmd},
+    {"q", "Query system - toggle message stream on/off", query_system},
     {"gp","Get pwm from RPM", gpcmd},
     {"mode", "See or set eeprom values", mode19_cmd},
     {"nu", "do nothing", null_cmd},
+#ifndef SPEED_CONTROL_ENABLE    //  Includes engine revs servo control loop
     {"ser","set throttle servo direct 0 - 99", set_servo_cmd},
-    {"sv","set engine RPM demand 3000 - 6000", set_rpm_cmd},
+#endif
+    {"sf","set speed control factor", speed_control_factor_set},
+    {"sv","set engine RPM demand 2500 - 6000", set_rpm_cmd},
+    {"tl","set throttle_limit 0.0-1.0", set_throttle_limit},
 };
 
 const int numof_menu_items = sizeof(command_list) / sizeof(kb_command);
 void    menucmd (struct parameters & a)
 {
-    pc.printf("\r\nIntelligent Alternator Controller - Jon Freeman 2019\r\nAt menucmd function - listing commands:-\r\n");
+    pc.printf("\r\nIntelligent Alternator Controller - Jon Freeman 2020\r\nAt menucmd function - listing commands:-\r\n");
     for(int i = 0; i < numof_menu_items; i++)
         pc.printf("[%s]\t\t%s\r\n", command_list[i].cmd_word, command_list[i].explan);
     pc.printf("End of List of Commands\r\n");
@@ -164,6 +187,7 @@
 
 void    command_line_interpreter    ()
 {
+
     const int MAX_CMD_LEN = 120;
     static  char    cmd_line[MAX_CMD_LEN + 4];
     static  int     cl_index = 0;
@@ -221,6 +245,7 @@
             cl_index = 0;
         }               // End of else key was CR, may or may not be command to lookup
     }                   //  End of while (pc.readable())
+
 }
 
 
--- a/i2c_bit_banged.cpp	Fri Jun 28 19:32:51 2019 +0000
+++ b/i2c_bit_banged.cpp	Sat Apr 25 15:35:58 2020 +0000
@@ -1,14 +1,20 @@
 #include "mbed.h"
 #include "Alternator.h"
+#ifdef  TARGET_NUCLEO_L432KC    //
 extern  Serial  pc;
 DigitalInOut    SDA (D4);       //  Horrible bodge to get i2c working using bit banging.
 DigitalInOut    SCL (D5);       //  DigitalInOut do not work as you might expect. Fine if used only as OpenDrain opuputs though!
 DigitalIn       SDA_IN  (A4);   //  That means paralleling up with two other pins as inputs
 DigitalIn       SCL_IN  (A5);   //  This works but is a pain. Inbuilt I2C should have worked but never does on small boards with 32 pin cpu.
-
+const int ACK     = 0;  //  but acknowledge is 0, NAK is 1
+#endif
+#ifdef  TARGET_NUCLEO_F401RE    //
+extern  BufferedSerial  pc;
+extern  I2C i2c;
+const int ACK     = 1;  //  but acknowledge is 0, NAK is 1
+#endif
 const int _24LC_rd = 0xa1;  //  set bit 0 for read, clear bit 0 for write
 const int _24LC_wr = 0xa0;  //  set bit 0 for read, clear bit 0 for write
-const int ACK     = 0;  //  but acknowledge is 0, NAK is 1
 
 
 /*struct  optpar  {
@@ -39,7 +45,7 @@
 
 
 
-eeprom_settings mode  ;
+eeprom_settings user_settings  ;
 
 eeprom_settings::eeprom_settings    ()  {}
 
@@ -57,16 +63,25 @@
     return  settings[i];
 }
 
-bool    eeprom_settings::wr  (char c, uint32_t i)  {           //  Read one setup char value from private buffer 'settings'
+bool    eeprom_settings::rd  (char * c, uint32_t i)  {           //  Read one setup char value from private buffer 'settings'
+    if  (i > 31)    {
+        pc.printf   ("ERROR Attempt to read setting %d\r\n", i);
+        return  false;
+    }
+    *c = settings[i];
+    return  true;
+}
+
+bool    eeprom_settings::wr  (char c, uint32_t i)  {           //  Write one setup char value to private buffer 'settings'
     if  (i > 31)
         return  false;
     settings[i] = c;
     return  true;
 }
 
-int     eeprom_settings::get_pwm (int rpm)   {
+double  eeprom_settings::get_pwm (int rpm)   {
     int p = rpm * lut_size;
-    p /= 8000;  //  8000 is upper RPM limit
+    p /= 8000;  //  8000 is upper RPM limit, p now scaled to sizeof lut
     if  (p < 0) p = 0;                    //  point to first
     if  (p >= lut_size) p = lut_size - 1; //  point to last
 //    pc.printf   ("In get_pwm, rpm = %d, lut entry = %d, pwm = %d\r\n", rpm, p, max_pwm_lut[p]);
@@ -76,33 +91,29 @@
 void    eeprom_settings::build_lut   ()  {
     int ptr = 0;
     int range, i;
-    int base = mode.rd(RPM0) * PWM_PERIOD_US;
-    double  acc, incr;
-    base /= 100;    //  got pwm_pulsewidth of 0 RPM
-    acc = (double) base;
-    pc.printf   ("pwm_period_us ar 0 RPM = %d\r\n", base);
+    double  acc = 0.0, incr = 0.0;
     for (i = 0; i < 8; i++) {
-        range = mode.rd(i+1) - mode.rd(i);  //  range now change in percent between two 'n'000 RPMs
-        range *= mode.rd(PWM_SCALE);        //  range now 10000 times factor due to percentage twice
-        range *= PWM_PERIOD_US;
+        range = user_settings.rd(i+1) - user_settings.rd(i);  //  range now change in percent between two 'n'000 RPMs
         incr = (double)range;
-        incr /= 10000.0;
+        incr /= 100.0;  //  percent
         incr /= lut_seg_size;
         for(int j = 0; j < lut_seg_size; j++)   {
-            max_pwm_lut[ptr++] = (int)acc;
+            max_pwm_lut[ptr++] = acc;
             acc += incr;
         }
     }
     max_pwm_lut[ptr] = (int)acc;
     pc.printf   ("At end of build_lut ptr=%d\r\n", ptr);
     range = 0;
-//    while   (range < ptr)   {
-//        for (i = 0; i < 10; i++)    {
-//            pc.printf   ("%d\t", max_pwm_lut[range++]);
-//        }
-//        pc.printf   ("\r\n");
-//    }
+
+    while   (range < ptr)   {
+        for (i = 0; i < 8; i++)    {
+            pc.printf   ("%.3f\t", max_pwm_lut[range++]);
+        }
+        pc.printf   ("\r\n");
+    }
     pc.printf   ("lut_size = %d\r\n", lut_size);
+
 }
 
 bool    eeprom_settings::load    ()  {               //  Get 'settings' buffer from EEPROM
@@ -117,6 +128,7 @@
 }
 
 
+//#ifdef  TARGET_NUCLEO_L432KC    //
 
 /**
 *   bool    i2c_init(void) {
@@ -126,6 +138,7 @@
 *   a I2C bus lockup or that the lines are not pulled up.
 */
 bool    i2c_init(void) {
+#ifdef  TARGET_NUCLEO_L432KC    //
     SDA.output();
     SCL.output();
     SDA.mode(OpenDrain);
@@ -139,6 +152,11 @@
     wait_us (1);
     if  (SCL_IN == 0 || SDA_IN == 0)    return  false;
   return true;
+#endif
+#ifdef  TARGET_NUCLEO_F401RE    //
+//    return  i2c.init    ()  ; //  class has no member "init"
+    return  true;
+#endif
 }
 
 /**
@@ -152,6 +170,7 @@
 *   commands must be preceded by a Start condition.
 */
 int i2c_start  ()  {   // Should be Both hi, start takes SDA low
+#ifdef  TARGET_NUCLEO_L432KC    //
     int    rv = 0;
     if  (SDA_IN == 0 )  {
         rv |= 1;    //  Fault - SDA was lo on entry
@@ -168,6 +187,11 @@
     SCL = 0;
     wait_us (1);
     return  rv;     //  Returns 0 on success, 1 with SDA fault, 2 with SCL fault, 3 with SDA and SCL fault
+#endif
+#ifdef  TARGET_NUCLEO_F401RE    //
+    i2c.start   ()  ;
+    return  0;
+#endif
 }
 
 /**
@@ -181,6 +205,7 @@
 *   operations must be ended with a Stop condition.
 */
 int i2c_stop  ()  {   //  Should be SDA=0, SCL=1, start takes SDA hi
+#ifdef  TARGET_NUCLEO_L432KC    //
     int    rv = 0;
     SDA = 0;    //  Pull SDA to 0
     wait_us (1);
@@ -198,8 +223,14 @@
     if  (SDA_IN == 0)
         pc.printf   ("SDA stuck lo in stop\r\n");
     return  rv;     //  Returns 0 on success, 1 with SDA fault, 2 with SCL fault, 3 with SDA and SCL fault
+#endif
+#ifdef  TARGET_NUCLEO_F401RE    //
+    i2c.stop    ()  ;
+    return  0;
+#endif
 }
 
+#ifdef  TARGET_NUCLEO_L432KC    //
 void    jclk    (int bit)   {
     SCL = bit;
     wait_us (1);
@@ -212,8 +243,10 @@
     SCL = 0;
     wait_us (1);
 }
+#endif
 
 int i2c_write (int    d)  {
+#ifdef  TARGET_NUCLEO_L432KC    //
     int ackbit = 0;
     if  (SCL_IN != 0)   {
         pc.printf   ("SCL hi on entry to write\r\n");
@@ -230,12 +263,17 @@
     jclk    (0);    //  SCL = 0
 //    pc.printf   ("wr 0x%x %s\r\n", d, ackbit == 0 ? "ACK" : "nak");
     return  ackbit; //      0 for acknowledged ACK, 1 for NAK
+#endif
+#ifdef  TARGET_NUCLEO_F401RE    //
+    return  i2c.write   (d);
+#endif
 }
 
 
 
 
 int i2c_read    (int acknak)  {     //  acknak  indicates if the byte is to be acknowledged (0 = acknowledge)
+#ifdef  TARGET_NUCLEO_L432KC    //
     int result = 0;                 //  SCL should be 1 on entry
     SDA = 1;        //  Master released SDA
     if  (SCL_IN != 0)   pc.printf   ("SCL hi arriving at read\r\n");
@@ -253,6 +291,10 @@
     jclkout ();    //  clock out the ACK bit __--__
 //    pc.printf   ("rd 0x%x %s\r\n", result, acknak == 0 ? "ACK" : "nak");
     return  result;             //  Always ? nah
+#endif
+#ifdef  TARGET_NUCLEO_F401RE    //
+    return  i2c.read    (acknak)    ;
+#endif
 }
 
 int check_24LC64   ()  {     //  Call from near top of main() to init i2c bus
@@ -323,13 +365,15 @@
         pc.printf   ("Length out of range %d in wr_24LC64\r\n", length);
         return  false;
     }
+    ack_poll    ();
     if  (!set_24LC64_internal_address   (start_addr))   {
         pc.printf   ("In wr_24LC64, Believe Device present, failed in writing 2 mem addr bytes %d\r\n", err);
         return  false;
     }
-    while(length--) {
-        err += i2c_write(*source++);
-    }
+    while(length--) 
+        if  (i2c_write(*source++) != ACK)
+            err++;
+//        err += i2c_write(*source++);
     i2c_stop();
     if  (err)   {
         pc.printf   ("in wr_24LC64, device thought good, mem addr write worked, failed writing string\r\n");
--- a/main.cpp	Fri Jun 28 19:32:51 2019 +0000
+++ b/main.cpp	Sat Apr 25 15:35:58 2020 +0000
@@ -1,20 +1,30 @@
 #include "mbed.h"
 #include "Alternator.h"
 
-#define MAGNETO_SPEED   //  Selects engine speed input as magneto coil on engine switch line
-#define SPEED_CONTROL_ENABLE    //  Includes engine revs servo control loop
 
 /*
     Alternator Regulator
     Jon Freeman
-    June 2019
+    June 2019 - Feb 2020
+    
+    ** Prototype built using Nucleo L432KC. Final design likely to use F401RE. Code should compile for either. **
     
-    WHAT THIS PROGRAMME DOES - 
+    **  main loop frequency upped from 32Hz to 100Hz **
+
+    WHAT THIS PROGRAMME DOES - Controls 4 stroke petrol engine driving vehicle alternator with new custom regulator
+    
+    Electronics powered by higher voltage of small 12v backup battery, or alternator field output supply
+        Note only Field+ and MAX5035 supplied thus, all else powered from MAX outputs.
+    Starting engine provides rectified tickle from magneto to enable MAX5035 creating +5 and +3v3 supplies.
+    Alternative, selected by jumper pposition, is external switch - battery+ to MAX enable circuit.
+    Anytime engine revs measured < 2000 (or some such) RPM, field current OFF (by pwm 0)
     
     BEGIN
-        Loop forever at 32 Hz   {
-            Read engine RPM
-            Adjust Alternator field current max limit according to RPM
+        Loop forever at 100 Hz   {
+            Read engine RPM by monitoring engine tacho signal present on engine On/Off switch line
+            Read alternator output load current
+            Using RPM and current readings, regulate engine rpm via model control servo
+            Adjust Alternator field current max limit according to RPM (analogue regulator limits output voltage)
             Measure system voltage (just in case this is ever useful)
             Respond to any commands arriving at serial port (setup and test link to laptop)
             Flash LED at 8 Hz as proof of life
@@ -30,11 +40,13 @@
     OUTPUT  pwm to MCP1630. This is clock to pwm chip. Also limits max duty ratio
     RS232 serial via USB to setup eeprom data
 */
-//  Uses software bit banged I2C - DONE (because no attempt to get I2C working on these small boards)
+//  Uses software bit banged I2C - DONE (because no attempt to get I2C working on these small boards has ever worked)
 
 /**
 *   Jumpers fitted to small mbed Nucleo boards - D5 - A5 and D4 - A4 CHECK - yes
 */
+//#ifdef  TARGET_NUCLEO_F303K8    //  Code too large to fit
+#ifdef  TARGET_NUCLEO_L432KC    //
 /*
     declared in file i2c_bit_banged.cpp
 DigitalInOut    SDA (D4);       //  Horrible bodge to get i2c working using bit banging.
@@ -47,217 +59,270 @@
 //  Above combo of Serial and BufferedSerial is the only one to work !
 
 //  INPUTS :
-AnalogIn    Ain_SystemVolts (A0);   //  Brought out to CN8 'AN' A0. Connect 3k3 resistor from A0 to ground.
-AnalogIn    Ammeter_In      (A1);   //  Output of ASC709LLFTR ammeter chip (pin 20)
-AnalogIn    Ammeter_Ref     (A6);   //  Ref output from ASC709LLFTR used to set ammeter zero (pin 25)
+AnalogIn    Ain_SystemVolts (A6);   //  Sniff of alternator output, not used in control loop as done using analogue MCP1630
+AnalogIn    Ammeter_In      (A1);   //  Output of ASC709LLFTR ammeter chip (pin 20), used to increase engine revs if need be
+AnalogIn    Ammeter_Ref     (A0);   //  Ref output from ASC709LLFTR used to set ammeter zero (pin 25)
+
+//  Nov 2019. Not convinced Ext_Rev_Demand is useful
 AnalogIn    Ext_Rev_Demand  (D3);   //  Servo determines engine revs, servo out to be higher of Ext_Rev_Demand and internal calc
+
 AnalogIn    Driver_Pot      (A3);   //  If whole control system can be made to fit
-//  Connect 33k resistor from A0 to nom 24 Volt system rail.
 
-//#ifdef  TARGET_NUCLEO_F303K8    //  
-#ifdef  TARGET_NUCLEO_L432KC    //
 /*
     MODULE PIN USAGE    
 1   PA_9 D1     LocalCom Tx
 2   PA_10 D0    LocalCom Rx
 3   NRST        
 4   GND     
-5   PA12_D2     NEW June 2019 - Output engine tacho cleaned-up
+5   PA12_D2     NEW June 2019 - Output engine tacho cleaned-up, brought out to testpoint 4
 6   PB_0 D3     AnalogIn Ext_Rev_Demand
 7   PB_7 D4     SDA i2c to 24LC memory
 8   PB_6 D5     SCL i2c to 24LC memory
 9   PB_12 D6    PwmOut     PWM_OSC_IN Timebase for pwm, also determines max duty ratio
 10  N.C.        
 11  N.C.        
-12  PA_8 D9     InterruptIn pulse_tacho from alternator, used to measure rpm
-13  PA_11 D10   
-14  PB_5 D11   
-15  PB_4 D12    
+12  PA_8 D9     InterruptIn pulse_tacho from engine magneto, used to measure rpm
+13  PA_11 D10   Throttle servo
+14  PB_5 D11   //  InterruptIn VEXT PWM controller output folded back for cpu to monitor, useful on test to read what pwm required to do what
+15  PB_4 D12    Scope_probe
 16  PB_3 D13 LED    Onboard LED
 17  3V3         
 18  AREF        
-19  PA_0 A0     AnalogIn V_Sample system link voltage
+19  PA_0 A0     AnalogIn Ammeter_Ref
 20  PA_1 A1     AnalogIn Ammeter_In
 21  PA_3 A2     PWM analogue out
-22  PA_4 A3     InterruptIn VEXT PWM controller output folded back for cpu to monitor, useful on test to read what pwm required to do what
+22  PA_4 A3     AnalogIn Driver_Pot
 23  PA_5 A4     n.c. SDA_IN paralleled to i2c pin, necessary because i2c has to be bit banged
 24  PA_6 A5     n.c. SCL_IN paralleled to i2c pin, necessary because i2c has to be bit banged
-25  PA_7 A6     AnalogIn Ammeter_Ref
-26  PA_2 A7 UART2_TX     Throttle Servo out now on D10, can not use D11, can not use D12 for these
+25  PA_7 A6     AnalogIn V_Sample system link voltage
+26  PA_2 A7     Not used
 27  5V          
 28  NRST        
 29  GND         
 30  VIN         
 */
 
-InterruptIn pulse_tacho     (D9);  //  Signal from 'W' alternator terminal via low pass filter and Schmitt trigger cleanup
-                                    //  NOTE D7 pin was no good for this
-//InterruptIn VEXT    (A3);     //  PWM controller output folded back for cpu to monitor, useful on test to read what pwm required to do what
-InterruptIn VEXT    (D11);     //  PWM controller output folded back for cpu to monitor, useful on test to read what pwm required to do what
+InterruptIn pulse_tacho     (D9);  //  Signal from engine magneto (clipped by I limit resistor and 3v3 zener)
+InterruptIn VEXT            (D2);     //  PWM controller output folded back for cpu to monitor, useful on test to read what pwm required to do what
 //  OUTPUTS :
 
-//DigitalOut  Scope_probe (D0);   //  Handy pin to hang scope probe onto while developing code
-DigitalOut  myled(LED1);        //  Green LED on board is PB_3 D13
-//PwmOut     PWM_OSC_IN    (A6);   //  Can alter prescaler can not use A5
-PwmOut     PWM_OSC_IN   (D6);   //  Can alter prescaler can not use A5
-PwmOut     A_OUT        (A2);   //  Can alter prescaler can not use A5
-//Servo   Throttle    (A2);   //  Pin A2, PA3
-//Servo   Throttle    (A7);   //  Changed from A2, June 2019
-Servo   Throttle    (D10);   //  Changed from A2, June 2019
-DigitalOut  EngineTachoOut  (D2);   //  New June 2019
+DigitalOut  Scope_probe     (D12);   //  Handy pin to hang scope probe onto while developing code
+DigitalOut  myled           (LED1);        //  Green LED on board is PB_3 D13
+PwmOut      PWM_OSC_IN      (A2);   //  Can alter prescaler can not use A5
+//PwmOut      A_OUT           (A2);   //  Can alter prescaler can not use A5    PIN STOLEN BY PWM_OSC_IN
+Servo       Throttle        (D10);   //  Changed from A2, June 2019
+DigitalOut  EngineTachoOut  (D11);   //  New June 2019
 #endif
+
+#ifdef  TARGET_NUCLEO_F401RE    //
+//Serial  pc      (USBTX, USBRX); //  Comms port to pc or terminal using USB lead
+BufferedSerial  pc          (PA_2, PA_3, 2048, 4, NULL);    //  Pins 16, 17    tx, rx to pc via usb lead
+//BufferedSerial  pc          (USBTX, USBRX);    //  Pins 16, 17    tx, rx to pc via usb lead
+BufferedSerial  LocalCom    (PC_6, PC_7);    //  Pins 37, 38  tx, rx to Touch Screen Controller
+
+//  INPUTS :
+AnalogIn    Ain_SystemVolts (PB_1);   //  Sniff of alternator output, not used in control loop as done using analogue MCP1630
+AnalogIn    Ammeter_In      (PC_5);   //  Output of ASC709LLFTR ammeter chip (pin 20), used to increase engine revs if need be
+AnalogIn    Ammeter_Ref     (PB_0);   //  Ref output from ASC709LLFTR used to set ammeter zero (pin 25)
+AnalogIn    Ext_Rev_Demand  (PC_1);   //  Servo determines engine revs, servo out to be higher of Ext_Rev_Demand and internal calc
+AnalogIn    Driver_Pot      (PC_2);   //  If whole control system can be made to fit
+
+/*
+    MODULE PIN USAGE    
+*/
+
+InterruptIn pulse_tacho     (PB_15);  //  Signal from engine magneto (clipped by I limit resistor and 3v3 zener)
+InterruptIn VEXT            (PC_12);     //  PWM controller output folded back for cpu to monitor, useful on test to read what pwm required to do what
+//  OUTPUTS :
+
+DigitalOut  Scope_probe     (PB_3);   //  Handy pin to hang scope probe onto while developing code
+DigitalOut  myled           (PA_5);        //  Green LED on board is PA_5
+//PwmOut      PWM_OSC_IN      (PA_10);   //  PA_10 is pwm1/3 Can alter prescaler can not use A5
+PwmOut      PWM_OSC_IN      (PB_9);   //  PA_10 is pwm4/4 Can alter prescaler can not use A5
+PwmOut      A_OUT           (PB_5);   //  PB_5 is pwm3/2 Can alter prescaler can not use A5    PIN STOLEN BY PWM_OSC_IN
+Servo       Throttle        (PA_0);   //  PA_8 is pwm1/1 Changed from A2, June 2019
+DigitalOut  EngineTachoOut  (PA_7);   //  New June 2019
+
+I2C i2c                     (PB_7, PB_6);   //  Pins 58, 59 For 24LC64 eeprom
+//#define SDA_PIN PB_7
+//#define SCL_PIN PB_6
+
+#endif
+
 Timer   microsecs;
 Ticker  loop_timer;     //  Device to cause periodic interrupts, used to sync iterations of main programme loop - slow
 
-extern  eeprom_settings mode  ;
+const   double  AMPS_CAL = 90.0;
+extern  eeprom_settings user_settings  ;
 //  SYSTEM CONSTANTS
 /*  Please Do Not Alter these */
-const   int     MAIN_LOOP_REPEAT_TIME_US    = 31250;    //  31250 us, with TACHO_TAB_SIZE = 32 means tacho_ticks_per_time is tacho_ticks_per_second
-const   int     MAIN_LOOP_ITERATION_Hz      = 1000000 / MAIN_LOOP_REPEAT_TIME_US;   //  = 32 Hz
-//const   int     FAST_INTERRUPT_RATE         = 3125;
+const   int     MAIN_LOOP_REPEAT_TIME_US    = 10000;    //  10000 us, with TACHO_TAB_SIZE = 100 means tacho_ticks_per_time is tacho_ticks_per_second
 /*  End of Please Do Not Alter these */
 /*  Global variable declarations */
 uint32_t    //semaphore           = 0,
+            speed_control_factor= 75000,  //  fiddled from cli to tweak engine speed controller response
             volt_reading        = 0,    //  Global updated by interrupt driven read of Battery Volts
-            amp_reading         = 0,
-            amp_offset          = 0,
             ext_rev_req         = 0,
             driver_reading      = 0,
             tacho_count         = 0,    //  Global incremented on each transition of InterruptIn pulse_tacho
-            tacho_ticks_per_time = 0,   //  Global tacho ticks in most recent (MAIN_LOOP_REPEAT_TIME_US * TACHO_TABLE_SIZE) micro secs
-            sys_timer32Hz       = 0;    //  gets incremented by our Ticker ISR every MAIN_LOOP_REPEAT_TIME_US
+            sys_timer100Hz      = 0;    //  gets incremented by our Ticker ISR every MAIN_LOOP_REPEAT_TIME_US
+double      amp_reading         = 0.0,
+            amp_offset          = 0.0,
+            raw_amp_reading     = 0.0,
+            raw_amp_offset      = 0.0;
+double      servo_position = 0.2;   //  set in speed control loop
+//    const double    throttle_limit = 0.4;
+double      throttle_limit = SERVO_MAX;
 bool        loop_flag   = false;    //  made true in ISR_loop_timer, picked up and made false again in main programme loop
-bool        flag_8Hz    = false;    //  As loop_flag but repeats 8 times per sec
+bool        flag_25Hz   = false;    //  As loop_flag but repeats 25 times per sec
+bool        flag_12Hz5  = false;    //  As loop_flag but repeats 12.5 times per sec
+bool        flag_1Hz    = false;    //  As loop_flag but repeats 1 times per sec
+bool        query_toggle    = false;
 
-
-const   double  scale = 0.125;
-const   double  shrink_by = 1.0 - scale;
-double  glob_rpm;
+const int AMP_FILTER_FACTOR    = 6;
 
 /*  End of Global variable declarations */
 
-//void    ISR_fast_interrupt  ()  {   //  here at 10 times main loop repeat rate (i.e. 320Hz)
-void    ISR_fast_interrupt  ()  {   //  here at ** 25 ** times main loop repeat rate (1250us, i.e. 800Hz)
-    static  int t = 0;
+//void    ISR_fast_interrupt  ()  {   //  here at 10 times main loop repeat rate (i.e. 1000Hz, 1.0ms)
+void    ISR_fast_interrupt  ()  {
+    static  uint32_t t = 0;
+    Scope_probe = 1;    //  To show how much time spent in interrupt handler
     switch  (t) {
         case    0:
             volt_reading    >>= 1;                                 //  Result = Result / 2
             volt_reading    += Ain_SystemVolts.read_u16    ();     //  Result = Result + New Reading
             break;
         case    1:
-            amp_reading     >>= 1;                                 //  Result = Result / 2
-            amp_reading     = Ammeter_In.read_u16();
+            raw_amp_reading = (double) Ammeter_In.read();
             break;
         case    2:
-            amp_offset      >>= 1;                                 //  Result = Result / 2
-            amp_offset      = Ammeter_Ref.read_u16();
+            raw_amp_offset = Ammeter_Ref.read();    //  Feb 2020 Not convinced this is useful
             break;
         case    3:
             ext_rev_req     >>= 1;                                 //  Result = Result / 2
-            ext_rev_req     = Ext_Rev_Demand.read_u16();
+            ext_rev_req     += Ext_Rev_Demand.read_u16();
             break;
         case    4:
             driver_reading  >>= 1;                                 //  Result = Result / 2
-            driver_reading  = Driver_Pot.read_u16();
-            break;
-        case    5:
-//            semaphore++;
-            const   int TACHO_TABLE_SIZE    = MAIN_LOOP_ITERATION_Hz;   //  Ensures table contains exactly one seconds worth of samples
-            static  uint32_t    h[TACHO_TABLE_SIZE],    //  circular buffer to contain list of 'tacho_count's
-                    i = 0, last_temp = 0;
-            static  double  rpm_filt = 0.0;
-            double  tmp;
-            
-            uint32_t    temp = tacho_count;         //  Read very latest total pulse count from global tacho_count
-            tmp = (double) (temp - last_temp);
-            last_temp = temp;
-#ifdef  MAGNETO_SPEED
-            tmp *= (scale * 32.0 * 60.0);    //  ???? Is this including alternator poles count ??? Do we need MAGNETO_SPEED included
-#else
-            tmp *= (scale * 32.0 * 60.0 / 12.0);    //  ???? Is this including alternator poles count ??? Do we need MAGNETO_SPEED included
-#endif
-            rpm_filt *= shrink_by;
-            rpm_filt += tmp;
-            glob_rpm = rpm_filt;
-            
-            tacho_ticks_per_time = temp - h[i];     //  latest tacho total pulse count - oldest stored tacho total pulse count
-            h[i]    = temp;                         //  latest overwrites oldest in table
-            i++;                                    //  index to next table position for next time around
-            if  (i >= TACHO_TABLE_SIZE)
-                i = 0;                              //  circular buffer
+            driver_reading  += Driver_Pot.read_u16();
+//            break;
+//        case    5:
             loop_flag = true;   //  set flag to allow main programme loop to proceed
-            sys_timer32Hz++;        //  Just a handy measure of elapsed time for anything to use
-            if  ((sys_timer32Hz & 0x03) == 0)
-                flag_8Hz    = true; //  flag gets set 8 times per sec. Other code may clear flag and make use of this
+            sys_timer100Hz++;        //  Just a handy measure of elapsed time for anything to use
+            if  ((sys_timer100Hz & 0x03) == 0)  //  is now 12.5Hz, not 8
+                flag_25Hz  = true; //  flag gets set 25 times per sec. Other code may clear flag and make use of this
+        default:
             break;
     }
     t++;
     if  (t > 9)
         t = 0;
+    Scope_probe = 0;    //  To show how much time spent in interrupt handler
 }
 
 
 
-/** void    ISR_loop_timer  ()
-*   This ISR responds to Ticker interrupts at a rate of (probably) 32 times per second (check from constant declarations above)
-*   This ISR sets global flag 'loop_flag' used to synchronise passes around main programme control loop.
-*   Also, updates global int 'tacho_ticks_per_time' to contain total number of transitions from alternator 'W' terminal in
-*   time period from exactly one second ago until now.
-*   Increments global 'sys_timer32Hz', usable anywhere as general measure of elapsed time
+//  New stuff November 2019
+/**
+*   Obtaining Amps_Deliverable from RPM.
+*   Lucas workshop sheet shows exponential relationship between RPM over threshold, and Amps_Deliverable,
+*   That is Amps_Deliverable rises steeply with RPM, flattening off towards 6000 RPM
+*   Curve modeled by eqn
+*       I_del = I_max (1 - exp(-(RPM-3000)/Const1))       where Const1 = 1000 is a starting point
+*   This is probably fine when driving alternator with BIG engine.
+*   When using small engine, rising load current sags engine RPM.
+*   Using a linear relationship builds in a good safety margin, possible eqn
+*       I_del = I_max (RPM - 3000) / 3000     for use over RPM range 3000-6000
 */
-void    ISR_loop_timer  ()      //  This is Ticker Interrupt Service Routine - loop timer - MAIN_LOOP_REPEAT_TIME_US
-{                               //  Jan 2019 MAIN_LOOP_REPEAT_TIME_US = 31.25 ms
-    const   int TACHO_TABLE_SIZE    = MAIN_LOOP_ITERATION_Hz;   //  Ensures table contains exactly one seconds worth of samples
-    static  uint32_t    h[TACHO_TABLE_SIZE],    //  circular buffer to contain list of 'tacho_count's
-            i = 0, last_temp = 0;
-    static  double  rpm_filt = 0.0;
-    double  tmp;
-    
-    uint32_t    temp = tacho_count;         //  Read very latest total pulse count from global tacho_count
-    tmp = (double) (temp - last_temp);
-    last_temp = temp;
-#ifdef  MAGNETO_SPEED
-    tmp *= (scale * 32.0 * 60.0);    //  ???? Is this including alternator poles count ??? Do we need MAGNETO_SPEED included
+const   double  RPM_Threshold   = 3000.0;
+const   double  RPM_Max         = 6000.0;
+//const   double  I_max = 30.0;
+const   double  RPM_Range   = RPM_Max - RPM_Threshold;
+//#define BIG_ENGINE
+
+double  Calculate_Amps_Deliverable (uint32_t RPM)  {
+    double r = (double)RPM - RPM_Threshold;
+    r /= RPM_Range;
+    if  (r < 0.0)
+        r = 0.0;
+    if  (r > 1.0)
+        r = 1.0;
+#ifdef  BIG_ENGINE
+    const   double  Const1 = -3.2;    //  Tweak this to adjust shape of exponential function
+    return  (1.0 - exp(r*Const1));
 #else
-    tmp *= (scale * 32.0 * 60.0 / 12.0);    //  ???? Is this including alternator poles count ??? Do we need MAGNETO_SPEED included
+    return  r;
 #endif
-//    tmp *= (scale * 32.0 * 60.0 / 12.0);
-    rpm_filt *= shrink_by;
-    rpm_filt += tmp;
-    glob_rpm = rpm_filt;
-    
-    tacho_ticks_per_time = temp - h[i];     //  latest tacho total pulse count - oldest stored tacho total pulse count
-    h[i]    = temp;                         //  latest overwrites oldest in table
-    i++;                                    //  index to next table position for next time around
-    if  (i >= TACHO_TABLE_SIZE)
-        i = 0;                              //  circular buffer
-    loop_flag = true;   //  set flag to allow main programme loop to proceed
-    sys_timer32Hz++;        //  Just a handy measure of elapsed time for anything to use
-    if  ((sys_timer32Hz & 0x03) == 0)
-        flag_8Hz    = true; //  flag gets set 8 times per sec. Other code may clear flag and make use of this
+}
+
+class   one_over_s_integrator   {   //  Need this to drive servo  Jan 2020 why?
+    double  internal_integral, max, min, Hz, gain;
+    public:
+    one_over_s_integrator   ()  {   internal_integral = 0.0; max = 1.0; min = -1.0; Hz = 100.0; gain = 1.0;}
+    double  integral    (double input)  ;
+    void    set_max (double)    ;
+    void    set_min (double)    ;
+    void    set_gain (double)    ;
+    void    set_sample_time (double)    ;
+}   ;
+
+void    one_over_s_integrator::set_max (double in)  {
+    max = in;
 }
+void    one_over_s_integrator::set_min (double in)  {
+    min = in;
+}
+void    one_over_s_integrator::set_gain (double in)  {
+    gain = in;
+}
+void    one_over_s_integrator::set_sample_time (double in)  {
+    Hz = 1.0 / in;
+}
+double  one_over_s_integrator::integral (double input)  {
+    internal_integral += gain * input / Hz;     //  100 for 100Hz update rate
+    if  (internal_integral > max)
+        internal_integral = max;
+    if  (internal_integral < min)
+        internal_integral = min;
+    return  internal_integral;
+}
+
+//double  one_over_s  ()
+//  End of New stuff November 2019
 
 
 //  New stuff June 2019
-//uint32_t    magneto_count = 0;
-#ifdef  MAGNETO_SPEED
 bool    magneto_stretch = false;
 Timeout magneto_timo;
-uint32_t magneto_times[8] = {0,0,0,0,0,0,0,0};
+uint32_t magneto_times[8] = {0,0,0,0,0,0,0,0};  //  June 2019, only 2 of these used
+
 
-void    ISR_magneto_tacho   ()  ;   //  New June 2019
-    //      Engine On/Off switch turns engine off by shorting magneto to ground.
-    //      Therefore have pulse signal one pulse per rev (even tghough 4 stroke, spark delivered at 2 stroke rate)
-    //      Pulse spacing 20ms @ 3000 RPM, 60ms @ 1000 RPM, 6ms @ 10000 RPM
-    
-    //  Relies also on a timeout
+/**    
+    void    magneto_timeout ()
+    Here 5ms after magneto pulse detected
+    This is sufficient time for ringing to cease, not long enough to lose next pulse even at max engine revs.
+    Reset 'magneto_stretch' flag set and used in 'ISR_magneto_tacho'
+*/
 void    magneto_timeout ()
 {
     magneto_stretch = false;    //  Magneto ringing finished by now, re-enable magneto pulse count
-    EngineTachoOut  = 0;
+    EngineTachoOut  = 0;    //  Cleaned tacho output brought out to pin to look at with scope
 }
 
-void    ISR_magneto_tacho   ()  //  Here rising or falling edge of magneto output, not both
+/**
+void    ISR_magneto_tacho   ()  ;   //  New June 2019
+    //      Engine On/Off switch turns engine off by shorting ignition volts magneto to ground.
+    //      Therefore when engine running, have pulse signal one pulse per rev (even though 4 stroke, spark delivered at 2 stroke rate)
+    //      Pulse spacing 20ms @ 3000 RPM, 60ms @ 1000 RPM, 6ms @ 10000 RPM
+    
+            Magneto signal rings, is quite unclean, therefore a cleanup strategy is needed.
+            Solution - On arrival at this interrupt handler,
+                If flag 'magneto_stretch' true, do nothing and return (to avoid multiple pulse count)
+                Set flag 'magneto_stretch' true;
+                Start timer 'magneto_timo' to cause 'magneto_timeout' interrupt in a time longer than ringing bt shorter than shortest time to next spark
+                Record time between most recent two sparks and set output bit for scope monitoring
+*/
+void    ISR_magneto_tacho   ()  //  This interrupt initiated by rising (or falling) edge of magneto output, (not both)
 {
     if  (!magneto_stretch)
     {
@@ -269,70 +334,105 @@
         magneto_stretch = true;
         magneto_timo.attach_us (&magneto_timeout, 5000);    //  To ignore ringing and multiple counts on magneto output, all settled within about 5ms
         tacho_count++;
-        EngineTachoOut  = 1;
+        EngineTachoOut  = 1;    //  Cleaned tacho output brought out to pin to look at with scope
     }
 }
 
-#endif
 //  Endof New stuff June 2019
 
-//uint32_t    time_diff;
-/** void    ISR_pulse_tacho ()
+const   double  shrink = 0.2;
+/*double  lpf_4th_order_asym    (double input)  {
 *
+*   input is driver's control pot.
+*   This needs regular calling, maybe 8Hz - 32Hz
+*
+*   Output from 4th stage of cascaded Butterworth lpf section
+*   Used to delay rising input to motor controller allowing time for engine revs to rise
 */
-void    ISR_pulse_tacho ()      //  Interrupt Service Routine - here after each lo to hi and hi to lo transition on pulse_tacho pin
-{
-//    static uint64_t ustot = 0;
-//    uint64_t    new_time = microsecs.read_high_resolution_us();
-    static uint32_t ustot = 0;
-    uint32_t    new_time = microsecs.read_us();
-    if  (new_time < ustot)      //  rollover detection
-        ustot = 0;
-////    time_diff = (uint32_t) new_time - ustot;
-//    time_diff = new_time - ustot;   //  always 0 or positive
-    ustot = new_time;
-    tacho_count++;
+double  lpf_4th_order_asym    (double input)  {
+    static  double  lpfs[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
+    if  (input < 0.0)   input = 0.0;
+    if  (input > 1.0)   input = 1.0;
+    lpfs[0] = input;    //  zeroth order filter
+    double tmp;
+    for (int order = 1; order < 5; order++) {
+        tmp = (lpfs[order] * (1.0 - shrink))
+              + (lpfs[order - 1] * shrink);
+        if  (tmp > input)
+            tmp = input;
+        lpfs[order] = tmp;
+    }
+    return  tmp;
 }
 
-uint32_t    t_on = 0, t_off = 0, measured_pw_us = 0;
-int ver = 0, vef = 0;
+
+    VEXT_Data   Field;
+
+
 void    ISR_VEXT_rise    ()  //  InterruptIn interrupt service
-{   //  Here is possible to read back how regulator has controlled pwm
-    ver++;
-    t_on = microsecs.read_us();
+{   //  Here is possible to read back how regulator has controlled pwm - may or may not be useful
+    uint32_t    tmp = microsecs.read_us();
+    Field.measured_period = tmp - Field.t_on;
+    Field.t_on = tmp;
+    Field.rise_count++;
 }
 void    ISR_VEXT_fall    ()  //  InterruptIn interrupt service
 {
-    vef++;
-    t_off = microsecs.read_us();
-    measured_pw_us = t_off - t_on;
+    Field.fall_count++;
+    Field.t_off = microsecs.read_us();
+    Field.measured_pw_us = Field.t_off - Field.t_on;
 }
 //  ****    End of Interrupt Service Routines   ****
 
 
 /** uint32_t    ReadEngineRPM  ()
-*   System timers arranged such that tacho_ticks_per_time contains most up to the moment count of tacho ticks per second.
-*   This * 60 / number of alternator poles gives Revs Per Minute
-*   Band pass filter alternator phase output - LF rolloff about 50Hz, HF rolloff about 1500Hz
+*   
+*   June 2019 - Replaced count of alternator frequency by count of engine magneto pulses.
+*   
 */
 uint32_t    ReadEngineRPM  ()
 {
-#ifdef  MAGNETO_SPEED
     uint32_t time_since_last_spark = microsecs.read_us() - magneto_times[1];
-    if  (time_since_last_spark > 50000)     //  if engine probably stopped, return old method RPM
-        return  tacho_ticks_per_time * 60;          //  1 pulse per rev from magneto
-    return  (60000000 / magneto_times[0]);  //  60 million / microsecs between two most recent sparks
-#else
-    return  tacho_ticks_per_time * 60 / 12;   //  Numof alternator poles, 12, factored in.
-#endif
+    if  (time_since_last_spark > 250000)     //  if engine probably stopped, return old method RPM
+        return  0;
+    return  (60000000 / magneto_times[0]);  //  60 million / microsecs between two most recent sparks, eg 10,000us between sparks @ 6000 RPM
 }
 
+/*double  Read_Ext_Rev_Req    ()
+{
+    double  rv = (double) ext_rev_req;
+    return  rv / 4096.0;
+}*/
+
+double  Read_Driver_Pot    ()
+{
+    double  rv = (double) driver_reading;
+    return  rv / 4096.0;
+}
+
+double  Read_AlternatorAmps   ()
+{
+    int32_t diff    = amp_reading - amp_offset;
+    double  amps    = ((double) diff) / (1 << AMP_FILTER_FACTOR);
+    amps -= 365.0;  //  offset probably specific to particular board.
+    amps /= 1433.0;     //  fiddle factor
+    return  amps;
+}
 
 double  Read_BatteryVolts   ()
 {
-    return  (double) volt_reading / 1626.0;    //  divisor fiddled to make voltage reading correct !
+    return  ((double) volt_reading) / 3282.5;    //  divisor fiddled to make voltage reading correct !
 }
 
+void    Read_Ammeter   (double * p)  
+{
+    p[0] = amp_reading;
+    p[1] = amp_offset;
+}
+
+/**
+void    set_servo   (double p)  {   //  Only for test, called from cli
+*/
 void    set_servo   (double p)  {   //  Only for test, called from cli
     Throttle = p;
 }
@@ -345,15 +445,56 @@
     return  * p;
 }
 
-uint32_t    RPM_demand = 0;         //  For test, set from cli
+uint32_t    RPM_demand = 0;         //  For test, set from cli 'sv'
+/**
+*/
 void    set_RPM_demand  (uint32_t   d)  {
     if  (d < 10)
         d = 10;
-    if  (d > 5600)
-        d = 5600;
+    if  (d > MAX_RPM_LIMIT)
+        d = MAX_RPM_LIMIT;
     RPM_demand = d;
 }
 
+/**void    set_pwm (double d)   {   Range 0.0 to 1.0
+    This PWM used to limit max duty ratio of alternator field energisation.
+    With R25=33k and C4=100n controlling ramp input to CS pin of MCP1630 (not MCP1630V),
+    ramp terminates fet 'on' pulse after a max of approx 980 us.
+    With const   int PWM_PERIOD_US = 2000    , duty ratio is thus limited to approx 50% max.
+    This is about right when using 12V alternator on 24V systems
+    A 1.225V reference (U7) is fed to the MCP1630 error amp which compares this to fed-back proportion of system voltage.
+    This adjusts final PWM down to zero % as needed to maintain alternator output voltage.
+*/
+void    set_pwm (double d)   {
+    uint32_t i;
+    if  (d < 0.0)
+        d = 0.0;
+    if  (d > 1.0)
+        d = 1.0;
+    i = (uint32_t)(d * (PWM_PERIOD_US / 2));   //  div 2 when using 12v alternator in 24v system
+//    pc.printf   ("Setting PWM to %d\r\n", i);
+    PWM_OSC_IN.pulsewidth_us  (PWM_PERIOD_US - i);            //  Note PWM is inverted as MCP1630 uses inverted OSC_IN signal
+}
+
+void    speed_control_factor_set    (struct parameters & a)    {
+    uint32_t v = (uint32_t)a.dbl[0];
+    if  (v > 10)
+        speed_control_factor = v;
+    pc.printf   ("speed_control_factor %d\r\n", speed_control_factor);
+}
+
+void    set_throttle_limit    (struct parameters & a)    {
+    if  (a.dbl[0] > 0.01 && a.dbl[0] < 1.001)
+        throttle_limit = a.dbl[0];
+    pc.printf   ("throttle_limit %.2f\r\n", throttle_limit);
+}
+
+void    query_system    (struct parameters & a)    {
+    query_toggle = !query_toggle;
+//    pc.printf   ("Stuff about current state of system\r\n");
+//    pc.printf   ("RPM=%d, servo%.2f\r\n", ReadEngineRPM  (), servo_position);
+}
+
 extern  void    command_line_interpreter    ()  ;   //  Comms with optional pc or device using serial port through board USB socket
 extern  bool    i2c_init    ()  ;
 extern  int     check_24LC64    ()  ;
@@ -362,85 +503,129 @@
 int main()
 {
     //  local variable declarations
-    double      servo_position = 0.2;   //  set in speed control loop
+//    double      servo_position = 0.2;   //  set in speed control loop
     double      revs_error;
-    int irevs_error;
-    int ticks = 0;
-    const double    throttle_limit = 0.4;
+    double      Amps_Deliverable = 0.0;     //  New Nov 2019
+//    double      tempfilt = 0.0, servo_fucker = 0.01;
     
-    loop_timer.attach_us    (&ISR_loop_timer, MAIN_LOOP_REPEAT_TIME_US);    //  Start periodic interrupt generator
-#ifdef    MAGNETO_SPEED
-    pc.printf   ("Magneto Mode\r\n");
+    int32_t    RPM_ave = 0, RPM_filt = 0, RPM_tmp;
+    int32_t irevs_error;
+    uint32_t ticks = 0;
+    
     pulse_tacho.fall        (&ISR_magneto_tacho); //    1 pulse per engine rev
-#else
-    pc.printf   ("Alternator W signal Mode\r\n");
-    pulse_tacho.rise        (&ISR_pulse_tacho); //  Handles - Transition on filtered input version of alternator phase output
-    pulse_tacho.fall        (&ISR_pulse_tacho); //
-#endif
     VEXT.rise               (&ISR_VEXT_rise);   //  Handles - MCP1630 has just turned mosfet on
     VEXT.fall               (&ISR_VEXT_fall);   //  Handles - MCP1630 has just turned mosfet off
     microsecs.reset()   ;   //  timer = 0
     microsecs.start ()  ;   //  64 bit, counts micro seconds and times out in half million years
-    PWM_OSC_IN.period_us      (PWM_PERIOD_US); //  about 313Hz
-    PWM_OSC_IN.pulsewidth_us  (9);            //  value is int
-    A_OUT.period_us     (100);
+
+    PWM_OSC_IN.period_us      (PWM_PERIOD_US); //  about 313Hz * 2
+//  PROBLEM using same pwm, common prescaler, can't update servo that fast, can't pwm field that slow.
+
+    PWM_OSC_IN.pulsewidth_us  (PWM_PERIOD_US / 2);            //  value is int
+//    PWM_OSC_IN.pulsewidth_us  (PWM_PERIOD_US);            //  value is int
+#ifdef  TARGET_NUCLEO_F401RE    //
+    A_OUT.period_us     (100);  //  pwm as analogue out
     A_OUT.pulsewidth_us (19);
+#endif
     Throttle    = servo_position;
-    pc.printf   ("\r\n\n\n\n\nAlternator Regulator 2019, Jon Freeman, SystemCoreClock=%d\r\n", SystemCoreClock);
+    pc.printf   ("\r\n\n\n\n\nAlternator Regulator 2020, Jon Freeman, SystemCoreClock=%d\r\n", SystemCoreClock);
     if  (!i2c_init())
         pc.printf   ("i2c bus failed init\r\n");
-    //  end of local variable declarations
     pc.printf   ("check_24LC64 returned 0x%x\r\n", check_24LC64());
-    mode.load   ()  ;   //  Fetch values from eeprom, also builds table of speed -> pwm lookups
-    
+    user_settings.load   ()  ;   //  Fetch values from eeprom, also builds table of speed -> pwm lookups
+//    pc.printf   ("Loaded\r\n");
     //  Setup Complete ! Can now start main control forever loop.
+    loop_timer.attach_us    (&ISR_fast_interrupt, MAIN_LOOP_REPEAT_TIME_US / 10);    //  Start periodic interrupt generator 1000us at Feb 2020
 
 //***** START OF MAIN LOOP
     while   (1) {      //  Loop forever, repeats synchroised by waiting for ticker Interrupt Service Routine to set 'loop_flag' true
         while   (!loop_flag)  {         //  Most of the time is spent in this loop, repeatedly re-checking for commands from pc port
             command_line_interpreter    ()  ;   //  Proceed beyond here once loop_timer ticker ISR has set loop_flag true
-        }               //  Jan 2019 pass here 32 times per sec
+        }               //  Jun 2019 pass here 100 times per sec
+//  BEGIN 100Hz stuff
         loop_flag = false;              //  Clear flag set by ticker interrupt handler
-#ifdef  SPEED_CONTROL_ENABLE
-//    uint32_t    RPM_demand = 0;         //  For test, set from cli
-//    double      servo_position = 0.0;   //  set in speed control loop
-//    double      revs_error;
-
-//        time_since_last_spark = microsecs.read_us() - magneto_times[1];
-        irevs_error = RPM_demand - ReadEngineRPM  ();
-        revs_error = (double) irevs_error;
-        if  (RPM_demand < 3000)
-            servo_position = Throttle = 0.0;
-        else    {
-            servo_position += (revs_error / 75000.0);
-            servo_position = normalise(&servo_position);
-            if  (servo_position < 0.0 || servo_position > 1.0)
-                pc.printf   ("servo_position error %f\r\n", servo_position);
-            if  (servo_position > throttle_limit)
-                servo_position = throttle_limit;
-            Throttle = servo_position;
-        }
-#endif
-
-        PWM_OSC_IN.pulsewidth_us  (mode.get_pwm((int)glob_rpm));    //  Update field current according to latest measured RPM
+        
+        //  Three variations on engine rpm.
+        RPM_tmp = ReadEngineRPM ();
+        RPM_ave += RPM_tmp;     //  Rising sum needs dividing and resetting to 0 when used
+        RPM_filt += RPM_tmp;
+        RPM_filt >>= 1;
+        
+        amp_reading += (raw_amp_reading - 0.5) * AMPS_CAL;
+        amp_reading /= 2.0;
+        amp_offset  += (raw_amp_offset - 0.5) * AMPS_CAL;   //  This reading probably not useful
+        amp_offset /= 2.0;
+        
+        Amps_Deliverable = Calculate_Amps_Deliverable   (ReadEngineRPM  ());    //  Added Nov 2019, not yet used. Returns normalised 0.0 to 1.0
+        
+//        PWM_OSC_IN.pulsewidth_us  (user_settings.get_pwm(ReadEngineRPM()));    //  Update field current limit according to latest measured RPM
 
 //        while   (LocalCom.readable())   {
 //            int q = LocalCom.getc();
 //            //q++;
 //            pc.putc (q);
 //        }
+//  END 100Hz stuff
+        if  (flag_25Hz)  {
+            flag_25Hz = false;
+//  BEGIN   25Hz stuff
 
-        if  (flag_8Hz)  {   //  Do any stuff to be done 8 times per second
-            flag_8Hz    = false;
-            myled = !myled;
-            LocalCom.printf ("%d\r\n", volt_reading);
-            
-            ticks++;
-            if  (ticks > 7) {   //  once per sec stuff
+//  END 25Hz stuff
+//  BEGIN   12.5Hz stuff
+            flag_12Hz5 = !flag_12Hz5;
+            if  (flag_12Hz5)  {   //  Do any even stuff to be done 12.5 times per second
+#ifdef  SPEED_CONTROL_ENABLE
+                if  (RPM_demand < TICKOVER_RPM)
+                    servo_position = Throttle = 0.0;
+                else    {
+                    RPM_ave /= 8;
+//                    irevs_error = RPM_demand - ReadEngineRPM  ();
+                    irevs_error = RPM_demand - RPM_filt;
+                    revs_error = (double) irevs_error;
+                    if  (abs(revs_error) > 3.0)    {    //  if speed error > 3rpm, tweak, otherwise deadband
+                        //servo_position += (revs_error / 7500.0);
+                        servo_position += (revs_error / speed_control_factor);
+                        servo_position = normalise(&servo_position);
+                        if  (servo_position < 0.0 || servo_position > 1.0)
+                            pc.printf   ("servo_position error %f\r\n", servo_position);
+                        if  (servo_position > throttle_limit)
+                            servo_position = throttle_limit;
+                        Throttle = servo_position;
+                    }
+                }
+                RPM_ave = 0;    //  Reset needed
+#endif
+            }
+            else    {               //  Do odd 12.5 times per sec stuff
+                flag_12Hz5  = false;
+                myled = !myled;
+                LocalCom.printf ("%d\r\n", volt_reading);
+    //void    set_pwm (double d)   {
+                
+    //            set_pwm (user_settings.get_pwm(ReadEngineRPM()));
+                
+    /*            servo_position += servo_fucker;
+                if  (servo_position > 1.0 || servo_position < 0.0)
+                    servo_fucker *= -1.0;
+                Throttle = servo_position;
+    */            
+            }   //  End of if(flag_12Hz5)
+//  END 12.5Hz stuff
+            ticks++;    //  advances @ 25Hz
+            if  (ticks > 24) {   //  once per sec stuff
+//  BEGIN   1Hz stuff
                 ticks = 0;
-                pc.printf   ("RPM %d, err %.1f, s_p %.2f\r\n", ReadEngineRPM  (), revs_error, servo_position);
+                if  (query_toggle)  {
+                    pc.printf   ("V=%.1f\tI=%.1f\tRPM=%d\tservo%.2f\r\n", Read_BatteryVolts(), amp_reading, ReadEngineRPM  (), servo_position);
+                }
+//                pc.printf   ("Tick %d\r\n", flag_12Hz5);
+//                tempfilt *= 0.99;
+//                tempfilt += Read_AlternatorAmps() * 0.01;
+//                pc.printf   ("RPM %d, err %.1f, s_p %.2f, lut %.3f\r\n", ReadEngineRPM  (), revs_error, servo_position, user_settings.get_pwm(ReadEngineRPM()));
+//                pc.printf   ("Vbat %.2f, servo %.3f, amps %.3f, filt %.3f\r\n", Read_BatteryVolts(), servo_position, Read_AlternatorAmps(), tempfilt);
+//  END 1Hz stuff
             }   //  eo once per second stuff
-        }   //  End of if(flag_8Hz)
+        }   //  End of 100Hz stuff
     }       //  End of main programme loop
 }           //  End of main function - end of programme
 //***** END OF MAIN LOOP