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)