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
Diff: main.cpp
- 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)