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

Revision:
1:8ef34deb5177
Parent:
0:23cc72b18e74
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);