Dual Brushless Motor ESC, 10-62V, up to 50A per motor. Motors ganged or independent, multiple control input methods, cycle-by-cycle current limit, speed mode and torque mode control. Motors tiny to kW. Speed limit and other parameters easily set in firmware. As used in 'The Brushless Brutalist' locomotive - www.jons-workshop.com. See also Model Engineer magazine June-October 2019.

Dependencies:   mbed BufferedSerial Servo PCT2075 FastPWM

Update 17th August 2020 Radio control inputs completed

Revision:
3:ecb00e0e8d68
Parent:
2:04761b196473
Child:
4:21d91465e4b1
--- a/main.cpp	Sat Mar 10 10:11:07 2018 +0000
+++ b/main.cpp	Sun Mar 18 08:17:56 2018 +0000
@@ -47,6 +47,9 @@
 AVW =   AVH | AWL,
 AWV =   AWH | AVL,
 
+KEEP_L_MASK_A   = AUL | AVL | AWL,
+KEEP_H_MASK_A   = AUH | AVH | AWH,
+
 BRA = AUL | AVL | AWL,
 
 BUL = 1 << 0,
@@ -64,6 +67,9 @@
 BVW =   BVH | BWL,
 BWV =   BWH | BVL,
 
+KEEP_L_MASK_B   = BUL | BVL | BWL,
+KEEP_H_MASK_B   = BUH | BVH | BWH,
+
 BRB = BUL | BVL | BWL,
 
 PORT_A_MASK = AUL | AVL | AWL | AUH | AVH | AWH,            //  NEW METHOD FOR DGD21032 MOSFET DRIVERS
@@ -109,14 +115,14 @@
 //  Pin 34  Port_B BWH
 DigitalOut  T4        (PB_14);    //  Pin 35
 DigitalOut  T3        (PB_15);    //  Pin 36
-//  BufferedSerial xb pins 37 Tx, 38 Rx
-BufferedSerial  xb          (PC_6, PC_7);    //  Pins 37, 38  tx, rx to XBee module
+//  BufferedSerial com2 pins 37 Tx, 38 Rx
+BufferedSerial  com2          (PC_6, PC_7);    //  Pins 37, 38  tx, rx to XBee module
 FastPWM     A_MAX_V_PWM     (PC_8, 1),  //  Pin 39                  pwm3/3
             A_MAX_I_PWM     (PC_9, 1); //  pin 40, prescaler value  pwm3/4
 //InterruptIn MotB_Hall   (PA_8); //  Pin 41
 //  Pin 41  Port_A AWH
-//  BufferedSerial ser3 pins 42 Tx, 43 Rx
-BufferedSerial  ser3        (PA_9, PA_10);    //    Pins 42, 43  tx, rx to any aux module
+//  BufferedSerial com3 pins 42 Tx, 43 Rx
+BufferedSerial  com3        (PA_9, PA_10);    //    Pins 42, 43  tx, rx to any aux module
 
 //  Feb 2018 Pins 44 and 45 now liberated, could use for serial or other uses
 //BufferedSerial  extra_ser   (PA_11, PA_12);    //  Pins 44, 45  tx, rx to XBee module
@@ -167,15 +173,18 @@
 /*  End of Please Do Not Alter these */
 
 /*  Global variable declarations */
+volatile    uint32_t    fast_sys_timer      = 0;    //  gets incremented by our Ticker ISR every VOLTAGE_READ_INTERVAL_US
 uint32_t    volt_reading        = 0,    //  Global updated by interrupt driven read of Battery Volts
             driverpot_reading   = 0,    //  Global updated by interrupt driven read of Drivers Pot
             sys_timer           = 0,    //  gets incremented by our Ticker ISR every MAIN_LOOP_REPEAT_TIME_US
-            fast_sys_timer      = 0,    //  gets incremented by our Ticker ISR every VOLTAGE_READ_INTERVAL_US
             AtoD_Semaphore = 0;
 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        sounder_on  = false;
-double      test_pot = 0.0, test_amps = 0.0;    //  These used in knifeandfork code testing only
+
+double      angle = 0.0,    angle_step = 0.000005,  sinv, cosv;
+
+//double      test_pot = 0.0, test_amps = 0.0;    //  These used in knifeandfork code testing only
 /*  End of Global variable declarations */
 
 Ticker  tick_vread;     //  Device to cause periodic interrupts, used to time voltage readings etc
@@ -202,7 +211,7 @@
 *   AtoD_reader() called from convenient point in code to take readings outside of ISRs
 */
 
-void    ISR_voltage_reader  ()      //  This is Ticker Interrupt Service Routine - few us between readings
+void    ISR_voltage_reader  ()      //  This is Ticker Interrupt Service Routine - few us between readings - VOLTAGE_READ_INTERVAL_US    = 50
 {
     AtoD_Semaphore++;
     fast_sys_timer++;        //  Just a handy measure of elapsed time for anything to use
@@ -308,30 +317,63 @@
 /*
     5   1   3   2   6   4  SENSOR SEQUENCE
 
-1   1   1   1   0   0   0  ---___---___
-2   0   0   1   1   1   0  __---___---_
-4   1   0   0   0   1   1  -___---___--
+1   1   1   1   0   0   0  ---___---___ Hall1
+2   0   0   1   1   1   0  __---___---_ Hall2
+4   1   0   0   0   1   1  -___---___-- Hall3
 
     UV  WV  WU  VU  VW  UW  OUTPUT SEQUENCE
 */
-const   uint16_t    A_tabl[] = {
+const   uint16_t    A_tabl[] = {  //  Origial table
     0,  0,  0,  0,  0,  0,  0,  0,  //  Handbrake
     0,  AWV,AVU,AWU,AUW,AUV,AVW,  0,  //  Forward 0, WV1, VU1, WU1, UW1, UV1, VW1, 0,    //  JP, FR, SG, PWM = 1 0 1 1   Forward1
     0,  AVW,AUV,AUW,AWU,AVU,AWV,  0,  //  Reverse 0, VW1, UV1, UW1, WU1, VU1, WV1, 0,    //  JP, FR, SG, PWM = 1 1 0 1   Reverse1
+    0,  BRA,BRA,BRA,BRA,BRA,BRA,0,   //  Regenerative Braking
+    KEEP_L_MASK_A, KEEP_H_MASK_A,   //  [32 and 33]
+    (uint16_t)((uint32_t)&MAH1 >> 16), (uint16_t)((uint32_t)&MAH1 & 0xffff),
+    (uint16_t)((uint32_t)&MAH2 >> 16), (uint16_t)((uint32_t)&MAH2 & 0xffff),
+    (uint16_t)((uint32_t)&MAH3 >> 16), (uint16_t)((uint32_t)&MAH3 & 0xffff)
+//    ((uint32_t)&MAH1),
+//    ((uint32_t)&MAH2),
+//    ((uint32_t)&MAH3)
+//    (uint16_t)((uint32_t)&MAH1 >> 16), (uint16_t)((uint32_t)&MAH1 & 0xffff),
+}   ;
+const   uint32_t    A_t2[] = {
+    (uint32_t)&MAH1,
+    (uint32_t)&MAH2,
+    (uint32_t)&MAH3
+}   ;
+/*const   uint16_t    A_tabl[] = {    //  Table revised advancing magnetic pull angle - WORKS, but sucks more power for no apparent advantage
+    0,  0,  0,  0,  0,  0,  0,  0,  //  Handbrake
+    0,  AWU,AVW,AVU,AUV,AWV,AUW,  0,  //  Forward 0, WV1, VU1, WU1, UW1, UV1, VW1, 0,    //  JP, FR, SG, PWM = 1 0 1 1   Forward1
+    0,  AVU,AUW,AVW,AWV,AWU,AUV,  0,  //  Reverse 0, VW1, UV1, UW1, WU1, VU1, WV1, 0,    //  JP, FR, SG, PWM = 1 1 0 1   Reverse1
     0,  BRA,BRA,BRA,BRA,BRA,BRA,0   //  Regenerative Braking
-}   ;
+}   ;*/
 const   uint16_t    B_tabl[] = {
     0,  0,  0,  0,  0,  0,  0,  0,  //  Handbrake
     0,  BWV,BVU,BWU,BUW,BUV,BVW,  0,  //  Forward 0, WV1, VU1, WU1, UW1, UV1, VW1, 0,    //  JP, FR, SG, PWM = 1 0 1 1   Forward1
     0,  BVW,BUV,BUW,BWU,BVU,BWV,  0,  //  Reverse 0, VW1, UV1, UW1, WU1, VU1, WV1, 0,    //  JP, FR, SG, PWM = 1 1 0 1   Reverse1
-    0,  BRB,BRB,BRB,BRB,BRB,BRB,0   //  Regenerative Braking
+    0,  BRB,BRB,BRB,BRB,BRB,BRB,0,   //  Regenerative Braking
+    KEEP_L_MASK_B, KEEP_H_MASK_B,
+    (uint16_t)((uint32_t)&MBH1 >> 16), (uint16_t)((uint32_t)&MBH1 & 0xffff),
+    (uint16_t)((uint32_t)&MBH2 >> 16), (uint16_t)((uint32_t)&MBH2 & 0xffff),
+    (uint16_t)((uint32_t)&MBH3 >> 16), (uint16_t)((uint32_t)&MBH3 & 0xffff)
+//    ((uint32_t)&MBH1),
+//    ((uint32_t)&MBH2),
+//    ((uint32_t)&MBH3)
+}   ;
+const   uint32_t    B_t2[] = {
+    (uint32_t)&MBH1,
+    (uint32_t)&MBH2,
+    (uint32_t)&MBH3
 }   ;
 
+//   void * vp[] = {(void*)MAH1, (void*)MAH2};
 
 class   motor
 {
     uint32_t    Hall_total, mode, edge_count_table[MAIN_LOOP_ITERATION_Hz]; //  to contain one seconds worth
-    uint32_t    Hall_tab_ptr, latest_pulses_per_sec;
+    uint32_t    latest_pulses_per_sec, Hall_tab_ptr, direction, ppstmp;
+    bool    moving_flag;
     const   uint16_t * lut;
     FastPWM * maxV, * maxI;
     PortOut * Motor_Port;
@@ -340,39 +382,40 @@
     struct  currents    {
         uint32_t    max, min, ave;
     }  I;
+    int32_t     angle_cnt;
     uint32_t    current_samples[CURRENT_SAMPLES_AVERAGED];  //  Circular buffer where latest current readings get stored
-    uint32_t    Hindex;
-//    PinName     Hall1, Hall2, Hall3;
+    uint32_t    Hindex[2], tickleon, encoder_error_cnt;
     motor   ()  {}  ;   //  Default constructor
-    motor   (PortOut * , FastPWM * , FastPWM * , const uint16_t * , InterruptIn * H1, InterruptIn * H2, InterruptIn * H3)  ;
+    motor   (PortOut * , FastPWM * , FastPWM * , const uint16_t *, const uint32_t *)  ;
     void    set_V_limit (double)    ;  //  Sets max motor voltage
     void    set_I_limit (double)    ;  //  Sets max motor current
     void    Hall_change ()  ;           //  Called in response to edge on Hall input pin
-    bool    set_mode    (int);
-    void    current_calc    ()  ;
-    uint32_t    pulses_per_sec   ()  ;    //  call this once per main loop pass to keep count = edges per sec
-    int     read_Halls  ()  ;
-    void    tickle  ()  ;
-}   ;   //MotorA, MotorB;
+    void    motor_set   ()  ;           //  Energise Port with data determined by Hall sensors
+    void    direction_set   (int)  ;    //  sets 'direction' with bit pattern to eor with FORWARD or REVERSE in set_mode
+    bool    set_mode    (int);          //  sets mode to HANDBRAKE, FORWARD, REVERSE or REGENBRAKE
+    bool    is_moving   ()  ;           //  Returns true if one or more Hall transitions within last 31.25 milli secs
+    void    current_calc    ()  ;       //  Updates 3 uint32_t I.min, I.ave, I.max
+    uint32_t    pulses_per_sec   ()  ;  //  call this once per main loop pass to keep count = edges per sec
+    int     read_Halls  ()  ;           //  Returns 3 bits of latest Hall sensor outputs
+    void    high_side_off   ()  ;
+}   ;   //MotorA, MotorB, or even Motor[2];
 
-motor   MotorA  (&MotA, &A_MAX_V_PWM, &A_MAX_I_PWM, A_tabl, &MAH1, &MAH2, &MAH3);
-motor   MotorB  (&MotB, &B_MAX_V_PWM, &B_MAX_I_PWM, B_tabl, &MBH1, &MBH2, &MBH3);
+motor   MotorA  (&MotA, &A_MAX_V_PWM, &A_MAX_I_PWM, A_tabl, A_t2);
+motor   MotorB  (&MotB, &B_MAX_V_PWM, &B_MAX_I_PWM, B_tabl, B_t2);
 
-motor::motor    (PortOut * P , FastPWM * _maxV_ , FastPWM * _maxI_ , const uint16_t * lutptr, InterruptIn * H1, InterruptIn * H2, InterruptIn * H3)        //  Constructor
-{
-    Hall1 =H1;
-    Hall2 =H2;
-    Hall3 =H3;
+motor * MotPtr[8];  //  Array of pointers to some number of motor objects
+
+motor::motor    (PortOut * P , FastPWM * _maxV_ , FastPWM * _maxI_ , const uint16_t * lutptr, const uint32_t * lut32ptr)        //  Constructor
+{   //  Constructor
     maxV = _maxV_;
     maxI = _maxI_;
-    Hall_total = mode = 0;  //  mode can be only 0, 8, 16 or 24, lut row select for Handbrake, Forward, Reverse, or Regen Braking
-    Hindex = 0;
-    Hall_tab_ptr = 0;
+    Hall_total = 0;  //  mode can be only 0, 8, 16 or 24, lut row select for Handbrake, Forward, Reverse, or Regen Braking
     latest_pulses_per_sec = 0;
     for (int i = 0; i < MAIN_LOOP_ITERATION_Hz; i++)
         edge_count_table[i] = 0;
     if  (lutptr != A_tabl && lutptr != B_tabl)
         pc.printf   ("Fatal in 'motor' constructor, Invalid lut address\r\n");
+    Hall_tab_ptr = 0;
     Motor_Port = P;
     pc.printf   ("In motor constructor, Motor port = %lx\r\n", P);
     maxV->period_ticks      (MAX_PWM_TICKS + 1);  //  around 18 kHz
@@ -383,7 +426,26 @@
 //        pc.printf   ("Fatal in 'motor' constructor, Invalid Port\r\n");
 //    else
 //        PortOut Motor_P (P, *mask);     //  PortA for motor A, PortB for motor B
+    mode    = REGENBRAKE;
     lut = lutptr;
+    Hindex[0] = Hindex[1]  = read_Halls    ();
+    ppstmp  = 0;
+    tickleon    = 0;
+    direction   = 0;
+    angle_cnt   = 0;        //  Incremented or decremented on each Hall event according to actual measured direction of travel
+    encoder_error_cnt = 0;  //  Incremented when Hall transition not recognised as either direction
+    Hall1 = (InterruptIn *)(((uint32_t)lut[34] << 16) | (uint32_t)lut[35]);
+    Hall2 = (InterruptIn *)(((uint32_t)lut[36] << 16) | (uint32_t)lut[37]);
+    Hall3 = (InterruptIn *)(((uint32_t)lut[38] << 16) | (uint32_t)lut[39]);
+//    Hall1 = (InterruptIn *)lut32ptr[0];
+//    Hall1 = (InterruptIn *)lut32ptr[1];
+//    Hall1 = (InterruptIn *)lut32ptr[2];
+}
+
+void    motor::direction_set   (int dir)  {
+    if  (dir != 0)
+        dir = FORWARD | REVERSE;  //  bits used in eor
+    direction = dir;
 }
 
 int     motor::read_Halls  ()  {
@@ -391,26 +453,13 @@
     if  (*Hall1 != 0)    x |= 1;
     if  (*Hall2 != 0)    x |= 2;
     if  (*Hall3 != 0)    x |= 4;
-    Hindex = x;
     return  x;
-//    return  Hindex;
 }
 
-void    motor::tickle   ()  //  Attempt to get mosfet driver to drive high side fets
-{
-    volatile int t;
-    for (int cnt = 0; cnt < 20; cnt++)   {
-        *Motor_Port = 0;
-        t = fast_sys_timer;
-        int x = read_Halls  () | mode;
-        pc.printf   ("Ti");
-        *Motor_Port = lut[x];
-        pc.printf   ("%d\t", t);
-    }
-}
-
-void    tickle  ()  {
-    MotorA.tickle   ();
+void    motor::high_side_off   ()  {
+    uint16_t    p = *Motor_Port;
+    p &= lut[32];   //  KEEP_L_MASK_A or B
+    *Motor_Port = p;
 }
 
 void    motor::current_calc ()
@@ -457,123 +506,96 @@
     maxI->pulsewidth_ticks  (a);  //  PWM
 }
 
-uint32_t    motor::pulses_per_sec   ()       //  call this once per main loop pass to keep count = edges per sec
-{
-    uint32_t    tmp = Hall_total;
-    latest_pulses_per_sec = tmp - edge_count_table[Hall_tab_ptr];
-    edge_count_table[Hall_tab_ptr] = tmp;
+uint32_t    motor::pulses_per_sec   ()       //  call this once per 'MAIN_LOOP_REPEAT_TIME_US= 31250' main loop pass to keep count = edges per sec
+{           //  Can also test for motor running or not here
+    if  (ppstmp == Hall_total)  {
+        moving_flag  = false;       //  Zero Hall transitions since previous call - motor not moving
+        tickleon    = 100;
+    }
+    else    {
+        moving_flag  = true;
+        ppstmp = Hall_total;
+    }
+    latest_pulses_per_sec = ppstmp - edge_count_table[Hall_tab_ptr];
+    edge_count_table[Hall_tab_ptr] = ppstmp;
     Hall_tab_ptr++;
     if  (Hall_tab_ptr >= MAIN_LOOP_ITERATION_Hz)
         Hall_tab_ptr = 0;
     return  latest_pulses_per_sec;
 }
 
+bool    motor::is_moving ()
+{
+    return  moving_flag;
+}
+
 bool    motor::set_mode (int m)
 {
     if  ((m != HANDBRAKE) && (m != FORWARD) && (m != REVERSE) && (m !=REGENBRAKE))  {
         pc.printf   ("Error in set_mode, invalid mode %d\r\n", m);
         return  false;
     }
+    if  (m == FORWARD || m == REVERSE)
+        m ^= direction;
     mode = m;
     return  true;
 }
 
 void    motor::Hall_change  ()
 {
+    const   int32_t delta_theta_lut[] =     //  Looks up -1 for forward move detected, +1 for reverse move detected, 0 for error or unknown
+            {
+                0, 0, 0, 0, 0, 0, 0, 0,     //  Previous Hindex was 0
+                0, 0, 0,-1, 0, 1, 0, 0,     //  Previous Hindex was 1
+                0, 0, 0, 1, 0, 0,-1, 0,     //  Previous Hindex was 2
+                0, 1,-1, 0, 0, 0, 0, 0,     //  Previous Hindex was 3
+                0, 0, 0, 0, 0,-1, 1, 0,     //  Previous Hindex was 4
+                0,-1, 0, 0, 1, 0, 0, 0,     //  Previous Hindex was 5
+                0, 0, 1, 0,-1, 0, 0, 0,     //  Previous Hindex was 6
+                0, 0, 0, 0, 0, 0, 0, 0,     //  Previous Hindex was 7
+            }  ;
+    int32_t delta_theta = delta_theta_lut[(Hindex[1] << 3) | Hindex[0]];
+    if  (delta_theta == 0)
+        encoder_error_cnt++;
+    else
+        angle_cnt += delta_theta;
+    *Motor_Port = lut[mode | Hindex[0]];
     Hall_total++;
-//    mode &= 0x038L; //  safety
-    *Motor_Port = lut[mode | Hindex];
+    Hindex[1] = Hindex[0];
 }
 
-void    MAH1r    ()
-{
-    MotorA.Hindex = 1;
-    if  (MAH2 != 0) MotorA.Hindex |= 2;
-    if  (MAH3 != 0) MotorA.Hindex |= 4;
-    MotorA.Hall_change  ();
-}
-void    MAH1f    ()
-{
-    MotorA.Hindex = 0;
-    if  (MAH2 != 0) MotorA.Hindex |= 2;
-    if  (MAH3 != 0) MotorA.Hindex |= 4;
-    MotorA.Hall_change  ();
-}
-void    MAH2r    ()
+void    MAH_isr    ()
 {
-    MotorA.Hindex = 2;
-    if  (MAH1 != 0) MotorA.Hindex |= 1;
-    if  (MAH3 != 0) MotorA.Hindex |= 4;
-    MotorA.Hall_change  ();
-}
-void    MAH2f    ()
-{
-    MotorA.Hindex = 0;
-    if  (MAH1 != 0) MotorA.Hindex |= 1;
-    if  (MAH3 != 0) MotorA.Hindex |= 4;
-    MotorA.Hall_change  ();
-}
-void    MAH3r    ()
-{
-    MotorA.Hindex = 4;
-    if  (MAH1 != 0) MotorA.Hindex |= 1;
-    if  (MAH2 != 0) MotorA.Hindex |= 2;
-    MotorA.Hall_change  ();
-}
-void    MAH3f    ()
-{
-    MotorA.Hindex = 0;
-    if  (MAH1 != 0) MotorA.Hindex |= 1;
-    if  (MAH2 != 0) MotorA.Hindex |= 2;
+    uint32_t x = 0;
+    MotorA.high_side_off    ();
+    T3 = !T3;
+    if  (MAH1 != 0) x |= 1;
+    if  (MAH2 != 0) x |= 2;
+    if  (MAH3 != 0) x |= 4;
+    MotorA.Hindex[0] = x;       //  New in [0], old in [1]
     MotorA.Hall_change  ();
 }
 
-void    MBH1r    ()
-{
-    MotorB.Hindex = 1;
-    if  (MBH2 != 0) MotorB.Hindex |= 2;
-    if  (MBH3 != 0) MotorB.Hindex |= 4;
-    MotorB.Hall_change  ();
-}
-void    MBH1f    ()
-{
-    MotorB.Hindex = 0;
-    if  (MBH2 != 0) MotorB.Hindex |= 2;
-    if  (MBH3 != 0) MotorB.Hindex |= 4;
-    MotorB.Hall_change  ();
-}
-void    MBH2r    ()
+void    MBH_isr    ()
 {
-    MotorB.Hindex = 2;
-    if  (MBH1 != 0) MotorB.Hindex |= 1;
-    if  (MBH3 != 0) MotorB.Hindex |= 4;
-    MotorB.Hall_change  ();
-}
-void    MBH2f    ()
-{
-    MotorB.Hindex = 0;
-    if  (MBH1 != 0) MotorB.Hindex |= 1;
-    if  (MBH3 != 0) MotorB.Hindex |= 4;
-    MotorB.Hall_change  ();
-}
-void    MBH3r    ()
-{
-    MotorB.Hindex = 4;
-    if  (MBH1 != 0) MotorB.Hindex |= 1;
-    if  (MBH2 != 0) MotorB.Hindex |= 2;
-    MotorB.Hall_change  ();
-}
-void    MBH3f    ()
-{
-    MotorB.Hindex = 0;
-    if  (MBH1 != 0) MotorB.Hindex |= 1;
-    if  (MBH2 != 0) MotorB.Hindex |= 2;
+    uint32_t x = 0;
+    MotorB.high_side_off    ();
+    if  (MBH1 != 0) x |= 1;
+    if  (MBH2 != 0) x |= 2;
+    if  (MBH3 != 0) x |= 4;
+    MotorB.Hindex[0] = x;
     MotorB.Hall_change  ();
 }
 
 
 //  End of Interrupt Service Routines
 
+void    motor::motor_set  ()
+{
+    Hindex[0]  = read_Halls    ();
+    *Motor_Port = lut[mode | Hindex[0]];
+}
+
 void    setVI   (double v, double i)  {
 //    void    set_V_limit (double)    ;  //  Sets max motor voltage
 //    void    set_I_limit (double)    ;  //  Sets max motor current
@@ -583,9 +605,38 @@
     MotorB.set_I_limit  (i);
 }
 
-void    AtoD_reader ()
+void    sincostest  ()  {
+    sinv = sin(angle);  //  to set speed and direction of MotorA
+    cosv = cos(angle);  //  to set speed and direction of MotorB
+    angle += angle_step;
+    if  (angle > TWOPI)
+        angle -= TWOPI;
+    if  (sinv > 0.0)
+        MotorA.set_mode (FORWARD);
+    else    {
+        MotorA.set_mode (REVERSE);
+        sinv = -sinv;
+    }
+    MotorA.set_V_limit  (0.01 + (sinv / 8.0));
+    if  (cosv > 0.0)
+        MotorB.set_mode (FORWARD);
+    else    {
+        MotorB.set_mode (REVERSE);
+        cosv = -cosv;
+    }
+    MotorB.set_V_limit  (0.01 + (cosv / 8.0));
+}
+
+void    AtoD_reader ()  //  Call to here every VOLTAGE_READ_INTERVAL_US    = 50 once loop responds to flag set in isr
 {
     static uint32_t i = 0, tab_ptr = 0;
+
+    sincostest  ();
+
+    if  (MotorA.tickleon)
+        MotorA.high_side_off    ();
+    if  (MotorB.tickleon)
+        MotorB.high_side_off    ();
     if  (AtoD_Semaphore > 20)   {
         pc.printf   ("WARNING - silly semaphore count %d, limiting to sensible\r\n", AtoD_Semaphore);
         AtoD_Semaphore = 20;
@@ -614,6 +665,14 @@
         i++;    //  prepare to read the next input in response to the next interrupt
         if  (i > 3)
             i = 0;
+    }   //  end of while (AtoD_Semaphore > 0)    {
+    if  (MotorA.tickleon)   {
+        MotorA.tickleon--;
+        MotorA.motor_set    ();
+    }
+    if  (MotorB.tickleon)   {
+        MotorB.tickleon--;
+        MotorB.motor_set    ();
     }
 }
 
@@ -651,6 +710,23 @@
 extern  bool    wr_24LC64  (int mem_start_addr, char * source, int length)   ;
 extern  bool    rd_24LC64  (int mem_start_addr, char * dest, int length)   ;
 
+struct  motorpairoptions    {   //  This to be user settable in eeprom, 32 bytes
+    uint8_t MotA_dir,   //  0 or 1
+            MotB_dir,   //  0 or 1
+            gang,       //  0 for separate control (robot mode), 1 for ganged loco bogie mode
+            serv1,      //  0, 1, 2 = Not used, Input, Output
+            serv2,      //  0, 1, 2 = Not used, Input, Output
+            cmd_source, //  0 Invalid, 1 COM1, 2 COM2, 3 Pot, 4 Servo1, 5 Servo2
+            last;
+}   ;
+
+int I_Am    ()  {   //  Returns boards id number as ASCII char
+    int i = J3;
+    if  (i != 0)
+        i = 1;
+    return  i | '0';
+}
+
 int main()
 {
     int j = 0;
@@ -658,20 +734,30 @@
 
     MotA   = 0;     //  Motor drive ports A and B
     MotB   = 0;
+    MotPtr[0] = &MotorA;
+    MotPtr[1] = &MotorB;
+
+    MAH1.rise   (& MAH_isr);
+    MAH1.fall   (& MAH_isr);
+    MAH2.rise   (& MAH_isr);
+    MAH2.fall   (& MAH_isr);
+    MAH3.rise   (& MAH_isr);
+    MAH3.fall   (& MAH_isr);
+
+    MBH1.rise   (& MBH_isr);
+    MBH1.fall   (& MBH_isr);
+    MBH2.rise   (& MBH_isr);
+    MBH2.fall   (& MBH_isr);
+    MBH3.rise   (& MBH_isr);
+    MBH3.fall   (& MBH_isr);
+/*
     MAH1.rise   (& MAH1r);
     MAH1.fall   (& MAH1f);
     MAH2.rise   (& MAH2r);
     MAH2.fall   (& MAH2f);
     MAH3.rise   (& MAH3r);
     MAH3.fall   (& MAH3f);
-
-    MBH1.rise   (& MBH1r);
-    MBH1.fall   (& MBH1f);
-    MBH2.rise   (& MBH2r);
-    MBH2.fall   (& MBH2f);
-    MBH3.rise   (& MBH3r);
-    MBH3.fall   (& MBH3f);
-
+*/
     MAH1.mode   (PullUp);
     MAH2.mode   (PullUp);
     MAH3.mode   (PullUp);
@@ -687,6 +773,10 @@
     const int TXTBUFSIZ = 36;
     char    buff[TXTBUFSIZ];
     bool    eerom_detected = false;
+    pc.baud     (9600);
+    com3.baud   (9600);
+    com2.baud   (9600);
+    
     pc.printf   ("RAM test - ");
     if  (check_24LC64() != 0xa0)  //  searches for i2c devices, returns address of highest found
         pc.printf   ("Check for 24LC64 eeprom FAILED\r\n");
@@ -708,6 +798,7 @@
     T4 = 0;
     T5 = 0;
     T6 = 0;
+//    MotPtr[0]->set_mode (REGENBRAKE);
     MotorA.set_mode (REGENBRAKE);
     MotorB.set_mode (REGENBRAKE);
 //    MotorA.set_mode (FORWARD);
@@ -718,6 +809,26 @@
     MotorB.set_I_limit  (0.5);
 
     //  Setup Complete ! Can now start main control forever loop.
+    //  March 16th 2018 thoughts !!!
+    //  Control from one of several sources and types as selected in options bytes in eeprom.
+    //  Control could be from e.g. Pot, Com2, Servos etc.
+    //  Suggest e.g.
+    /*
+    switch  (mode_byte) {   //  executed once only upon startup
+        case    POT:
+            while   (1) {   //  forever loop
+                call    common_stuff    ();
+                ...
+            }
+            break;
+        case    COM2:
+            while   (1) {   //  forever loop
+                call    common_stuff    ();
+                ...
+            }
+            break;
+    }
+    */
 
     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
@@ -727,22 +838,20 @@
         loop_flag = false;              //  Clear flag set by ticker interrupt handler
         Apps    = MotorA.pulses_per_sec   ();   //  Needed to keep table updated to give reading in Hall transitions per second
         Bpps    = MotorB.pulses_per_sec   ();
-
+        T4 = !T4;   //  toggle to hang scope on to verify loop execution
         //  do stuff
         if  (flag_8Hz)  {   //  do slower stuff
             flag_8Hz    = false;
- //           LED = !LED;                   // Toggle LED on board, should be seen to fast flash
+            LED = !LED;                   // Toggle LED on board, should be seen to fast flash
             j++;
             if  (j > 6)    {   //  Send some status info out of serial port every second and a bit or thereabouts
                 j = 0;
-            LED = !LED;                   // Toggle LED on board, should be seen to fast flash
             MotorA.current_calc ();
             MotorB.current_calc ();
-//            pc.printf   ("Hello\r\n");
-//            uint8_t stat;
-//                pc.printf   ("Arpm %d, Brpm %d, sys_timer %d, HA %d, HB %d\r\n", (Apps * 60) / 24, (Bpps * 60) / 24, sys_timer, MotorA.read_Halls  (), MotorB.read_Halls  ());
-//                pc.printf   ("Apps %d, Bpps %d, sys_timer %d, HA %d, HB %d\r\n", Apps, Bpps, sys_timer, MotorA.read_Halls  (), MotorB.read_Halls  ());
-                pc.printf   ("V=%+.2f, Pot=%+.2f, HA %d, HB %d, IA %d, IB %d, Arpm %d, Brpm %d\r\n", Read_BatteryVolts(), Read_DriverPot(), MotorA.read_Halls  (), MotorB.read_Halls  (), MotorA.I.ave, MotorB.I.ave, (Apps * 60) / 24, (Bpps * 60) / 24);
+//                pc.printf   ("V=%+.2f, Pot=%+.2f, HA %d, HB %d, IA %d, IB %d, Arpm %d, Brpm %d\r\n", Read_BatteryVolts(), Read_DriverPot(), MotorA.read_Halls  (), MotorB.read_Halls  (), MotorA.I.ave, MotorB.I.ave, (Apps * 60) / 24, (Bpps * 60) / 24);
+                //pc.printf   ("V=%+.2f, Pot=%+.2f, HA %d, HB %d, IAmin %d, IAave %d, IAmax %d, IB %d, Arpm %d, Brpm %d\r\n", Read_BatteryVolts(), Read_DriverPot(), MotorA.read_Halls  (), MotorB.read_Halls  (), MotorA.I.min, MotorA.I.ave, MotorA.I.max, MotorB.I.ave, (Apps * 60) / 24, (Bpps * 60) / 24);
+                pc.printf   ("\tAangle_cnt %d\tAencoder_error_cnt %d", MotorA.angle_cnt, MotorA.encoder_error_cnt);
+                pc.printf   ("\tBangle_cnt %d\tBencoder_error_cnt %d, J3 %d\r\n", MotorB.angle_cnt, MotorB.encoder_error_cnt, J3 == 0 ? 0 : 1);
             }
         }   //  End of if(flag_8Hz)
     }       //  End of main programme loop