Electric Locomotive control system. Touch screen driver control, includes regenerative braking, drives 4 brushless motors, displays speed MPH, system volts and power

Dependencies:   BSP_DISCO_F746NG FastPWM LCD_DISCO_F746NG SD_DISCO_F746NG TS_DISCO_F746NG mbed

Files at this revision

API Documentation at this revision

Comitter:
JonFreeman
Date:
Mon Nov 13 09:53:00 2017 +0000
Parent:
0:23cc72b18e74
Commit message:
Brushless Motor electric locomotive congtrol system; Drives 4 motors using touch-screen control.; Displays speed MPH, system volts and power

Changed in this revision

BSP_DISCO_F746NG.lib Show annotated file Show diff for this revision Revisions of this file
Electric_Loco.h Show annotated file Show diff for this revision Revisions of this file
costab.cpp Show annotated file Show diff for this revision Revisions of this file
dro.h Show diff for this revision Revisions of this file
graphics.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
sd_card.cpp Show annotated file Show diff for this revision Revisions of this file
throttle.cpp Show annotated file Show diff for this revision Revisions of this file
diff -r 23cc72b18e74 -r 8ef34deb5177 BSP_DISCO_F746NG.lib
--- a/BSP_DISCO_F746NG.lib	Sun Nov 12 06:26:29 2017 +0000
+++ b/BSP_DISCO_F746NG.lib	Mon Nov 13 09:53:00 2017 +0000
@@ -1,1 +1,1 @@
-https://developer.mbed.org/teams/ST/code/BSP_DISCO_F746NG/#5f0817e857ea
+https://os.mbed.com/users/JonFreeman/code/BSP_DISCO_F746NG/#5f0817e857ea
diff -r 23cc72b18e74 -r 8ef34deb5177 Electric_Loco.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Electric_Loco.h	Mon Nov 13 09:53:00 2017 +0000
@@ -0,0 +1,80 @@
+/*  Updated 12 Nov 2017
+    Jon Freeman
+
+  5" and 7.25" gauge Electric Locomotive Controller - ST DISCO-F746NG
+Uses built in display and touch screen.
+
+Display shows 'analogue' moving coil meter movements for :
+    Locomotive speed Miles per Hour
+    System voltage (range 20v - 90v or thereabouts)
+    Power Watts delivered to drive motors.
+
+Touch screen has three 'buttons', these are currently unused, and are where the meter movements show.
+Idea is to use two for two horns,
+
+Display has 'slider' touch control. This drives the loco.
+Control in central position when not driving or drfting.
+Moving towards bottom of screen applies regenerative braking - move further down applies harder braking.
+Moving towards top of screen powers drive motors, move further up applies more torque (current controller implemented)
+Take finger off and control drifts down to central 'neutral' position.
+*/
+#define MAX_TOUCHES 6       //  Touch screen can decode up to this many simultaneous finger press positions
+#define NEUTRAL_VAL   150   //  Number of pixels
+
+#define SLIDERX 418         //  slider graphic x position
+#define SLIDERY 2           //  slider graphic y position
+#define SLIDERW 50          //  pixel width of slider
+#define SLIDERH 268         //  pixel height of slider
+
+//  To get speedo reading correctly, need to use correct gear ratio and wheel size info
+//#define BOGIE_5_INCH
+#define BOGIE_7_and_a_quarter_INCH
+
+const   int
+
+    NUMBER_OF_MOTORS    = 4,    //  1 to 6 motors
+
+    BUTTON_RAD  = (SLIDERW / 2) - 4,    //  radius of circular 'knob' in slider control
+    MIN_POS     = BUTTON_RAD + 5,               //  top of screen
+    MAX_POS     = SLIDERH - (BUTTON_RAD + 1),   //  bottom of screen
+    CIRC_CTR = SLIDERX + BUTTON_RAD + 4;
+
+static const double
+#ifdef  BOGIE_7_and_a_quarter_INCH
+    MOTOR_PINION_T  = 17.0, //  motor pinion teeth, wheel gear teeth and wheel dia required to calculate speed and distance.
+    WHEEL_GEAR_T    = 76.0,
+    WHEEL_DIA_MM    = 147.0,
+#endif
+#ifdef  BOGIE_5_INCH
+    MOTOR_PINION_T  = 27.0, //  motor pinion teeth, wheel gear teeth and wheel dia required to calculate speed and distance.
+    WHEEL_GEAR_T    = 85.0,
+    WHEEL_DIA_MM    = 98.0,
+#endif
+    PI = 4.0 * atan(1.0),
+    WHEEL_CIRCUMFERENCE_METRE = PI * WHEEL_DIA_MM / 1000.0,
+    PULSES_PER_WHEEL_REV    = 32.0 * WHEEL_GEAR_T / MOTOR_PINION_T,
+    PULSES_PER_METRE        = PULSES_PER_WHEEL_REV / WHEEL_CIRCUMFERENCE_METRE,
+    rpm2mph         = 60.0                                      //  = Motor Revs per hour;
+                  * (MOTOR_PINION_T / WHEEL_GEAR_T)   //  = Wheel rev per hour
+                  * WHEEL_CIRCUMFERENCE_METRE         //  = metres per hour
+                  * 39.37                             //  = inches per hour
+                  / (1760 * 36)                       //  = miles per hour
+                  ;
+
+const   double LOCO_HANDBRAKE_ESCAPE_SPEED = 0.5;
+
+enum    {NO_DPS, ONE_DP};
+//  Assign unique number to every button we may use, and keep count of total number of them
+enum        {
+            ENTER, SLIDER, SPEEDO_BUT, VMETER_BUT, AMETER_BUT,
+            NUMOF_BUTTONS}  ;   //  button names
+enum        {
+            STATES, INACTIVE, RUN, NEUTRAL_DRIFT, REGEN_BRAKE, PARK, HANDBRAKE_SLIPPING};
+
+struct  slide   {   int position;    int    oldpos; int state; int direction;   bool recalc_run;    bool handbrake_slipping;    
+                    double handbrake_effort;   double   loco_speed;  }   ;
+struct  point   {   int x;    int y;  }   ;
+//struct  rect    {   struct point a, b; }   ;
+struct  key     {   int keynum; int x;  int y;  bool pressed;  }   ;
+struct  ky_bd   {   int count,  slider_y; key ky[MAX_TOUCHES + 1];   bool  sli;   }  ;
+
diff -r 23cc72b18e74 -r 8ef34deb5177 costab.cpp
--- a/costab.cpp	Sun Nov 12 06:26:29 2017 +0000
+++ b/costab.cpp	Mon Nov 13 09:53:00 2017 +0000
@@ -1,4 +1,8 @@
 #include "mbed.h"
+/*
+Purpose of this file is to provide sin and cos functions. Yes, C++ has these already, but using inbuilt sin and cos on
+DISCO-F746NG causes display to flicker! Interrupt getting missed or whatever, these look-up functions solve the flicker !
+*/
 static const double HALF_PI = 2.0 * atan(1.0);
 static const double TWO_PI  = 8.0 * atan(1.0);
 
diff -r 23cc72b18e74 -r 8ef34deb5177 dro.h
--- a/dro.h	Sun Nov 12 06:26:29 2017 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/*  Updated 12 Nov 2017
-    Jon Freeman
-
-  5" and 7.25" gauge Electric Locomotive Controller - ST DISCO-F746NG
-Uses built in display and touch screen.
-
-Display shows 'analogue' moving coil meter movements for :
-    Locomotive speed Miles per Hour
-    System voltage (range 20v - 90v or thereabouts)
-    Power Watts delivered to drive motors.
-
-Touch screen has three 'buttons', these are currently unused, and are where the meter movements show.
-Idea is to use two for two horns,
-
-Display has 'slider' touch control. This drives the loco.
-Control in central position when not driving or drfting.
-Moving towards bottom of screen applies regenerative braking - move further down applies harder braking.
-Moving towards top of screen powers drive motors, move further up applies more torque (current controller implemented)
-Take finger off and control drifts down to central 'neutral' position.
-*/
-#define MAX_TOUCHES 6       //  Touch screen can decode up to this many simultaneous finger press positions
-#define NEUTRAL_VAL   150   //  Number of pixels
-
-#define SLIDERX 418         //  slider graphic x position
-#define SLIDERY 2           //  slider graphic y position
-#define SLIDERW 50          //  pixel width of slider
-#define SLIDERH 268         //  pixel height of slider
-const   int
-BUTTON_RAD  = (SLIDERW / 2) - 4,    //  radius of circular 'knob' in slider control
-MIN_POS     = BUTTON_RAD + 5,               //  top of screen
-MAX_POS     = SLIDERH - (BUTTON_RAD + 1),   //  bottom of screen
-CIRC_CTR = SLIDERX + BUTTON_RAD + 4;
-
-static const double PI = 4.0 * atan(1.0);
-const   double LOCO_HANDBRAKE_ESCAPE_SPEED = 0.5;
-
-enum    {NO_DPS, ONE_DP};
-//  Assign unique number to every button we may use, and keep count of total number of them
-enum        {
-            ENTER, SLIDER, SPEEDO_BUT, VMETER_BUT, AMETER_BUT,
-            NUMOF_BUTTONS}  ;   //  button names
-enum        {
-            STATES, INACTIVE, RUN, NEUTRAL_DRIFT, REGEN_BRAKE, PARK, HANDBRAKE_SLIPPING};
-
-struct  slide   {   int position;    int    oldpos; int state; int direction;   bool recalc_run;    bool handbrake_slipping;    
-                    double handbrake_effort;   double   loco_speed;  }   ;
-struct  point   {   int x;    int y;  }   ;
-//struct  rect    {   struct point a, b; }   ;
-struct  key     {   int keynum; int x;  int y;  bool pressed;  }   ;
-struct  ky_bd   {   int count,  slider_y; key ky[MAX_TOUCHES + 1];   bool  sli;   }  ;
-
diff -r 23cc72b18e74 -r 8ef34deb5177 graphics.cpp
--- a/graphics.cpp	Sun Nov 12 06:26:29 2017 +0000
+++ b/graphics.cpp	Mon Nov 13 09:53:00 2017 +0000
@@ -1,7 +1,7 @@
 #include "mbed.h"
 #include "TS_DISCO_F746NG.h"
 #include "LCD_DISCO_F746NG.h"
-#include "dro.h"
+#include "Electric_Loco.h"
 
 #define VOLTMETER_X 68      //  Voltmeter screen position
 #define VOLTMETER_Y 68
@@ -331,9 +331,11 @@
     Powermeter.setup   (AMMETER_X, AMMETER_Y, V_A_SIZE, -1400.0, 1400.0, 1.25 * PI, -0.25 * PI , 14, "Watt", NO_DPS);
 }
 
-void    update_meters  (double speed, double current, double voltage)
+//void    update_meters  (double speed, double current, double voltage)
+void    update_meters  (double speed, double power, double voltage)
 {
-    Powermeter.set_value(voltage * current);
+//    Powermeter.set_value(voltage * current);
+    Powermeter.set_value(power);
     Voltmeter.set_value (voltage);
     Speedo.set_value    (speed);
 }
diff -r 23cc72b18e74 -r 8ef34deb5177 main.cpp
--- a/main.cpp	Sun Nov 12 06:26:29 2017 +0000
+++ b/main.cpp	Mon Nov 13 09:53:00 2017 +0000
@@ -1,7 +1,8 @@
 //  Electric Locomotive Controller
 //  Jon Freeman  B. Eng Hons
 
-//  Last Updated 12 April 2017
+//  Last Updated 13 November 2017
+
 //  Touch Screen Loco 2017 - WITH SD card data logger functions
 
 //  This code runs on STM 32F746NG DISCO module, high performance ARM Cortex with touch screen
@@ -16,15 +17,12 @@
 //  NOTE that when braking, the motor supply rail voltage will be lifted.  Failure to design-in some type of 'surplus power dump'
 //  may result in over-voltage damage to batteries or power electronics.
 
-
 #include "mbed.h"
 #include "FastPWM.h"
 #include "TS_DISCO_F746NG.h"
 #include "LCD_DISCO_F746NG.h"
-#include "SD_DISCO_F746NG.h"
-#include "dro.h"
-
-
+//#include "SD_DISCO_F746NG.h"  //  SD card stuff now in separate file sd_card.cpp
+#include "Electric_Loco.h"
 
 //  Design Topology
 //  This F746NG is the single loco control computer.
@@ -57,7 +55,7 @@
 
 LCD_DISCO_F746NG    lcd;
 TS_DISCO_F746NG     touch_screen;
-SD_DISCO_F746NG     sd;
+//SD_DISCO_F746NG     sd;   //  SD card stuff now in sd_card.cpp
 
 FastPWM     maxv    (D12, 1), 
             maxi    (D11, 1); //  pin, prescaler value
@@ -70,7 +68,8 @@
 DigitalOut led_grn          (LED1); //  the only on board user led
 
 DigitalIn   f_r_switch      (D0);   //  Reads position of centre-off ignition switch
-DigitalIn   spareio_d8      (D8);
+//DigitalIn   spareio_d8      (D8);
+//DigitalOut  throttle_servo_pulse_out    (D8);   //  now defined in throttle.cpp
 DigitalIn   spareio_d9      (D9);
 DigitalIn   spareio_d10     (D10);  //  D8, D9, D10 wired to jumper on pcb - not used to Apr 2017
 
@@ -100,50 +99,25 @@
 extern  void    update_meters  (double, double, double)  ;
 extern  void    command_line_interpreter    ()  ;
 
-static  const   int NUMBER_OF_MOTORS    = 4,
-    SD_BLOCKSIZE        = 512,   /* SD card data Block Size in Bytes      */
+extern  int     throttle    (double, double)    ;   //  called from main every 31ms
+
+extern  void    update_SD_card  ()  ;   //  Hall pulse total updated once per sec and saved in blocks of 128 to SD card
+extern  bool    read_SD_state   ()  ;
+extern  bool    mainSDtest();
+
+static  const   int
     DAMPER_DECAY        = 42,   //  Small num -> fast 'viscous damper' on dead-mans function with finger removed from panel
-    MAF_PTS             = 140,      //  Moving Average Filter points
-    PWM_HZ              = 13000,
+    MAF_PTS             = 140,      //  Moving Average Filter points. Filters reduce noise on volatage and current readings
+    PWM_HZ              = 16000,    //  chosen to be above cutoff frequency of average human ear
 //    PWM_HZ            = 2000,   //  Used this to experiment on much bigger motor
     MAX_PWM_TICKS       = 108000000 / PWM_HZ,   //  108000000 for F746N, due to cpu clock = 216 MHz
     FWD                 = 0,
     REV                 = ~FWD;
 
-static const double
-    MOTOR_PINION_T  = 17.0, //  motor pinion teeth, wheel gear teeth and wheel dia required to calculate speed and distance.
-    WHEEL_GEAR_T    = 76.0,
-    WHEEL_DIA_MM    = 147.0,
-    WHEEL_CIRCUMFERENCE_METRE = PI * WHEEL_DIA_MM / 1000.0,
-    PULSES_PER_WHEEL_REV    = 32.0 * WHEEL_GEAR_T / MOTOR_PINION_T,
-    PULSES_PER_METRE        = PULSES_PER_WHEEL_REV / WHEEL_CIRCUMFERENCE_METRE,
-    rpm2mph         = 60.0                                      //  = Motor Revs per hour;
-                  * (MOTOR_PINION_T / WHEEL_GEAR_T)   //  = Wheel rev per hour
-                  * WHEEL_CIRCUMFERENCE_METRE         //  = metres per hour
-                  * 39.37                             //  = inches per hour
-                  / (1760 * 36)                       //  = miles per hour
-                  ;
-
-//  Assume SD card size is 4Gbyte, might be 8 Gbyte
-//  Then can use 8388608 blocks (8 * 1024 * 1024)
-
-uint64_t    SD_blockptr = 0;
-uint32_t    SDBuffer[(SD_BLOCKSIZE >> 2)];   //  = space for (512 / 4) uint32_t
-uint8_t     SD_state = SD_OK,   sd_jf = 0;
-
-static  const    uint64_t    GIGAB = 1024 * 1024 * 1024;
-//static  const    uint64_t    SDBLOCKS = (GIGAB / SD_BLOCKSIZE) * 4;    //  software drives SD up to 4Gbyte only - 8 M block
-static  const    uint64_t    SDBLOCKS = (GIGAB / SD_BLOCKSIZE) * 2;    //  software drives SD up to 4Gbyte only - 8 M block
-//  If data logger takes 2 minutes to fill 1 block, a 4G card takes 32 years run-time to fill
-//  If system generates approx 320 pulses per metre travelled, max distance recordable in uint32_t is 65536 * 65536 / 320 = 13421.772 km
-
-//from dro.h    struct  slide   {   int position;    int    oldpos; int state; int direction;   bool recalc_run;    bool handbrake_slipping;    double handbrake_effort;   double   loco_speed  }   ;
+//from .h    struct  slide   {   int position;    int    oldpos; int state; int direction;   bool recalc_run;    bool handbrake_slipping;    double handbrake_effort;   double   loco_speed  }   ;
 struct slide    slider  ;
 
 
-
-//static const double mph_2_mm_per_sec = 447.04;  //  exact
-
 int     V_maf[MAF_PTS + 2],    I_maf[MAF_PTS + 2],  maf_ptr = 0;
 //uint32_t Hall_pulse[8] = {0,0,0,0,0,0,0,0};  //  more than max number of motors
 uint32_t Hall_pulse[8] = {1,1,1,1,1,1,1,1};  //  more than max number of motors
@@ -155,163 +129,6 @@
 
 double  last_pwm = 0.0;
 
-bool    sd_error    ()  {   //  Test and Clear error code sd_jf, return true if any error bits set, false on 0
-    bool    retval = false;
-    if  (sd_jf != 0)    {
-        retval = true;
-        sd_jf = 0;
-    }
-    return  retval;
-}
-
-bool    check_SD_block_clear    (uint32_t block) {
-    uint32_t    b[(SD_BLOCKSIZE >> 2)];
-    SD_state    = sd.ReadBlocks(b, (uint64_t)(SD_BLOCKSIZE * block), SD_BLOCKSIZE, 1);
-    if(SD_state != SD_OK)   {
-        sd_jf = 1;
-        pc.printf   ("Failed, not SD_OK, erasing block %d\r\n", block);
-        return  false;
-    }
-    for (int i = 0; i < (SD_BLOCKSIZE >> 2); i++)
-        if  (b[i] != 0)
-            return  false;
-    return  true;
-}
-
-/*bool erase_block (uint32_t    block2erase)    {
-    uint64_t addr = SD_BLOCKSIZE * (uint64_t)block2erase;
-    SD_state = sd.Erase(addr, addr + SD_BLOCKSIZE);
-    if  (SD_state != SD_OK) {
-        sd_jf = 1;  //  Assert error flag
-        pc.printf   ("Failed, not SD_OK, erasing block %d\r\n", block2erase);
-        return  false;
-    }
-    return  check_SD_block_clear (block2erase);
-}*/
-
-bool    SD_find_next_clear_block    (uint64_t * blok)  {   //  Successive approximation algorithm to quickly find next vacant SD card 512 byte block
-    uint64_t toaddsub = SDBLOCKS / 2, stab = SDBLOCKS - 1;
-    pc.printf   ("At SD_find_next_clear_block \r\n");
-    while   (toaddsub)  {
-        pc.printf   ("stab = %lld, toadsub = %lld\r\n", stab, toaddsub);    //  lld for long long int
-        bool    clear_block = true;
-        SD_state    = sd.ReadBlocks(SDBuffer, SD_BLOCKSIZE * stab, SD_BLOCKSIZE, 1);
-        if(SD_state != SD_OK)   {
-            sd_jf = 1;
-            pc.printf   ("SD error in SD_find_next_clear_block, returning -1\r\n");
-            return false;
-        }
-        for (int i = 0; i < (SD_BLOCKSIZE >> 2); i++)   {
-            if  (SDBuffer[i] != 0) {
-                clear_block = false;
-                pc.printf   ("Buff at %d contains %x\r\n", i, SDBuffer[i]);
-                i = SD_BLOCKSIZE;  //  to exit loop
-            }
-        }
-        if  (clear_block)
-            stab -= toaddsub;
-        else
-            stab += toaddsub;
-        toaddsub >>= 1;
-    }
-    if  (!check_SD_block_clear(stab))
-        stab++;
-    if  (sd_error())    {   //  sd_error() tests and clears error bits
-        pc.printf   ("check_SD_block_clear(%ld)returned ERROR in SD_find_next_clear_block\r\n", stab);
-        sd_jf = 1;  //  reassert error flag
-        return  false;
-    }
-    pc.printf   ("Completed find_next, stab = %d\r\n", stab);
-    *blok = stab;   //  block number of next free block
-    return  true;
-}
-
-bool SD_card_erase_all   (void)  {   //  assumes sd card is 4 Gbyte, erases 4 Gbyte. Called from CLI
-    uint64_t    EndAddr  = GIGAB * 4, 
-                StartAddr = 0LL;
-    sd_jf = 0;
-    pc.printf   ("Erasing SD card ... ");
-    //  uint8_t Erase(uint64_t StartAddr, uint64_t EndAddr);
-    SD_state    = sd.Erase(StartAddr, EndAddr);
-    if  (SD_state != SD_OK) {
-        pc.printf   ("SD_card_erase_all FAILED\r\n");
-        sd_jf = 1;
-        return  false;
-    }
-    pc.printf   ("no error detected\r\n");
-    return  true;
-}
-
-
-bool mainSDtest()
-{
-    SD_state = sd.Init();
-    if(SD_state != SD_OK) {
-        pc.printf   ("sd.Init set SD_state to %0x\r\n", SD_state);
-        if(SD_state == MSD_ERROR_SD_NOT_PRESENT) {
-            pc.printf("SD shall be inserted before running test\r\n");
-        } else {
-            pc.printf("SD Initialization : FAIL.\r\n");
-        }
-        pc.printf("SD Test Aborted.\r\n");
-        return  false;
-    } 
-//    else {    //  SD_state is SD_OK
-    pc.printf("SD Initialization : OK.\r\n");
-
-
-
-//        SD_card_erase_all();
-//        if    (sd_error())
-//            pc.printf ("SD_card_erase_all() reports ERROR");
-
-
-
-    SD_find_next_clear_block(& SD_blockptr);
-    pc.printf   ("SD_find_next_clear_block returned %lld\r\n\n\n", SD_blockptr);
-    if  (sd_error())    {
-        pc.printf   ("***** ERROR returned from SD_find_next_clear_block ***** SD ops aborted\r\n");
-        return  false;
-    }   
-    pc.printf("SD_find_next_clear_block() returned %ld\r\n", SD_blockptr);
-    if  (SD_blockptr < 1)   {
-        pc.printf   ("Looks like card newly erased, SD_blockptr value of %d\r\n", SD_blockptr);
-        SD_blockptr = 0;
-        historic_distance = 0;
-    }
-    else    {
-        SD_state = sd.ReadBlocks(SDBuffer, SD_BLOCKSIZE * (SD_blockptr - 1), SD_BLOCKSIZE, 1);
-        if  (SD_state != SD_OK) {
-            pc.printf   ("Error reading last block from SD block %d\r\n", SD_blockptr - 1);
-            return  false;
-        }
-        for (int i = 0; i < (SD_BLOCKSIZE >> 2); i++)
-            pc.printf   ("%lx\t", SDBuffer[i]);
-        historic_distance = SDBuffer[(SD_BLOCKSIZE >> 2) - 1];
-        pc.printf   ("\r\nAbove, data read from last filled SD block %lld, using historic_distance = %lx\r\n", SD_blockptr - 1, historic_distance);
-    }
-    if  (SD_blockptr > 2)   {
-        for (int i = SD_blockptr - 2; i < SD_blockptr + 2; i++)    {
-            pc.printf   ("check_SD_block_clear (%d) ", i);
-            if  (check_SD_block_clear(i))
-                pc.printf   ("block %ld is CLEAR\r\n", i);
-            else
-                pc.printf   ("block %ld is NOT clear\r\n", i);
-            if  (sd_error())    {
-                pc.printf   ("ERROR from check_SD_block_clear ()\r\n");
-            }
-        }
-    }
-    return  true;
-}
-
-
-
-
-
-
-
-
 class   speed_measurement       //  Interrupts at qtr sec cause read of Hall_pulse counters which are incremented by transitions of Hall inputs
 {
     static const int    SPEED_AVE_PTS   = 9;    //  AVE_PTS - points in moving average filters
@@ -393,6 +210,10 @@
     return  historic_distance + Hall_pulse[0] + Hall_pulse[1] + Hall_pulse[2] + Hall_pulse[3];
 }
 
+uint32_t    get_pulse_total ()  {   //  called by SD card code
+    return  speed.pulse_total();
+}
+
 void    set_V_limit (double p)  //  Sets max motor voltage
 {
     if  (p < 0.0)
@@ -458,22 +279,33 @@
 {
     Hall_pulse[3]++;
 }
-/*void    ISR_mot5_hall_handler  ()
+void    ISR_mot5_hall_handler  ()   //  If only 4 motors this never gets used, there is no fifth motor
 {
     Hall_pulse[4]++;
 }
-void    ISR_mot6_hall_handler  ()
+void    ISR_mot6_hall_handler  ()   //  As one above
 {
     Hall_pulse[5]++;
 }
-*/
+
 
 void    ISR_current_reader  (void)      //  FIXED at 250us
 {
+    static  int ms32 = 0, ms250 = 0;
     trigger_current_read    = true; //  every 250us, i.e. 4kHz  NOTE only sets trigger here, readings taken in main loop
+    ms32++;
+    if  (ms32 > 124)    {
+        ms32 = 0;
+        trigger_32ms = true;
+        ms250++;
+        if  (ms250 > 7) {
+            ms250 = 0;
+            qtrsec_trig = true;
+        }
+    }
 }
 
-void    ISR_tick_32ms   (void)      //
+/*void    ISR_tick_32ms   (void)      //
 {
     trigger_32ms = true;
 }
@@ -481,7 +313,7 @@
 {
     qtrsec_trig = true;
 }
-
+*/
 //  End of Interrupt Service Routines
 
 
@@ -521,7 +353,7 @@
     1   1       1    Regen Braking
 */
 void    set_run_mode    (int    mode)
-{
+{   //  NOTE Nov 2017 - Handbrake not implemented
     if  (mode == HANDBRAKE_SLIPPING)    slider.handbrake_slipping = true;
     else                                slider.handbrake_slipping = false;
     switch  (mode) {
@@ -560,36 +392,21 @@
     }
 }
 
-void    update_SD_card  ()  {   //  Hall pulse total updated once per sec and saved in blocks of 128 to SD card
-    static int index = 0;
-    static uint32_t    buff[(SD_BLOCKSIZE >> 2) + 2];
-    buff[index++] = speed.pulse_total();        //  pulse_total for all time, add this to buffer to write to SD
-    if  (index >= (SD_BLOCKSIZE >> 2)) {
-        pc.printf   ("Writing new SD block %d ... ", SD_blockptr);
-        SD_state = sd.WriteBlocks(buff, SD_BLOCKSIZE * SD_blockptr, SD_BLOCKSIZE, 1);
-        SD_blockptr++;
-        if  (SD_state == SD_OK)
-            pc.printf   ("OK, distance %d\r\n", buff[index - 1] / (int)PULSES_PER_METRE);
-        else
-            pc.printf   ("ERROR\r\n");
-        index = 0;
-    }
-}
-
 int main()
 {
     int c_5 = 0, seconds = 0, minutes = 0;
+    double  electrical_power_Watt = 0.0;
     ky_bd   kybd_a, kybd_b;
     memset  (&kybd_a, 0, sizeof(kybd_a));
     memset  (&kybd_b, 0, sizeof(kybd_b));
 
-    spareio_d8.mode (PullUp);
+//    spareio_d8.mode (PullUp); now output driving throttle servo
     spareio_d9.mode (PullUp);
     spareio_d10.mode(PullUp);
 
     Ticker  tick250us;
-    Ticker  tick32ms;
-    Ticker  tick250ms;
+//    Ticker  tick32ms;
+//    Ticker  tick250ms;
 
 //  Setup User Interrupt Vectors
     mot1hall.fall       (&ISR_mot1_hall_handler);
@@ -601,9 +418,10 @@
     mot4hall.fall       (&ISR_mot4_hall_handler);
     mot4hall.rise       (&ISR_mot4_hall_handler);
 
-    tick250us.attach_us (&ISR_current_reader, 250);    //  set to longer time to test
-    tick32ms.attach_us  (&ISR_tick_32ms, 32001);
-    tick250ms.attach_us (&ISR_tick_250ms, 250002);
+    tick250us.attach_us (&ISR_current_reader, 250);    //  count 125 of these to trig 31.25ms
+//    tick32ms.attach_us  (&ISR_tick_32ms, 32001);
+//    tick32ms.attach_us  (&ISR_tick_32ms, 31250);    //  then count 8 pulses per 250ms
+//    tick250ms.attach_us (&ISR_tick_250ms, 250002);
     pc.baud (9600);
     GfetT1 = 0;
     GfetT2 = 0;     //  two output bits for future use driving horns
@@ -650,6 +468,7 @@
 
     mainSDtest();
 
+    double  torque_req = 0.0;
     bool    toggle32ms = false;
     //  Main loop
     while(1) {      //
@@ -660,6 +479,10 @@
 
         if  (trigger_32ms == true)  {       //  Stuff to do every 32 milli secs
             trigger_32ms = false;
+
+//  CALL THROTTLE HERE  - why here ? Ah yes, this initiates servo pulse. Need steady stream of servo pulses even when nothing changes.
+            throttle    (torque_req, 2.3);
+
             toggle32ms = !toggle32ms;
             if  (toggle32ms)    {
                 present_kybd = &kybd_a;
@@ -690,7 +513,7 @@
 
                     else    {           //  nice slow non-jerky glidey movement required
                         dbl = (double)(k - slider.position);
-                        dbl /= 13.179;
+                        dbl /= 13.179;  //  Where did 13.179 come from ?
                         if  (dbl < 0.0)
                             dbl -= 1.0;
                         if  (dbl > 0.0)
@@ -767,7 +590,7 @@
             if  (slider.recalc_run) {   //  range of slider.position in RUN mode is min_pos_() to NEUTRAL_VAL - 1
                 slider.recalc_run = false;  //  All RUN power and pwm calcs done here
                 int b = slider.position;
-                double  torque_req;
+//                double  torque_req;   //  now declared above to be used as parameter for throttle
                 if  (b > NEUTRAL_VAL)
                     b = NEUTRAL_VAL;
                 if  (b < MIN_POS)   //  if finger position is above top of slider limit
@@ -793,7 +616,8 @@
 //static const double mph_2_mm_per_sec = 447.04;  //  exact
 //            double  mm_travelled_in_qtrsec = speedmph * mph_2_mm_per_sec / 4.0;
             slider.loco_speed = speedmph;
-            update_meters  (speedmph, amps, volts)  ;
+            electrical_power_Watt = volts * amps;   //  visible throughout main
+            update_meters  (speedmph, electrical_power_Watt, volts)  ;   //  displays speed, volts and power (volts times amps)
 //            update_meters  (7.5, amps, volts)  ;
             led_grn = !led_grn;
             if  (slider.state == PARK)   {
@@ -820,7 +644,8 @@
                     minutes++;
                     //  do once per minute stuff here
                 }   //  fall back into once per second
-                if(SD_state == SD_OK) {
+//                if(SD_state == SD_OK) {
+                if(read_SD_state() == true) {
                     uint32_t distance = speed.metres_travelled();
                     char    dist[20];
                     sprintf (dist, "%05d m", distance);
diff -r 23cc72b18e74 -r 8ef34deb5177 sd_card.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sd_card.cpp	Mon Nov 13 09:53:00 2017 +0000
@@ -0,0 +1,204 @@
+#include "mbed.h"
+#include "Electric_Loco.h"
+#include "SD_DISCO_F746NG.h"
+/*
+SD card used only to keep log of total distance travelled.
+Odometer is trivial.
+This file treats SD card as random access memory.
+A better implementation would use library functions for FAT file system etc.
+
+May revisit this.
+
+*/
+SD_DISCO_F746NG     sd;
+extern  Serial pc;
+extern  uint32_t    historic_distance;
+extern  uint32_t    get_pulse_total ()  ;
+
+static  const   int
+    SD_BLOCKSIZE        = 512;   /* SD card data Block Size in Bytes      */
+//  Assume SD card size is 4Gbyte, might be 8 Gbyte
+//  Then can use 8388608 blocks (8 * 1024 * 1024)
+
+uint64_t    SD_blockptr = 0;
+uint32_t    SDBuffer[(SD_BLOCKSIZE >> 2)];   //  = space for (512 / 4) uint32_t
+uint8_t     SD_state = SD_OK,   sd_jf = 0;
+
+static  const    uint64_t    GIGAB = 1024 * 1024 * 1024;
+//static  const    uint64_t    SDBLOCKS = (GIGAB / SD_BLOCKSIZE) * 4;    //  software drives SD up to 4Gbyte only - 8 M block
+static  const    uint64_t    SDBLOCKS = (GIGAB / SD_BLOCKSIZE) * 2;    //  software drives SD up to 4Gbyte only - 8 M block
+//  If data logger takes 2 minutes to fill 1 block, a 4G card takes 32 years run-time to fill
+//  If system generates approx 320 pulses per metre travelled, max distance recordable in uint32_t is 65536 * 65536 / 320 = 13421.772 km
+bool    sd_error    ()  {   //  Test and Clear error code sd_jf, return true if any error bits set, false on 0
+    bool    retval = false;
+    if  (sd_jf != 0)    {
+        retval = true;
+        sd_jf = 0;
+    }
+    return  retval;
+}
+
+bool    check_SD_block_clear    (uint32_t block) {
+    uint32_t    b[(SD_BLOCKSIZE >> 2)];
+    SD_state    = sd.ReadBlocks(b, (uint64_t)(SD_BLOCKSIZE * block), SD_BLOCKSIZE, 1);
+    if(SD_state != SD_OK)   {
+        sd_jf = 1;
+        pc.printf   ("Failed, not SD_OK, erasing block %d\r\n", block);
+        return  false;
+    }
+    for (int i = 0; i < (SD_BLOCKSIZE >> 2); i++)
+        if  (b[i] != 0)
+            return  false;
+    return  true;
+}
+
+bool    read_SD_state   ()  {
+    if  (SD_state == SD_OK)
+        return  true;
+    return  false;
+}
+/*bool erase_block (uint32_t    block2erase)    {
+    uint64_t addr = SD_BLOCKSIZE * (uint64_t)block2erase;
+    SD_state = sd.Erase(addr, addr + SD_BLOCKSIZE);
+    if  (SD_state != SD_OK) {
+        sd_jf = 1;  //  Assert error flag
+        pc.printf   ("Failed, not SD_OK, erasing block %d\r\n", block2erase);
+        return  false;
+    }
+    return  check_SD_block_clear (block2erase);
+}*/
+
+bool    SD_find_next_clear_block    (uint64_t * blok)  {   //  Successive approximation algorithm to quickly find next vacant SD card 512 byte block
+    uint64_t toaddsub = SDBLOCKS / 2, stab = SDBLOCKS - 1;
+    pc.printf   ("At SD_find_next_clear_block \r\n");
+    while   (toaddsub)  {
+        pc.printf   ("stab = %lld, toadsub = %lld\r\n", stab, toaddsub);    //  lld for long long int
+        bool    clear_block = true;
+        SD_state    = sd.ReadBlocks(SDBuffer, SD_BLOCKSIZE * stab, SD_BLOCKSIZE, 1);
+        if(SD_state != SD_OK)   {
+            sd_jf = 1;
+            pc.printf   ("SD error in SD_find_next_clear_block, returning -1\r\n");
+            return false;
+        }
+        for (int i = 0; i < (SD_BLOCKSIZE >> 2); i++)   {
+            if  (SDBuffer[i] != 0) {
+                clear_block = false;
+                pc.printf   ("Buff at %d contains %x\r\n", i, SDBuffer[i]);
+                i = SD_BLOCKSIZE;  //  to exit loop
+            }
+        }
+        if  (clear_block)
+            stab -= toaddsub;
+        else
+            stab += toaddsub;
+        toaddsub >>= 1;
+    }
+    if  (!check_SD_block_clear(stab))
+        stab++;
+    if  (sd_error())    {   //  sd_error() tests and clears error bits
+        pc.printf   ("check_SD_block_clear(%ld)returned ERROR in SD_find_next_clear_block\r\n", stab);
+        sd_jf = 1;  //  reassert error flag
+        return  false;
+    }
+    pc.printf   ("Completed find_next, stab = %d\r\n", stab);
+    *blok = stab;   //  block number of next free block
+    return  true;
+}
+
+bool SD_card_erase_all   (void)  {   //  assumes sd card is 4 Gbyte, erases 4 Gbyte. Called from CLI
+    uint64_t    EndAddr  = GIGAB * 4, 
+                StartAddr = 0LL;
+    sd_jf = 0;
+    pc.printf   ("Erasing SD card ... ");
+    //  uint8_t Erase(uint64_t StartAddr, uint64_t EndAddr);
+    SD_state    = sd.Erase(StartAddr, EndAddr);
+    if  (SD_state != SD_OK) {
+        pc.printf   ("SD_card_erase_all FAILED\r\n");
+        sd_jf = 1;
+        return  false;
+    }
+    pc.printf   ("no error detected\r\n");
+    return  true;
+}
+
+
+bool mainSDtest()
+{
+    SD_state = sd.Init();
+    if(SD_state != SD_OK) {
+        pc.printf   ("sd.Init set SD_state to %0x\r\n", SD_state);
+        if(SD_state == MSD_ERROR_SD_NOT_PRESENT) {
+            pc.printf("SD shall be inserted before running test\r\n");
+        } else {
+            pc.printf("SD Initialization : FAIL.\r\n");
+        }
+        pc.printf("SD Test Aborted.\r\n");
+        return  false;
+    } 
+//    else {    //  SD_state is SD_OK
+    pc.printf("SD Initialization : OK.\r\n");
+
+
+
+//        SD_card_erase_all();
+//        if    (sd_error())
+//            pc.printf ("SD_card_erase_all() reports ERROR");
+
+
+
+    SD_find_next_clear_block(& SD_blockptr);
+    pc.printf   ("SD_find_next_clear_block returned %lld\r\n\n\n", SD_blockptr);
+    if  (sd_error())    {
+        pc.printf   ("***** ERROR returned from SD_find_next_clear_block ***** SD ops aborted\r\n");
+        return  false;
+    }   
+    pc.printf("SD_find_next_clear_block() returned %ld\r\n", SD_blockptr);
+    if  (SD_blockptr < 1)   {
+        pc.printf   ("Looks like card newly erased, SD_blockptr value of %d\r\n", SD_blockptr);
+        SD_blockptr = 0;
+        historic_distance = 0;
+    }
+    else    {
+        SD_state = sd.ReadBlocks(SDBuffer, SD_BLOCKSIZE * (SD_blockptr - 1), SD_BLOCKSIZE, 1);
+        if  (SD_state != SD_OK) {
+            pc.printf   ("Error reading last block from SD block %d\r\n", SD_blockptr - 1);
+            return  false;
+        }
+        for (int i = 0; i < (SD_BLOCKSIZE >> 2); i++)
+            pc.printf   ("%lx\t", SDBuffer[i]);
+        historic_distance = SDBuffer[(SD_BLOCKSIZE >> 2) - 1];
+        pc.printf   ("\r\nAbove, data read from last filled SD block %lld, using historic_distance = %lx\r\n", SD_blockptr - 1, historic_distance);
+    }
+    if  (SD_blockptr > 2)   {
+        for (int i = SD_blockptr - 2; i < SD_blockptr + 2; i++)    {
+            pc.printf   ("check_SD_block_clear (%d) ", i);
+            if  (check_SD_block_clear(i))
+                pc.printf   ("block %ld is CLEAR\r\n", i);
+            else
+                pc.printf   ("block %ld is NOT clear\r\n", i);
+            if  (sd_error())    {
+                pc.printf   ("ERROR from check_SD_block_clear ()\r\n");
+            }
+        }
+    }
+    return  true;
+}
+
+void    update_SD_card  ()  {   //  Hall pulse total updated once per sec and saved in blocks of 128 to SD card
+    static int index = 0;
+    static uint32_t    buff[(SD_BLOCKSIZE >> 2) + 2];
+//    buff[index++] = speed.pulse_total();        //  pulse_total for all time, add this to buffer to write to SD
+    buff[index++] = get_pulse_total();        //  pulse_total for all time, add this to buffer to write to SD
+    if  (index >= (SD_BLOCKSIZE >> 2)) {
+        pc.printf   ("Writing new SD block %d ... ", SD_blockptr);
+        SD_state = sd.WriteBlocks(buff, SD_BLOCKSIZE * SD_blockptr, SD_BLOCKSIZE, 1);
+        SD_blockptr++;
+        if  (SD_state == SD_OK)
+            pc.printf   ("OK, distance %d\r\n", buff[index - 1] / (int)PULSES_PER_METRE);
+        else
+            pc.printf   ("ERROR\r\n");
+        index = 0;
+    }
+}
+
+
diff -r 23cc72b18e74 -r 8ef34deb5177 throttle.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/throttle.cpp	Mon Nov 13 09:53:00 2017 +0000
@@ -0,0 +1,56 @@
+/*
+This file about using model control servo to drive the throttle of Honda GX120 engine
+
+Using a model control servo to set the throttle of Honda GX120 petrol engine
+First thoughts  12 Nov 2017.
+
+Servo driven by positive pulse of duration 1000 to 2000 micro sec. Repetition rate 50Hz
+Will first try repeating in 32ms loop giving repetirion rate approx 30 Hz.
+Pos pulse generated by setting to 1 in 32ms handler and starting a 'timeout'
+
+    ... as part of 32ms code
+    if  (torque_demand < 0.01)  {
+        throttle    (TICKOVER);
+    }
+    else    {   //  got positive torque demand
+        throttle    (MID_REVS);
+    }
+    throttle_servo_pulse_out = 1;
+    throttle_servo_pulse_width.attach_us    (&servo_pulse_lo, global_throttle_us);
+*/
+#include "mbed.h"
+#include "Electric_Loco.h"
+
+static const   int
+    SERVO_RANGE     = 1000, //  micro sec difference between max and min
+    SERVO_OFFSET    = 1000, //  min servo pulse width micro sec
+    TICKOVER        = SERVO_OFFSET + 0,
+    MID_REVS        = SERVO_OFFSET + 500,
+    RANGE_MULTIPLIER    = SERVO_RANGE + SERVO_OFFSET - MID_REVS;
+
+DigitalOut  throttle_servo_pulse_out    (D8);   //  now defined in throttle.cpp
+
+Timeout throttle_servo_pulse_width;
+
+void    throttle_servo_pulse_lo  ()  {   //  Interrupt handler called at end of 'Timeout'
+    throttle_servo_pulse_out = 0;   //  end servo positive pulse
+}
+
+int throttle    (double torque_demand, double speed_mph)    {   //  called from main every 31ms
+    int duration_us;
+    if  (throttle_servo_pulse_out != 0)
+        return  -1; //  pulse positive already
+    if  (torque_demand < 0.01)  {
+        duration_us = TICKOVER;
+    }
+    else    {   //  got positive torque demand
+        double  tmpd = torque_demand * RANGE_MULTIPLIER;
+        duration_us = MID_REVS + (int) tmpd;
+    }
+    throttle_servo_pulse_out = 1;   //  start servo positive pulse
+    throttle_servo_pulse_width.attach_us    (&throttle_servo_pulse_lo, duration_us);
+    return  0;
+}
+//  endof New Nov 2017    Controlling throttle of Honda engine
+
+