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:
13:ef7a06fa11de
Parent:
12:d1d21a2941ef
Child:
15:2591e2008323
--- a/main.cpp	Mon Mar 04 17:51:08 2019 +0000
+++ b/main.cpp	Sun Sep 29 16:34:37 2019 +0000
@@ -1,29 +1,34 @@
-//  Cloned from 'DualBLS2018_06' on 23 November 2018
+/*
+    STM3_ESC    Electronic Speed Controller board, drives Two Brushless Motors, full Four Quadrant Control.
+    Jon Freeman  B. Eng Hons
+    2015 - 2019
+*/
 #include "mbed.h"
-//#include    "users/mbed_official/code/mbed-dev/file/707f6e361f3e/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F401xE/device/stm32f401xe.h"
-#include "DualBLS.h"
-
-#include    "stm32f401xe.h"
+#include "STM3_ESC.h"
 #include "BufferedSerial.h"
 #include "FastPWM.h"
 #include "Servo.h"
 #include "brushless_motor.h"
 #include "Radio_Control_In.h"
+//#ifdef  TARGET_NUCLEO_F401RE    //
 
+//#endif
 /*
-Brushless_STM3 board
+Brushless_STM3_ESC board
 Jan 2019    *   WatchDog implemented. Default is disabled, 'kd' command from TS controller enables and reloads
             *   Tidied brushless_motor class, parameter passing now done properly
             *   class   RControl_In created. Inputs now routed to new pins, can now use two chans both class   RControl_In and Servo out
-                (buggery board required for new inputs)
+                (buggery board required for new inputs on June 2018 issue boards)
             *   Added version string
             *   Error handler written and included
             *   Realised Nanotec motors are 6 pole, all others are 8 pole. Modified 'mode' to include 'motor poles' in EEPROM data, now speed reading correct for all
             *   Reorganised EEPROM data - mode setting now much easier, less error prone
-            *   Maximum speed now one EEPROM option, range 1.0 to 25.0 MPH
+            *   Maximum speed now one EEPROM option, range 1.0 to 25.0 MPH in 0.1 MPH steps
                 
 New 29th May 2018 **
                 LMT01 temperature sensor routed to T1 - and rerouted to PC_13 as InterruptIn on T1 (ports A and B I think) not workable
+        March 2019 temp sensor only included with TEMP_SENSOR_ENABLE defined. Temp reading not essential, LMT01 was not a good choice due to
+        significant loading of interrupts, threatening integrity of Real Time System
 */
 
 
@@ -34,7 +39,7 @@
 
 ____________________________________________________________________________________
         CONTROL PHILOSOPHY
-This Bogie drive board software should ensure sensible control when commands supplied are not sensible !
+This STM3_ESC Bogie drive board software should ensure sensible control when commands supplied are not sensible !
 
 That is, smooth transition between Drive, Coast and Brake to be implemented here.
 The remote controller must not be relied upon to deliver sensible command sequences.
@@ -49,17 +54,21 @@
 #if defined (TARGET_NUCLEO_F401RE)  //  CPU in 64 pin LQFP
 #include    "F401RE.h"  //  See here for warnings about Servo InterruptIn not working
 #endif
+#if defined (TARGET_NUCLEO_F411RE)  //  CPU in 64 pin LQFP
+#include    "F411RE.h"  //  See here for warnings about Servo InterruptIn not working
+#endif
 #if defined (TARGET_NUCLEO_F446ZE)  //  CPU in 144 pin LQFP
 #include    "F446ZE.h"              //  A thought for future version
 #endif
 /*  Global variable declarations */
-char   const    const_version_string[] = {"1.0.0\0"};  //  Version string, readable from serial ports
+char   const_version_string[] = {"1.0.y2019.m09.d29\0"};  //  Version string, readable from serial ports
 volatile    uint32_t    fast_sys_timer      = 0;    //  gets incremented by our Ticker ISR every VOLTAGE_READ_INTERVAL_US
-int         WatchDog    = WATCHDOG_RELOAD + 80; //  Allow extra few seconds at powerup
+//int         WatchDog    = WATCHDOG_RELOAD + 80; //  Allow extra few seconds at powerup
+int         WatchDog    = 0; //  Set this up in main once pre-flight checks done. Allow extra few seconds at powerup
 bool        WatchDogEnable  = false;    //  Must recieve explicit instruction from pc or controller to enable
 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
+            sys_timer           = 0,    //  gets incremented by our Ticker ISR every MAIN_LOOP_REPEAT_TIME_US, 31250us at Sept 2019
             AtoD_Semaphore      = 0;
 
 bool        loop_flag   = false;        //  made true in ISR_loop_timer, picked up and made false again in main programme loop
@@ -67,15 +76,24 @@
 bool        temp_sensor_exists = false;
 double      rpm2mph;
 
+double      Current_Scaler_Sep_2019 = 1.0;  //  New idea - Sept 2019. Plan is to scale down motor current limit when voltage lower than nom.
+                                            //  See schematic for full details, but cycle-by-cycle current limit has the effect of allowing larger average I
+                                            //  at lower voltages. This is simply because current takes longer to build in motor inductance when voltage
+                                            //  is low. Conversely, at high supply voltages, motor current reaches limit quickly, cutting drive, meaning
+                                            //  similar current flows for shorter times at higher voltages.
+
+#ifdef  TEMP_SENSOR_ENABLE
 uint32_t    temp_sensor_count = 0,  //  incremented by every rising edge from LMT01
             last_temperature_count = 0;  //  global updated approx every 100ms after each LMT01 conversion completes
+#endif
 /*  End of Global variable declarations */
 
 Ticker  tick_vread;     //  Device to cause periodic interrupts, used to time voltage readings etc
 Ticker  loop_timer;     //  Device to cause periodic interrupts, used to sync iterations of main programme loop
+#ifdef  TEMP_SENSOR_ENABLE
 Ticker  temperature_find_ticker;
 Timer   temperature_timer;
-
+#endif
 #ifdef USING_DC_MOTORS
 Timer   dc_motor_kicker_timer;
 Timeout motors_restore;
@@ -127,6 +145,7 @@
 extern  cli_2019    pcc, tsc;   //  command line interpreters from pc and touch screen
 
 //  Interrupt Service Routines
+#ifdef  TEMP_SENSOR_ENABLE
 void    ISR_temperature_find_ticker ()      //  every 960 us, i.e. slightly faster than once per milli sec
 {
     static  bool    readflag = false;
@@ -139,7 +158,7 @@
     if  (t == 6)
         readflag = false;
 }
-
+#endif
 /** 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.
@@ -164,6 +183,7 @@
     fast_sys_timer++;        //  Just a handy measure of elapsed time for anything to use
 }
 
+#ifdef  TEMP_SENSOR_ENABLE
 void    temp_sensor_isr ()      //  got rising edge from LMT01. ALMOST CERTAIN this misses some
 {
     int t = temperature_timer.read_us   (); //  Must be being overrun by something, most likely culprit A-D reading ?
@@ -173,7 +193,7 @@
         temp_sensor_count++;
 //    T2 = !T2;   //  scope hanger
 }
-
+#endif
 //  End of Interrupt Service Routines
 
 
@@ -281,6 +301,24 @@
 }
 
 
+void    Update_Current_Scaler   ()  {       //  ***NEW Sept 2019*** Called at 8Hz
+const   double  Vnom = 48.0,
+                Vmin = Vnom / 3.0,
+                Voff = Vnom / 4.0;
+                
+    double  v = Read_BatteryVolts   ();
+    if  (v > Vnom)
+        v = Vnom;
+    if  (v < Voff)
+        v = Voff;
+    if  (v > Vmin)  {   //  In expected normal operating voltage range
+        Current_Scaler_Sep_2019 = v / Vnom;         //  May need to revisit law
+    }
+    else    {   //  In very low voltage region
+        Current_Scaler_Sep_2019 = 0.5 * (v / Vnom);
+    }
+}
+
 void    mode_set_both_motors   (int mode, double val)      //  called from cli to set fw, re, rb, hb
 {
     MotorA.set_mode (mode);
@@ -356,7 +394,7 @@
     const   int buflen = sizeof(dirbuf) / sizeof(int);
     const   double      Pot_Brake_Range = 0.35;  //pow   (0.5, SERVOIN_PWR_BENDER); //0.353553 for SERVOIN_PWR_BENDER = 1.5;
 
-    direction_old = direction_new;
+   direction_old = direction_new;
 
 //      Test for change in direction switch setting.
 //      If whole buffer NEWLY filled with all Fwd or all Rev, state = brake_wait
@@ -460,16 +498,16 @@
 int main()
 {
     int eighth_sec_count = 0;
-    double  servo_angle = 0.0;  //  For testing servo outs
-
-    Temperature_pin.fall (&temp_sensor_isr);
-    Temperature_pin.mode (PullUp);
     //  Setup system timers to cause periodic interrupts to synchronise and automate volt and current readings, loop repeat rate etc
     tick_vread.attach_us    (&ISR_voltage_reader, VOLTAGE_READ_INTERVAL_US);    //  Start periodic interrupt generator
     loop_timer.attach_us    (&ISR_loop_timer, MAIN_LOOP_REPEAT_TIME_US);    //  Start periodic interrupt generator
+#ifdef  TEMP_SENSOR_ENABLE
     temperature_find_ticker.attach_us   (&ISR_temperature_find_ticker, 960);
+    Temperature_pin.fall (&temp_sensor_isr);
+    Temperature_pin.mode (PullUp);
+    temperature_timer.start ();
+#endif
     //  Done setting up system interrupt timers
-    temperature_timer.start ();
 
     pc.baud     (9600);     //  COM port to pc
     com3.baud   (1200);     //  Once had an idea to use this for IR comms, never tried
@@ -495,8 +533,10 @@
 //    T5 = 0; now input from fw/re on remote control box
     T6 = 0;
 
+#ifdef  TEMP_SENSOR_ENABLE
     if  ((last_temperature_count > 160) && (last_temperature_count < 2400))   //  in range -40 to +100 degree C
         temp_sensor_exists  = true;
+#endif
 #ifdef  USING_DC_MOTORS
     dc_motor_kicker_timer.start   ();
 #endif
@@ -514,6 +554,10 @@
 //    pc.printf   ("SystemCoreClock=%d, MAX_PWM_TICKS=%d\r\n", SystemCoreClock, MAX_PWM_TICKS);
 //    pcc.test    ()  ;
 //    tsc.test    ()  ;
+
+    WatchDog    = WATCHDOG_RELOAD + 80; //  Allow extra few seconds at powerup
+
+
     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
             pcc.core    ()  ;   //  Proceed beyond here once loop_timer ticker ISR has set loop_flag true
@@ -522,8 +566,8 @@
         }                       //  32Hz original setting for loop repeat rate
         loop_flag = false;              //  Clear flag set by ticker interrupt handler. WHEN LAST CHECKED this was about every 32ms
 
-        RC_chan_1.validate_rx();
-        RC_chan_2.validate_rx();
+        RC_chan_1.validate_rx();   //  Tests for pulse width and repetition rates being believable signal from radio control
+        RC_chan_2.validate_rx();    //  bool return values not noted here, these 2 lines are pointless.
 
         switch  (mode.rd(COMM_SRC))  {   //  Look to selected source of driving command, act on commands from wherever
             case    0:  //  Invalid
@@ -574,14 +618,14 @@
             LED = !LED;                   // Toggle LED on board, should be seen to fast flash
             if  (WatchDogEnable)    {
                 WatchDog--;
-                if  (WatchDog == 0) {   //  Deal with WatchDog timer timeout here
+                if  (WatchDog < 1) {   //  Deal with WatchDog timer timeout here
+                    WatchDog = 0;
                     setVI  (0.0, 0.0);  //  set motor volts and amps to zero
 //                    com2.printf ("TIMEOUT %c\r\n", mode.rd(BOARD_ID));   //  Potential problem of multiple units reporting at same time overcome by adding board number to WATCHDOG_RELOAD
                     pc.printf ("TIMEOUT %c\r\n", mode.rd(BOARD_ID));   //  Brute touch screen controller can do nothing with this
                 }                       //  End of dealing with WatchDog timer timeout
-                if  (WatchDog < 0)
-                    WatchDog = 0;
             }
+            Update_Current_Scaler   ();
             eighth_sec_count++;
             if  (eighth_sec_count > 6)    {   //  Send some status info out of serial port every second and a bit or thereabouts
                 eighth_sec_count = 0;
@@ -593,8 +637,9 @@
                                     pc.printf   ("Temp %.2f\r\n", tmprt);
                                 }*/
             }
-#define SERVO_OUT_TEST
+//#define SERVO_OUT_TEST
 #ifdef  SERVO_OUT_TEST
+    static double  servo_angle = 0.0;  //  For testing servo outs
             //  servo out test here     December 2018
             servo_angle += 0.01;
             if  (servo_angle > TWOPI)