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

Committer:
JonFreeman
Date:
Sun Aug 16 14:13:19 2020 +0000
Revision:
17:cc9b854295d6
Parent:
16:d1e4b9ad3b8b
August 2020. Checked Radio Control input ops.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
JonFreeman 13:ef7a06fa11de 1 /*
JonFreeman 13:ef7a06fa11de 2 STM3_ESC Electronic Speed Controller board, drives Two Brushless Motors, full Four Quadrant Control.
JonFreeman 13:ef7a06fa11de 3 Jon Freeman B. Eng Hons
JonFreeman 16:d1e4b9ad3b8b 4 2015 - 2020
JonFreeman 13:ef7a06fa11de 5 */
JonFreeman 0:435bf84ce48a 6 #include "mbed.h"
JonFreeman 13:ef7a06fa11de 7 #include "STM3_ESC.h"
JonFreeman 0:435bf84ce48a 8 #include "BufferedSerial.h"
JonFreeman 0:435bf84ce48a 9 #include "FastPWM.h"
JonFreeman 4:21d91465e4b1 10 #include "Servo.h"
JonFreeman 10:e40d8724268a 11 #include "brushless_motor.h"
JonFreeman 11:bfb73f083009 12 #include "Radio_Control_In.h"
JonFreeman 16:d1e4b9ad3b8b 13 #include "LM75B.h" // New I2C temp sensor code March 2020 (to suit possible next board issue, harmless otherwise)
JonFreeman 16:d1e4b9ad3b8b 14 //#ifdef TARGET_NUCLEO_F401RE // Target is TARGET_NUCLEO_F401RE for all boards produced.
JonFreeman 13:ef7a06fa11de 15 //#endif
JonFreeman 5:ca86a7848d54 16 /*
JonFreeman 13:ef7a06fa11de 17 Brushless_STM3_ESC board
JonFreeman 16:d1e4b9ad3b8b 18 Apr 2020 * RC tested on 'Idle Halt' branch line - all good - also tested Inverter Gen power sorce. All good.
JonFreeman 16:d1e4b9ad3b8b 19 Dec 2019 * Radio control inputs now fully implemented, requires fitting tiny 'RC_in_fix' board.
JonFreeman 12:d1d21a2941ef 20 Jan 2019 * WatchDog implemented. Default is disabled, 'kd' command from TS controller enables and reloads
JonFreeman 12:d1d21a2941ef 21 * Tidied brushless_motor class, parameter passing now done properly
JonFreeman 12:d1d21a2941ef 22 * class RControl_In created. Inputs now routed to new pins, can now use two chans both class RControl_In and Servo out
JonFreeman 13:ef7a06fa11de 23 (buggery board required for new inputs on June 2018 issue boards)
JonFreeman 12:d1d21a2941ef 24 * Added version string
JonFreeman 12:d1d21a2941ef 25 * Error handler written and included
JonFreeman 12:d1d21a2941ef 26 * 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
JonFreeman 12:d1d21a2941ef 27 * Reorganised EEPROM data - mode setting now much easier, less error prone
JonFreeman 13:ef7a06fa11de 28 * Maximum speed now one EEPROM option, range 1.0 to 25.0 MPH in 0.1 MPH steps
JonFreeman 12:d1d21a2941ef 29
JonFreeman 10:e40d8724268a 30 New 29th May 2018 **
JonFreeman 10:e40d8724268a 31 LMT01 temperature sensor routed to T1 - and rerouted to PC_13 as InterruptIn on T1 (ports A and B I think) not workable
JonFreeman 13:ef7a06fa11de 32 March 2019 temp sensor only included with TEMP_SENSOR_ENABLE defined. Temp reading not essential, LMT01 was not a good choice due to
JonFreeman 13:ef7a06fa11de 33 significant loading of interrupts, threatening integrity of Real Time System
JonFreeman 16:d1e4b9ad3b8b 34 *** New sensor code for LM75B temp sensor added March 2020 ***
JonFreeman 5:ca86a7848d54 35 */
JonFreeman 5:ca86a7848d54 36
JonFreeman 5:ca86a7848d54 37
JonFreeman 0:435bf84ce48a 38 /* STM32F401RE - compile using NUCLEO-F401RE
JonFreeman 12:d1d21a2941ef 39 // PROJECT - STM3_ESC Dual Brushless Motor Controller - Jon Freeman June 2018.
JonFreeman 0:435bf84ce48a 40
JonFreeman 0:435bf84ce48a 41 AnalogIn to read each motor current
JonFreeman 0:435bf84ce48a 42
JonFreeman 0:435bf84ce48a 43 ____________________________________________________________________________________
JonFreeman 0:435bf84ce48a 44 CONTROL PHILOSOPHY
JonFreeman 16:d1e4b9ad3b8b 45 This STM3_ESC Dual Brushless Motor drive board software should ensure sensible control when commands supplied are not sensible !
JonFreeman 0:435bf84ce48a 46
JonFreeman 0:435bf84ce48a 47 That is, smooth transition between Drive, Coast and Brake to be implemented here.
JonFreeman 0:435bf84ce48a 48 The remote controller must not be relied upon to deliver sensible command sequences.
JonFreeman 0:435bf84ce48a 49
JonFreeman 0:435bf84ce48a 50 So much the better if the remote controller does issue sensible commands, but ...
JonFreeman 0:435bf84ce48a 51
JonFreeman 0:435bf84ce48a 52 ____________________________________________________________________________________
JonFreeman 0:435bf84ce48a 53
JonFreeman 0:435bf84ce48a 54
JonFreeman 0:435bf84ce48a 55 */
JonFreeman 8:93203f473f6e 56
JonFreeman 16:d1e4b9ad3b8b 57 //#if defined (TARGET_NUCLEO_L476RG) // CPU in 64 pin LQFP ** NOT PROVED ** No good, pinmap different
JonFreeman 16:d1e4b9ad3b8b 58 //#include "F401RE.h"
JonFreeman 16:d1e4b9ad3b8b 59 //#endif
JonFreeman 16:d1e4b9ad3b8b 60 #if defined (TARGET_NUCLEO_F401RE) // CPU in 64 pin LQFP - This is what all produced boards have
JonFreeman 15:2591e2008323 61 #include "F401RE.h"
JonFreeman 6:f289a49c1eae 62 #endif
JonFreeman 16:d1e4b9ad3b8b 63 #if defined (TARGET_NUCLEO_F411RE) // CPU in 64 pin LQFP - Never tried, but probably would work as is
JonFreeman 15:2591e2008323 64 #include "F411RE.h"
JonFreeman 13:ef7a06fa11de 65 #endif
JonFreeman 7:6deaeace9a3e 66 #if defined (TARGET_NUCLEO_F446ZE) // CPU in 144 pin LQFP
JonFreeman 10:e40d8724268a 67 #include "F446ZE.h" // A thought for future version
JonFreeman 6:f289a49c1eae 68 #endif
JonFreeman 0:435bf84ce48a 69 /* Global variable declarations */
JonFreeman 16:d1e4b9ad3b8b 70
JonFreeman 16:d1e4b9ad3b8b 71 //volatile uint32_t fast_sys_timer = 0; // gets incremented by our Ticker ISR every VOLTAGE_READ_INTERVAL_US
JonFreeman 16:d1e4b9ad3b8b 72 uint32_t WatchDog = 0, // Set this up in main once pre-flight checks done. Allow extra few seconds at powerup
JonFreeman 16:d1e4b9ad3b8b 73 volt_reading = 0, // Global updated by interrupt driven read of Battery Volts
JonFreeman 0:435bf84ce48a 74 driverpot_reading = 0, // Global updated by interrupt driven read of Drivers Pot
JonFreeman 13:ef7a06fa11de 75 sys_timer = 0, // gets incremented by our Ticker ISR every MAIN_LOOP_REPEAT_TIME_US, 31250us at Sept 2019
JonFreeman 5:ca86a7848d54 76 AtoD_Semaphore = 0;
JonFreeman 12:d1d21a2941ef 77
JonFreeman 16:d1e4b9ad3b8b 78 bool WatchDogEnable = false; // Must recieve explicit instruction from pc or controller to enable
JonFreeman 11:bfb73f083009 79 bool loop_flag = false; // made true in ISR_loop_timer, picked up and made false again in main programme loop
JonFreeman 11:bfb73f083009 80 bool flag_8Hz = false; // As loop_flag but repeats 8 times per sec
JonFreeman 16:d1e4b9ad3b8b 81 bool temp_sensor_exists = false; // March 2020 - Now used to indicate presence or not of LM75B i2c temp sensor
JonFreeman 3:ecb00e0e8d68 82
JonFreeman 16:d1e4b9ad3b8b 83 /*
JonFreeman 16:d1e4b9ad3b8b 84 * Not used since LMT01 proved not a good choice. See LM75B code added March 2020
JonFreeman 16:d1e4b9ad3b8b 85 *
JonFreeman 13:ef7a06fa11de 86 #ifdef TEMP_SENSOR_ENABLE
JonFreeman 6:f289a49c1eae 87 uint32_t temp_sensor_count = 0, // incremented by every rising edge from LMT01
JonFreeman 12:d1d21a2941ef 88 last_temperature_count = 0; // global updated approx every 100ms after each LMT01 conversion completes
JonFreeman 6:f289a49c1eae 89 Ticker temperature_find_ticker;
JonFreeman 6:f289a49c1eae 90 Timer temperature_timer;
JonFreeman 13:ef7a06fa11de 91 #endif
JonFreeman 16:d1e4b9ad3b8b 92 */
JonFreeman 16:d1e4b9ad3b8b 93 /* End of Global variable declarations */
JonFreeman 12:d1d21a2941ef 94
JonFreeman 16:d1e4b9ad3b8b 95 Ticker tick_vread; // Device to cause periodic interrupts, used to time voltage readings etc - 50 us
JonFreeman 16:d1e4b9ad3b8b 96 Ticker loop_timer; // Device to cause periodic interrupts, used to sync iterations of main programme loop
JonFreeman 16:d1e4b9ad3b8b 97
JonFreeman 16:d1e4b9ad3b8b 98 eeprom_settings user_settings (SDA_PIN, SCL_PIN) ; // This MUST come before Motors setup
JonFreeman 16:d1e4b9ad3b8b 99 RControl_In RC_chan_1 (PC_14);
JonFreeman 16:d1e4b9ad3b8b 100 RControl_In RC_chan_2 (PC_15); // Instantiate two radio control input channels and specify which InterruptIn pin
JonFreeman 12:d1d21a2941ef 101 error_handling_Jan_2019 ESC_Error ; // Provides array usable to store error codes.
JonFreeman 0:435bf84ce48a 102
JonFreeman 16:d1e4b9ad3b8b 103 //PCT2075 temp_sensor( i2c ); // or LM75B temp_sensor( p?, p? ); Added March 2020
JonFreeman 16:d1e4b9ad3b8b 104 PCT2075 temp_sensor( SDA_PIN, SCL_PIN ); // or LM75B temp_sensor( p?, p? ); Added March 2020
JonFreeman 4:21d91465e4b1 105
JonFreeman 16:d1e4b9ad3b8b 106
JonFreeman 0:435bf84ce48a 107 /*
JonFreeman 16:d1e4b9ad3b8b 108 5 1 3 2 6 4 Brushless Motor Hall SENSOR SEQUENCE
JonFreeman 0:435bf84ce48a 109
JonFreeman 3:ecb00e0e8d68 110 1 1 1 1 0 0 0 ---___---___ Hall1
JonFreeman 3:ecb00e0e8d68 111 2 0 0 1 1 1 0 __---___---_ Hall2
JonFreeman 3:ecb00e0e8d68 112 4 1 0 0 0 1 1 -___---___-- Hall3
JonFreeman 0:435bf84ce48a 113
JonFreeman 0:435bf84ce48a 114 UV WV WU VU VW UW OUTPUT SEQUENCE
JonFreeman 8:93203f473f6e 115
JonFreeman 16:d1e4b9ad3b8b 116 Dec 2019 Support for DC motors deleted.
JonFreeman 16:d1e4b9ad3b8b 117 Hall codes 0 and 7 not used for brushless motors. Without Hall sensors connected pullup resistors give code 7.
JonFreeman 8:93203f473f6e 118
JonFreeman 0:435bf84ce48a 119 */
JonFreeman 16:d1e4b9ad3b8b 120 const uint16_t A_tabl[] = { // Table of motor energisation bit patterns. Rows are Handbrake, Forward, Reverse, Regen brake. Cols relate to Hall sensor outputs
JonFreeman 16:d1e4b9ad3b8b 121 // AUHVL|AWL, AWHUL|AVL, AWHVL|AUH, AVHWL|AUL, AUHWL|AVH, AVHUL|AWH
JonFreeman 16:d1e4b9ad3b8b 122 0, AUHVL|AWL, AWHUL|AVL, AWHVL|AUH, AVHWL|AUL, AUHWL|AVH, AVHUL|AWH, 0, // Handbrake
JonFreeman 16:d1e4b9ad3b8b 123 // 1->3 2->6 3->2 4->5 5->1 6->4
JonFreeman 16:d1e4b9ad3b8b 124 0, AWHVL, AVHUL, AWHUL, AUHWL, AUHVL, AVHWL, 0, // Forward 0, WV1, VU1, WU1, UW1, UV1, VW1, 0,
JonFreeman 16:d1e4b9ad3b8b 125 // 1->5 2->3 3->1 4->6 5->4 6->2
JonFreeman 16:d1e4b9ad3b8b 126 0, AVHWL, AUHVL, AUHWL, AWHUL, AVHUL, AWHVL, 0, // Reverse 0, VW1, UV1, UW1, WU1, VU1, WV1, 0,
JonFreeman 16:d1e4b9ad3b8b 127 0, BRA, BRA, BRA, BRA, BRA, BRA, BRA, // Regenerative Braking
JonFreeman 4:21d91465e4b1 128 KEEP_L_MASK_A, KEEP_H_MASK_A // [32 and 33]
JonFreeman 3:ecb00e0e8d68 129 } ;
JonFreeman 16:d1e4b9ad3b8b 130 // AUHVL|AWL, AWHUL|AVL, AWHVL|AUH, AVHWL|AUL, AUHWL|AVH, AVHUL|AWH
JonFreeman 16:d1e4b9ad3b8b 131 const uint16_t B_tabl[] = { // Different tables for Motors A and B as different ports and different port bits used.
JonFreeman 16:d1e4b9ad3b8b 132 // 0, 0, 0, 0, 0, 0, 0, 0, // Handbrake
JonFreeman 16:d1e4b9ad3b8b 133 0, BUHVL|BWL, BWHUL|BVL, BWHVL|BUH, BVHWL|BUL, BUHWL|BVH, BVHUL|BWH, 0, // Handbrake
JonFreeman 16:d1e4b9ad3b8b 134 0, BWHVL, BVHUL, BWHUL, BUHWL, BUHVL, BVHWL, 0, // Forward 0, WV1, VU1, WU1, UW1, UV1, VW1, 0,
JonFreeman 16:d1e4b9ad3b8b 135 0, BVHWL, BUHVL, BUHWL, BWHUL, BVHUL, BWHVL, 0, // Reverse 0, VW1, UV1, UW1, WU1, VU1, WV1, 0,
JonFreeman 16:d1e4b9ad3b8b 136 0, BRB, BRB, BRB, BRB, BRB, BRB, BRB, // Regenerative Braking
JonFreeman 4:21d91465e4b1 137 KEEP_L_MASK_B, KEEP_H_MASK_B
JonFreeman 3:ecb00e0e8d68 138 } ;
JonFreeman 0:435bf84ce48a 139
JonFreeman 12:d1d21a2941ef 140 brushless_motor MotorA (MOT_A_I_ADC, APWMV, APWMI, A_tabl, _MAH1, _MAH2, _MAH3, PortA, PORT_A_MASK, ISHUNTA);
JonFreeman 16:d1e4b9ad3b8b 141 brushless_motor MotorB (MOT_B_I_ADC, BPWMV, BPWMI, B_tabl, _MBH1, _MBH2, _MBH3, PortB, PORT_B_MASK, ISHUNTB); // Two brushless motor instantiations
JonFreeman 5:ca86a7848d54 142
JonFreeman 12:d1d21a2941ef 143 extern cli_2019 pcc, tsc; // command line interpreters from pc and touch screen
JonFreeman 12:d1d21a2941ef 144
JonFreeman 16:d1e4b9ad3b8b 145 static const int LM75_I2C_ADDR = 0x90; // i2c temperature sensor
JonFreeman 16:d1e4b9ad3b8b 146
JonFreeman 12:d1d21a2941ef 147 // Interrupt Service Routines
JonFreeman 16:d1e4b9ad3b8b 148 /*
JonFreeman 13:ef7a06fa11de 149 #ifdef TEMP_SENSOR_ENABLE
JonFreeman 12:d1d21a2941ef 150 void ISR_temperature_find_ticker () // every 960 us, i.e. slightly faster than once per milli sec
JonFreeman 12:d1d21a2941ef 151 {
JonFreeman 12:d1d21a2941ef 152 static bool readflag = false;
JonFreeman 12:d1d21a2941ef 153 int t = temperature_timer.read_ms ();
JonFreeman 12:d1d21a2941ef 154 if ((t == 5) && (!readflag)) {
JonFreeman 12:d1d21a2941ef 155 last_temperature_count = temp_sensor_count;
JonFreeman 12:d1d21a2941ef 156 temp_sensor_count = 0;
JonFreeman 12:d1d21a2941ef 157 readflag = true;
JonFreeman 12:d1d21a2941ef 158 }
JonFreeman 12:d1d21a2941ef 159 if (t == 6)
JonFreeman 12:d1d21a2941ef 160 readflag = false;
JonFreeman 12:d1d21a2941ef 161 }
JonFreeman 16:d1e4b9ad3b8b 162
JonFreeman 16:d1e4b9ad3b8b 163 void temp_sensor_isr () // got rising edge from LMT01. ALMOST CERTAIN this misses some
JonFreeman 16:d1e4b9ad3b8b 164 {
JonFreeman 16:d1e4b9ad3b8b 165 int t = temperature_timer.read_us (); // Must be being overrun by something, most likely culprit A-D reading ?
JonFreeman 16:d1e4b9ad3b8b 166 temperature_timer.reset ();
JonFreeman 16:d1e4b9ad3b8b 167 temp_sensor_count++;
JonFreeman 16:d1e4b9ad3b8b 168 if (t > 18) // Yes proved some interrupts get missed, this fixes temperature reading
JonFreeman 16:d1e4b9ad3b8b 169 temp_sensor_count++;
JonFreeman 16:d1e4b9ad3b8b 170 // T2 = !T2; // scope hanger
JonFreeman 16:d1e4b9ad3b8b 171 }
JonFreeman 13:ef7a06fa11de 172 #endif
JonFreeman 16:d1e4b9ad3b8b 173 */
JonFreeman 16:d1e4b9ad3b8b 174
JonFreeman 12:d1d21a2941ef 175 /** void ISR_loop_timer ()
JonFreeman 12:d1d21a2941ef 176 * This ISR responds to Ticker interrupts at a rate of (probably) 32 times per second (check from constant declarations above)
JonFreeman 12:d1d21a2941ef 177 * This ISR sets global flag 'loop_flag' used to synchronise passes around main programme control loop.
JonFreeman 12:d1d21a2941ef 178 * Increments global 'sys_timer', usable anywhere as general measure of elapsed time
JonFreeman 12:d1d21a2941ef 179 */
JonFreeman 16:d1e4b9ad3b8b 180 void ISR_loop_timer () // This is Ticker Interrupt Service Routine - loop timer - MAIN_LOOP_REPEAT_TIME_US (32Hz)
JonFreeman 8:93203f473f6e 181 {
JonFreeman 12:d1d21a2941ef 182 loop_flag = true; // set flag to allow main programme loop to proceed
JonFreeman 12:d1d21a2941ef 183 sys_timer++; // Just a handy measure of elapsed time for anything to use
JonFreeman 12:d1d21a2941ef 184 if ((sys_timer & 0x03) == 0)
JonFreeman 12:d1d21a2941ef 185 flag_8Hz = true;
JonFreeman 8:93203f473f6e 186 }
JonFreeman 8:93203f473f6e 187
JonFreeman 12:d1d21a2941ef 188 /** void ISR_voltage_reader ()
JonFreeman 12:d1d21a2941ef 189 * This ISR responds to Ticker interrupts every 'VOLTAGE_READ_INTERVAL_US' micro seconds
JonFreeman 12:d1d21a2941ef 190 *
JonFreeman 12:d1d21a2941ef 191 * AtoD_reader() called from convenient point in code to take readings outside of ISRs
JonFreeman 12:d1d21a2941ef 192 */
JonFreeman 12:d1d21a2941ef 193 void ISR_voltage_reader () // This is Ticker Interrupt Service Routine ; few us between readings ; VOLTAGE_READ_INTERVAL_US = 50
JonFreeman 12:d1d21a2941ef 194 {
JonFreeman 12:d1d21a2941ef 195 AtoD_Semaphore++;
JonFreeman 16:d1e4b9ad3b8b 196 // fast_sys_timer++; // Just a handy measure of elapsed time for anything to use
JonFreeman 16:d1e4b9ad3b8b 197 }
JonFreeman 16:d1e4b9ad3b8b 198
JonFreeman 16:d1e4b9ad3b8b 199 // End of Interrupt Service Routines
JonFreeman 16:d1e4b9ad3b8b 200
JonFreeman 16:d1e4b9ad3b8b 201 const char * get_version () {
JonFreeman 16:d1e4b9ad3b8b 202 return "1.0.y2020.m05.d21\0"; // Version string, readable using 'ver' serial command
JonFreeman 12:d1d21a2941ef 203 }
JonFreeman 12:d1d21a2941ef 204
JonFreeman 17:cc9b854295d6 205 /*bool read_temperature (float & t) {
JonFreeman 16:d1e4b9ad3b8b 206 // pc.printf ("test param temp = %7.3f\r\n", t);
JonFreeman 16:d1e4b9ad3b8b 207 if (!temp_sensor_exists)
JonFreeman 16:d1e4b9ad3b8b 208 return false;
JonFreeman 16:d1e4b9ad3b8b 209 t = temp_sensor;
JonFreeman 16:d1e4b9ad3b8b 210 return true;
JonFreeman 17:cc9b854295d6 211 }*/
JonFreeman 12:d1d21a2941ef 212
JonFreeman 16:d1e4b9ad3b8b 213 void setVI_A (double v, double i)
JonFreeman 8:93203f473f6e 214 {
JonFreeman 4:21d91465e4b1 215 MotorA.set_V_limit (v); // Sets max motor voltage
JonFreeman 4:21d91465e4b1 216 MotorA.set_I_limit (i); // Sets max motor current
JonFreeman 16:d1e4b9ad3b8b 217 }
JonFreeman 16:d1e4b9ad3b8b 218
JonFreeman 16:d1e4b9ad3b8b 219 void setVI_B (double v, double i)
JonFreeman 16:d1e4b9ad3b8b 220 {
JonFreeman 4:21d91465e4b1 221 MotorB.set_V_limit (v);
JonFreeman 4:21d91465e4b1 222 MotorB.set_I_limit (i);
JonFreeman 4:21d91465e4b1 223 }
JonFreeman 11:bfb73f083009 224
JonFreeman 16:d1e4b9ad3b8b 225 void setVI_both (double v, double i)
JonFreeman 16:d1e4b9ad3b8b 226 {
JonFreeman 16:d1e4b9ad3b8b 227 setVI_A (v, i);
JonFreeman 16:d1e4b9ad3b8b 228 setVI_B (v, i);
JonFreeman 16:d1e4b9ad3b8b 229 }
JonFreeman 16:d1e4b9ad3b8b 230
JonFreeman 3:ecb00e0e8d68 231
JonFreeman 5:ca86a7848d54 232 /**
JonFreeman 12:d1d21a2941ef 233 * void AtoD_reader () // Call to here every VOLTAGE_READ_INTERVAL_US = 50 once loop responds to flag set in isr
JonFreeman 12:d1d21a2941ef 234 * Not part of ISR
JonFreeman 5:ca86a7848d54 235 */
JonFreeman 3:ecb00e0e8d68 236 void AtoD_reader () // Call to here every VOLTAGE_READ_INTERVAL_US = 50 once loop responds to flag set in isr
JonFreeman 0:435bf84ce48a 237 {
JonFreeman 11:bfb73f083009 238 static uint32_t i = 0;
JonFreeman 3:ecb00e0e8d68 239 if (MotorA.tickleon)
JonFreeman 3:ecb00e0e8d68 240 MotorA.high_side_off ();
JonFreeman 3:ecb00e0e8d68 241 if (MotorB.tickleon)
JonFreeman 3:ecb00e0e8d68 242 MotorB.high_side_off ();
JonFreeman 16:d1e4b9ad3b8b 243 if (AtoD_Semaphore > 10) {
JonFreeman 8:93203f473f6e 244 pc.printf ("WARNING - sema cnt %d\r\n", AtoD_Semaphore);
JonFreeman 16:d1e4b9ad3b8b 245 AtoD_Semaphore = 10;
JonFreeman 0:435bf84ce48a 246 }
JonFreeman 16:d1e4b9ad3b8b 247 while (AtoD_Semaphore > 0) { // semaphore gets incremented in timer interrupt handler, t=50us
JonFreeman 0:435bf84ce48a 248 AtoD_Semaphore--;
JonFreeman 0:435bf84ce48a 249 // Code here to sequence through reading voltmeter, demand pot, ammeters
JonFreeman 0:435bf84ce48a 250 switch (i) { //
JonFreeman 0:435bf84ce48a 251 case 0:
JonFreeman 0:435bf84ce48a 252 volt_reading += Ain_SystemVolts.read_u16 (); // Result = Result + New Reading
JonFreeman 0:435bf84ce48a 253 volt_reading >>= 1; // Result = Result / 2
JonFreeman 0:435bf84ce48a 254 break; // i.e. Very simple digital low pass filter
JonFreeman 0:435bf84ce48a 255 case 1:
JonFreeman 0:435bf84ce48a 256 driverpot_reading += Ain_DriverPot.read_u16 ();
JonFreeman 0:435bf84ce48a 257 driverpot_reading >>= 1;
JonFreeman 0:435bf84ce48a 258 break;
JonFreeman 0:435bf84ce48a 259 case 2:
JonFreeman 12:d1d21a2941ef 260 MotorA.sniff_current (); // Initiate ADC current reading
JonFreeman 0:435bf84ce48a 261 break;
JonFreeman 0:435bf84ce48a 262 case 3:
JonFreeman 11:bfb73f083009 263 MotorB.sniff_current ();
JonFreeman 0:435bf84ce48a 264 break;
JonFreeman 0:435bf84ce48a 265 }
JonFreeman 0:435bf84ce48a 266 i++; // prepare to read the next input in response to the next interrupt
JonFreeman 0:435bf84ce48a 267 if (i > 3)
JonFreeman 0:435bf84ce48a 268 i = 0;
JonFreeman 3:ecb00e0e8d68 269 } // end of while (AtoD_Semaphore > 0) {
JonFreeman 3:ecb00e0e8d68 270 if (MotorA.tickleon) {
JonFreeman 3:ecb00e0e8d68 271 MotorA.tickleon--;
JonFreeman 5:ca86a7848d54 272 MotorA.motor_set (); // Reactivate any high side switches turned off above
JonFreeman 3:ecb00e0e8d68 273 }
JonFreeman 3:ecb00e0e8d68 274 if (MotorB.tickleon) {
JonFreeman 3:ecb00e0e8d68 275 MotorB.tickleon--;
JonFreeman 3:ecb00e0e8d68 276 MotorB.motor_set ();
JonFreeman 0:435bf84ce48a 277 }
JonFreeman 0:435bf84ce48a 278 }
JonFreeman 0:435bf84ce48a 279
JonFreeman 10:e40d8724268a 280
JonFreeman 0:435bf84ce48a 281 /** double Read_DriverPot ()
JonFreeman 0:435bf84ce48a 282 * driverpot_reading is a global 16 bit unsigned int updated in interrupt service routine
JonFreeman 10:e40d8724268a 283 * ISR also filters signal by returning average of most recent two readings
JonFreeman 0:435bf84ce48a 284 * This function returns normalised double (range 0.0 to 1.0) representation of driver pot position
JonFreeman 0:435bf84ce48a 285 */
JonFreeman 0:435bf84ce48a 286 double Read_DriverPot ()
JonFreeman 0:435bf84ce48a 287 {
JonFreeman 5:ca86a7848d54 288 return ((double) driverpot_reading) / 65536.0; // Normalise 0.0 <= control pot <= 1.0
JonFreeman 0:435bf84ce48a 289 }
JonFreeman 0:435bf84ce48a 290
JonFreeman 0:435bf84ce48a 291 double Read_BatteryVolts ()
JonFreeman 0:435bf84ce48a 292 {
JonFreeman 5:ca86a7848d54 293 return ((double) volt_reading) / 951.0; // divisor fiddled to make voltage reading correct !
JonFreeman 0:435bf84ce48a 294 }
JonFreeman 0:435bf84ce48a 295
JonFreeman 12:d1d21a2941ef 296
JonFreeman 16:d1e4b9ad3b8b 297 double trim (const double min, const double max, double input) {
JonFreeman 16:d1e4b9ad3b8b 298 if (input < min) input = min;
JonFreeman 16:d1e4b9ad3b8b 299 if (input > max) input = max;
JonFreeman 16:d1e4b9ad3b8b 300 return input;
JonFreeman 13:ef7a06fa11de 301 }
JonFreeman 13:ef7a06fa11de 302
JonFreeman 16:d1e4b9ad3b8b 303 void brake_motors_both (double brake_effort) {
JonFreeman 16:d1e4b9ad3b8b 304 MotorA.brake (brake_effort);
JonFreeman 16:d1e4b9ad3b8b 305 MotorB.brake (brake_effort);
JonFreeman 16:d1e4b9ad3b8b 306 }
JonFreeman 16:d1e4b9ad3b8b 307
JonFreeman 16:d1e4b9ad3b8b 308
JonFreeman 16:d1e4b9ad3b8b 309 void mode_set_motors_both (int mode) // called from cli to set fw, re, rb, hb
JonFreeman 8:93203f473f6e 310 {
JonFreeman 2:04761b196473 311 MotorA.set_mode (mode);
JonFreeman 2:04761b196473 312 MotorB.set_mode (mode);
JonFreeman 2:04761b196473 313 }
JonFreeman 0:435bf84ce48a 314
JonFreeman 12:d1d21a2941ef 315
JonFreeman 16:d1e4b9ad3b8b 316 bool is_it_worth_the_bother (double a, double b) { // Tests size of change. No point updating tiny demand changes
JonFreeman 10:e40d8724268a 317 double c = a - b;
JonFreeman 10:e40d8724268a 318 if (c < 0.0)
JonFreeman 10:e40d8724268a 319 c = 0.0 - c;
JonFreeman 10:e40d8724268a 320 if (c > 0.02)
JonFreeman 10:e40d8724268a 321 return true;
JonFreeman 10:e40d8724268a 322 return false;
JonFreeman 10:e40d8724268a 323 }
JonFreeman 10:e40d8724268a 324
JonFreeman 16:d1e4b9ad3b8b 325 void hand_control_state_machine (int source) { // if hand control user_settings '3', get here @ approx 30Hz to read drivers control pot
JonFreeman 16:d1e4b9ad3b8b 326 // if servo1 user_settings '4', reads input from servo1 instead
JonFreeman 16:d1e4b9ad3b8b 327 enum { // states used in RC_or_hand_control_state_machine()
JonFreeman 10:e40d8724268a 328 HAND_CONT_IDLE,
JonFreeman 10:e40d8724268a 329 HAND_CONT_BRAKE_WAIT,
JonFreeman 10:e40d8724268a 330 HAND_CONT_BRAKE_POT,
JonFreeman 10:e40d8724268a 331 HAND_CONT_STATE_INTO_BRAKING,
JonFreeman 10:e40d8724268a 332 HAND_CONT_STATE_BRAKING,
JonFreeman 10:e40d8724268a 333 HAND_CONT_STATE_INTO_DRIVING,
JonFreeman 10:e40d8724268a 334 HAND_CONT_STATE_DRIVING
JonFreeman 10:e40d8724268a 335 } ;
JonFreeman 10:e40d8724268a 336
JonFreeman 10:e40d8724268a 337 static int hand_controller_state = HAND_CONT_IDLE;
JonFreeman 10:e40d8724268a 338 // static int old_hand_controller_direction = T5; // Nov 2018 reworked thanks to feedback from Rob and Quentin
JonFreeman 8:93203f473f6e 339 static double brake_effort, drive_effort, pot_position, old_pot_position = 0.0;
JonFreeman 10:e40d8724268a 340 static int dirbuf[5] = {100,100,100,100,100}; // Initialised with invalid direction such that 'change' is read from switch
JonFreeman 10:e40d8724268a 341 static int dirbufptr = 0, direction_old = -1, direction_new = -1;
JonFreeman 10:e40d8724268a 342 const int buflen = sizeof(dirbuf) / sizeof(int);
JonFreeman 16:d1e4b9ad3b8b 343 double User_Brake_Range; //
JonFreeman 10:e40d8724268a 344
JonFreeman 13:ef7a06fa11de 345 direction_old = direction_new;
JonFreeman 10:e40d8724268a 346
JonFreeman 10:e40d8724268a 347 // Test for change in direction switch setting.
JonFreeman 10:e40d8724268a 348 // If whole buffer NEWLY filled with all Fwd or all Rev, state = brake_wait
JonFreeman 10:e40d8724268a 349 int diracc = 0;
JonFreeman 10:e40d8724268a 350 if (dirbufptr >= buflen)
JonFreeman 10:e40d8724268a 351 dirbufptr = 0;
JonFreeman 10:e40d8724268a 352 dirbuf[dirbufptr++] = T5; // Read direction switch into circular debounce buffer
JonFreeman 10:e40d8724268a 353 for (int i = 0; i < buflen; i++)
JonFreeman 10:e40d8724268a 354 diracc += dirbuf[i]; // will = 0 or buflen if direction switch stable
JonFreeman 10:e40d8724268a 355 if (diracc == buflen || diracc == 0) // if was all 0 or all 1
JonFreeman 10:e40d8724268a 356 direction_new = diracc / buflen;
JonFreeman 10:e40d8724268a 357 if (direction_new != direction_old)
JonFreeman 10:e40d8724268a 358 hand_controller_state = HAND_CONT_BRAKE_WAIT; // validated change of direction switch
JonFreeman 10:e40d8724268a 359
JonFreeman 10:e40d8724268a 360 switch (source) {
JonFreeman 16:d1e4b9ad3b8b 361 case HAND: // driver pot is control input
JonFreeman 10:e40d8724268a 362 pot_position = Read_DriverPot (); // Only place read in the loop, rteads normalised 0.0 to 1.0
JonFreeman 16:d1e4b9ad3b8b 363 User_Brake_Range = user_settings.user_brake_range();
JonFreeman 10:e40d8724268a 364 break;
JonFreeman 10:e40d8724268a 365 default:
JonFreeman 10:e40d8724268a 366 pot_position = 0.0;
JonFreeman 10:e40d8724268a 367 break;
JonFreeman 8:93203f473f6e 368 }
JonFreeman 10:e40d8724268a 369
JonFreeman 10:e40d8724268a 370 switch (hand_controller_state) {
JonFreeman 10:e40d8724268a 371 case HAND_CONT_IDLE: // Here for first few passes until validated direction switch reading
JonFreeman 10:e40d8724268a 372 break;
JonFreeman 10:e40d8724268a 373
JonFreeman 11:bfb73f083009 374 case HAND_CONT_BRAKE_WAIT: // Only get here after direction input changed or newly validated at power on
JonFreeman 10:e40d8724268a 375 pc.printf ("At HAND_CONT_BRAKE_WAIT\r\n");
JonFreeman 10:e40d8724268a 376 brake_effort = 0.05; // Apply braking not too fiercely
JonFreeman 16:d1e4b9ad3b8b 377 brake_motors_both (brake_effort);
JonFreeman 10:e40d8724268a 378 hand_controller_state = HAND_CONT_BRAKE_POT;
JonFreeman 10:e40d8724268a 379 break;
JonFreeman 10:e40d8724268a 380
JonFreeman 11:bfb73f083009 381 case HAND_CONT_BRAKE_POT: // Only get here after one pass through HAND_CONT_BRAKE_WAIT but
JonFreeman 16:d1e4b9ad3b8b 382 if (brake_effort < 0.95) { // remain in this state until driver has turned pott fully anticlockwise
JonFreeman 11:bfb73f083009 383 brake_effort += 0.05; // ramp braking up to max over about one sec after direction change or validation
JonFreeman 16:d1e4b9ad3b8b 384 brake_motors_both (brake_effort); // Direction change or testing at power on
JonFreeman 10:e40d8724268a 385 pc.printf ("Brake effort %.2f\r\n", brake_effort);
JonFreeman 10:e40d8724268a 386 }
JonFreeman 10:e40d8724268a 387 else { // once braking up to quite hard
JonFreeman 10:e40d8724268a 388 if (pot_position < 0.02) { // Driver has turned pot fully anticlock
JonFreeman 10:e40d8724268a 389 hand_controller_state = HAND_CONT_STATE_BRAKING;
JonFreeman 10:e40d8724268a 390 }
JonFreeman 8:93203f473f6e 391 }
JonFreeman 8:93203f473f6e 392 break;
JonFreeman 10:e40d8724268a 393
JonFreeman 8:93203f473f6e 394 case HAND_CONT_STATE_INTO_BRAKING:
JonFreeman 16:d1e4b9ad3b8b 395 brake_effort = (User_Brake_Range - pot_position) / User_Brake_Range;
JonFreeman 10:e40d8724268a 396 if (brake_effort < 0.0)
JonFreeman 10:e40d8724268a 397 brake_effort = 0.5;
JonFreeman 16:d1e4b9ad3b8b 398 brake_motors_both (brake_effort);
JonFreeman 8:93203f473f6e 399 old_pot_position = pot_position;
JonFreeman 10:e40d8724268a 400 hand_controller_state = HAND_CONT_STATE_BRAKING;
JonFreeman 8:93203f473f6e 401 pc.printf ("Brake\r\n");
JonFreeman 8:93203f473f6e 402 break;
JonFreeman 10:e40d8724268a 403
JonFreeman 8:93203f473f6e 404 case HAND_CONT_STATE_BRAKING:
JonFreeman 16:d1e4b9ad3b8b 405 if (pot_position > User_Brake_Range) // escape braking into drive
JonFreeman 10:e40d8724268a 406 hand_controller_state = HAND_CONT_STATE_INTO_DRIVING;
JonFreeman 8:93203f473f6e 407 else {
JonFreeman 16:d1e4b9ad3b8b 408 if (is_it_worth_the_bother(pot_position, old_pot_position)) {
JonFreeman 8:93203f473f6e 409 old_pot_position = pot_position;
JonFreeman 16:d1e4b9ad3b8b 410 brake_effort = (User_Brake_Range - pot_position) / User_Brake_Range;
JonFreeman 16:d1e4b9ad3b8b 411 brake_motors_both (brake_effort);
JonFreeman 8:93203f473f6e 412 }
JonFreeman 8:93203f473f6e 413 }
JonFreeman 8:93203f473f6e 414 break;
JonFreeman 10:e40d8724268a 415
JonFreeman 11:bfb73f083009 416 case HAND_CONT_STATE_INTO_DRIVING: // Only get here after HAND_CONT_STATE_BRAKING
JonFreeman 10:e40d8724268a 417 pc.printf ("Drive\r\n");
JonFreeman 10:e40d8724268a 418 if (direction_new == 1)
JonFreeman 16:d1e4b9ad3b8b 419 mode_set_motors_both (MOTOR_FORWARD); // sets both motors
JonFreeman 10:e40d8724268a 420 else
JonFreeman 16:d1e4b9ad3b8b 421 mode_set_motors_both (MOTOR_REVERSE);
JonFreeman 10:e40d8724268a 422 hand_controller_state = HAND_CONT_STATE_DRIVING;
JonFreeman 10:e40d8724268a 423 break;
JonFreeman 10:e40d8724268a 424
JonFreeman 8:93203f473f6e 425 case HAND_CONT_STATE_DRIVING:
JonFreeman 16:d1e4b9ad3b8b 426 if (pot_position < User_Brake_Range) // escape braking into drive
JonFreeman 10:e40d8724268a 427 hand_controller_state = HAND_CONT_STATE_INTO_BRAKING;
JonFreeman 8:93203f473f6e 428 else {
JonFreeman 16:d1e4b9ad3b8b 429 if (is_it_worth_the_bother(pot_position, old_pot_position)) {
JonFreeman 8:93203f473f6e 430 old_pot_position = pot_position;
JonFreeman 16:d1e4b9ad3b8b 431 drive_effort = (pot_position - User_Brake_Range) / (1.0 - User_Brake_Range);
JonFreeman 16:d1e4b9ad3b8b 432 setVI_both (drive_effort, drive_effort); // Wind volts and amps up and down together ???
JonFreeman 8:93203f473f6e 433 pc.printf ("Drive %.2f\r\n", drive_effort);
JonFreeman 8:93203f473f6e 434 }
JonFreeman 8:93203f473f6e 435 }
JonFreeman 8:93203f473f6e 436 break;
JonFreeman 10:e40d8724268a 437
JonFreeman 8:93203f473f6e 438 default:
JonFreeman 10:e40d8724268a 439 pc.printf ("Unhandled Hand Controller state %d\r\n", hand_controller_state);
JonFreeman 8:93203f473f6e 440 break;
JonFreeman 8:93203f473f6e 441 } // endof switch (hand_controller_state) {
JonFreeman 8:93203f473f6e 442 }
JonFreeman 8:93203f473f6e 443
JonFreeman 16:d1e4b9ad3b8b 444 void set_RCIN_offsets () {
JonFreeman 16:d1e4b9ad3b8b 445 RC_chan_1.set_offset (user_settings.rd(RCI1_TRIM), user_settings.rd(RCIN_REGBRAKE_RANGE), user_settings.rd(RCIN_STICK_ATTACK));
JonFreeman 16:d1e4b9ad3b8b 446 RC_chan_2.set_offset (user_settings.rd(RCI2_TRIM), user_settings.rd(RCIN_REGBRAKE_RANGE), user_settings.rd(RCIN_STICK_ATTACK));
JonFreeman 16:d1e4b9ad3b8b 447 RC_chan_1.set_chanmode (user_settings.rd(RCIN1), user_settings.rd(RCIN1REVERSE)) ;
JonFreeman 16:d1e4b9ad3b8b 448 RC_chan_2.set_chanmode (user_settings.rd(RCIN2), user_settings.rd(RCIN2REVERSE)) ;
JonFreeman 16:d1e4b9ad3b8b 449 }
JonFreeman 16:d1e4b9ad3b8b 450
JonFreeman 17:cc9b854295d6 451 void rcins_report () {
JonFreeman 17:cc9b854295d6 452 pc.printf ("RC1 pulsewidth %d, period %d, pulsecount %d\r\n", RC_chan_1.pulsewidth(), RC_chan_1.period(), RC_chan_1.pulsecount());
JonFreeman 17:cc9b854295d6 453 pc.printf ("RC2 pulsewidth %d, period %d, pulsecount %d\r\n", RC_chan_2.pulsewidth(), RC_chan_2.period(), RC_chan_2.pulsecount());
JonFreeman 17:cc9b854295d6 454 pc.printf ("\r\n");
JonFreeman 17:cc9b854295d6 455 }
JonFreeman 16:d1e4b9ad3b8b 456
JonFreeman 16:d1e4b9ad3b8b 457 int main() // Programme entry point
JonFreeman 0:435bf84ce48a 458 {
JonFreeman 16:d1e4b9ad3b8b 459 uint32_t eighth_sec_count = 0;
JonFreeman 0:435bf84ce48a 460 // Setup system timers to cause periodic interrupts to synchronise and automate volt and current readings, loop repeat rate etc
JonFreeman 16:d1e4b9ad3b8b 461 tick_vread.attach_us (&ISR_voltage_reader, VOLTAGE_READ_INTERVAL_US); // Start periodic interrupt generator - 50 us
JonFreeman 0:435bf84ce48a 462 loop_timer.attach_us (&ISR_loop_timer, MAIN_LOOP_REPEAT_TIME_US); // Start periodic interrupt generator
JonFreeman 16:d1e4b9ad3b8b 463
JonFreeman 0:435bf84ce48a 464 // Done setting up system interrupt timers
JonFreeman 0:435bf84ce48a 465
JonFreeman 12:d1d21a2941ef 466 com3.baud (1200); // Once had an idea to use this for IR comms, never tried
JonFreeman 12:d1d21a2941ef 467 com2.baud (19200); // Opto isolated serial bus connecting 'n' STM3_ESC boards to 1 Brute Touch Screen controller
JonFreeman 16:d1e4b9ad3b8b 468 // pc.baud (9600); // COM port to pc
JonFreeman 16:d1e4b9ad3b8b 469 pc.baud (user_settings.baud()); // COM port to pc
JonFreeman 6:f289a49c1eae 470
JonFreeman 16:d1e4b9ad3b8b 471 // pc.printf ("Baud rate %d\r\n", user_settings.baud());
JonFreeman 5:ca86a7848d54 472
JonFreeman 16:d1e4b9ad3b8b 473 MotorA.set_direction (user_settings.rd(MOTADIR)); // user_settingss all setup in class eeprom_settings {} user_settings ; constructor
JonFreeman 16:d1e4b9ad3b8b 474 MotorB.set_direction (user_settings.rd(MOTBDIR));
JonFreeman 16:d1e4b9ad3b8b 475 MotorA.poles (user_settings.rd(MOTAPOLES)); // Returns true if poles 4, 6 or 8. Returns false otherwise
JonFreeman 16:d1e4b9ad3b8b 476 MotorB.poles (user_settings.rd(MOTBPOLES)); // Only two calls are here
JonFreeman 16:d1e4b9ad3b8b 477 // MotorA.set_mode (MOTOR_REGENBRAKE); // Done in motor constructor
JonFreeman 16:d1e4b9ad3b8b 478 // MotorB.set_mode (MOTOR_REGENBRAKE);
JonFreeman 16:d1e4b9ad3b8b 479 // setVI_both (0.9, 0.5); // Power up with moderate regen braking applied
JonFreeman 16:d1e4b9ad3b8b 480 set_RCIN_offsets ();
JonFreeman 16:d1e4b9ad3b8b 481
JonFreeman 16:d1e4b9ad3b8b 482 class RC_stick_info RCstick1, RCstick2;
JonFreeman 12:d1d21a2941ef 483
JonFreeman 6:f289a49c1eae 484 // T1 = 0; Now WRONGLY hoped to be InterruptIn counting pulses from LMT01 temperature sensor
JonFreeman 5:ca86a7848d54 485 T2 = 0; // T2, T3, T4 As yet unused pins
JonFreeman 16:d1e4b9ad3b8b 486 // T3 = 0;// T4 = 0;// T5 = 0; now input from fw/re on remote control box
JonFreeman 0:435bf84ce48a 487 T6 = 0;
JonFreeman 16:d1e4b9ad3b8b 488 pc.printf ("\r\n\nSTM3_ESC Starting Ver %s, Command Source setting = %d\r\n", get_version(), user_settings.rd(COMM_SRC));
JonFreeman 16:d1e4b9ad3b8b 489 pc.printf ("Designed by Jon Freeman B. Eng. Hons - 2017-2020. e jon@jons-workshop.com\r\n");
JonFreeman 16:d1e4b9ad3b8b 490 if (user_settings.do_we_have_i2c (0xa0))
JonFreeman 16:d1e4b9ad3b8b 491 pc.printf ("Got EEPROM, i2c error count = %d\r\n", user_settings.errs());
JonFreeman 16:d1e4b9ad3b8b 492 else
JonFreeman 16:d1e4b9ad3b8b 493 pc.printf ("No eeprom found\r\n");
JonFreeman 16:d1e4b9ad3b8b 494 pc.printf ("ESC_Error.all_good() ret'd %s\r\n", ESC_Error.all_good() ? "true - Ready to Roll !" : "false");
JonFreeman 5:ca86a7848d54 495
JonFreeman 16:d1e4b9ad3b8b 496 //bool eeprom_settings::do_we_have_i2c (uint32_t x)
JonFreeman 16:d1e4b9ad3b8b 497 pc.printf ("LM75 temp sensor ");
JonFreeman 16:d1e4b9ad3b8b 498 if (user_settings.do_we_have_i2c (LM75_I2C_ADDR)) {
JonFreeman 16:d1e4b9ad3b8b 499 pc.printf ("reports temperature %7.3f\r\n", (float)temp_sensor );
JonFreeman 6:f289a49c1eae 500 temp_sensor_exists = true;
JonFreeman 16:d1e4b9ad3b8b 501 }
JonFreeman 16:d1e4b9ad3b8b 502 else
JonFreeman 16:d1e4b9ad3b8b 503 pc.printf ("Not Fitted\r\n");
JonFreeman 16:d1e4b9ad3b8b 504
JonFreeman 16:d1e4b9ad3b8b 505 uint32_t old_hand_controller_direction = T5;
JonFreeman 16:d1e4b9ad3b8b 506 if (user_settings.rd(COMM_SRC) == 3) { // Read fwd/rev switch 'T5', PA15 on 401
JonFreeman 8:93203f473f6e 507 pc.printf ("Pot control\r\n");
JonFreeman 8:93203f473f6e 508 if (T5)
JonFreeman 16:d1e4b9ad3b8b 509 mode_set_motors_both (MOTOR_FORWARD); // sets both motors
JonFreeman 8:93203f473f6e 510 else
JonFreeman 16:d1e4b9ad3b8b 511 mode_set_motors_both (MOTOR_REVERSE);
JonFreeman 8:93203f473f6e 512 }
JonFreeman 12:d1d21a2941ef 513 // pc.printf ("SystemCoreClock=%d, MAX_PWM_TICKS=%d\r\n", SystemCoreClock, MAX_PWM_TICKS);
JonFreeman 13:ef7a06fa11de 514
JonFreeman 13:ef7a06fa11de 515 WatchDog = WATCHDOG_RELOAD + 80; // Allow extra few seconds at powerup
JonFreeman 16:d1e4b9ad3b8b 516 pcc.flush (); // Flush serial rx buffers
JonFreeman 16:d1e4b9ad3b8b 517 tsc.flush ();
JonFreeman 16:d1e4b9ad3b8b 518 // pc.printf ("sizeof int is %d\r\n", sizeof(int)); // sizeof int is 4
JonFreeman 16:d1e4b9ad3b8b 519 double Current_Scaler; // New idea - Sept 2019. Plan is to scale down motor current limit when voltage lower than nom.
JonFreeman 16:d1e4b9ad3b8b 520 // See schematic for full details, but cycle-by-cycle current limit has the effect of allowing larger average I
JonFreeman 16:d1e4b9ad3b8b 521 // at lower voltages. This is simply because current takes longer to build in motor inductance when voltage
JonFreeman 16:d1e4b9ad3b8b 522 // is low. Conversely, at high supply voltages, motor current reaches limit quickly, cutting drive, meaning
JonFreeman 16:d1e4b9ad3b8b 523 // similar current flows for shorter times at higher voltages.
JonFreeman 13:ef7a06fa11de 524
JonFreeman 0:435bf84ce48a 525 while (1) { // Loop forever, repeats synchroised by waiting for ticker Interrupt Service Routine to set 'loop_flag' true
JonFreeman 0:435bf84ce48a 526 while (!loop_flag) { // Most of the time is spent in this loop, repeatedly re-checking for commands from pc port
JonFreeman 12:d1d21a2941ef 527 pcc.core () ; // Proceed beyond here once loop_timer ticker ISR has set loop_flag true
JonFreeman 12:d1d21a2941ef 528 tsc.core () ; // Proceed beyond here once loop_timer ticker ISR has set loop_flag true
JonFreeman 0:435bf84ce48a 529 AtoD_reader (); // Performs A to D conversions at rate set by ticker interrupts
JonFreeman 11:bfb73f083009 530 } // 32Hz original setting for loop repeat rate
JonFreeman 10:e40d8724268a 531 loop_flag = false; // Clear flag set by ticker interrupt handler. WHEN LAST CHECKED this was about every 32ms
JonFreeman 10:e40d8724268a 532
JonFreeman 16:d1e4b9ad3b8b 533 // double trim (const double min, const double max, double input) {
JonFreeman 16:d1e4b9ad3b8b 534 Current_Scaler = trim (0.1, 1.0, Read_BatteryVolts() / user_settings.Vnom());
JonFreeman 16:d1e4b9ad3b8b 535 MotorA.I_scale (Current_Scaler); // Reduces motor current limits when voltage below nominal.
JonFreeman 16:d1e4b9ad3b8b 536 MotorB.I_scale (Current_Scaler); // This goes some way towards preventing engine stalls perhaps
JonFreeman 16:d1e4b9ad3b8b 537
JonFreeman 16:d1e4b9ad3b8b 538 MotorA.speed_monitor_and_control (); // Needed to keep table updated to give reading in Hall transitions per second
JonFreeman 16:d1e4b9ad3b8b 539 MotorB.speed_monitor_and_control (); // Read MotorX.PPS to read pulses per sec or MotorX.RPM to read motor RPM
JonFreeman 16:d1e4b9ad3b8b 540
JonFreeman 16:d1e4b9ad3b8b 541 RC_chan_1.read(RCstick1); // Get latest info from Radio Control inputs
JonFreeman 16:d1e4b9ad3b8b 542 RC_chan_2.read(RCstick2);
JonFreeman 16:d1e4b9ad3b8b 543
JonFreeman 16:d1e4b9ad3b8b 544 //#define SERVO_OUT_TEST
JonFreeman 16:d1e4b9ad3b8b 545 #ifdef SERVO_OUT_TEST
JonFreeman 16:d1e4b9ad3b8b 546 if (RCstick1.active) Servo1 = RCstick1.deflection;
JonFreeman 16:d1e4b9ad3b8b 547 if (RCstick2.active) Servo2 = RCstick2.deflection;
JonFreeman 16:d1e4b9ad3b8b 548 #endif
JonFreeman 16:d1e4b9ad3b8b 549
JonFreeman 16:d1e4b9ad3b8b 550 switch (user_settings.rd(COMM_SRC)) { // Look to selected source of driving command, act on commands from wherever
JonFreeman 16:d1e4b9ad3b8b 551 // case 0: // Invalid
JonFreeman 16:d1e4b9ad3b8b 552 // break;
JonFreeman 16:d1e4b9ad3b8b 553 // case COM1: // COM1 - Primarily for testing, bypassed by command line interpreter
JonFreeman 16:d1e4b9ad3b8b 554 // break;
JonFreeman 16:d1e4b9ad3b8b 555 case COM2: // COM2 - Nothing done here, all serial instructions handled in command line interpreter
JonFreeman 8:93203f473f6e 556 break;
JonFreeman 11:bfb73f083009 557 case HAND: // Put all hand controller input stuff here
JonFreeman 16:d1e4b9ad3b8b 558 hand_control_state_machine (HAND);
JonFreeman 8:93203f473f6e 559 break; // endof hand controller stuff
JonFreeman 11:bfb73f083009 560 case RC_IN1: // RC_chan_1
JonFreeman 16:d1e4b9ad3b8b 561 RC_chan_1.energise (RCstick1, MotorA) ; // RC chan 1 drives both motors
JonFreeman 16:d1e4b9ad3b8b 562 RC_chan_1.energise (RCstick1, MotorB) ;
JonFreeman 8:93203f473f6e 563 break;
JonFreeman 11:bfb73f083009 564 case RC_IN2: // RC_chan_2
JonFreeman 16:d1e4b9ad3b8b 565 RC_chan_2.energise (RCstick2, MotorA) ; // RC chan 2 drives both motors
JonFreeman 16:d1e4b9ad3b8b 566 RC_chan_2.energise (RCstick2, MotorB) ;
JonFreeman 16:d1e4b9ad3b8b 567 break;
JonFreeman 16:d1e4b9ad3b8b 568 case RC_IN_BOTH: // Robot Mode
JonFreeman 16:d1e4b9ad3b8b 569 RC_chan_1.energise (RCstick1, MotorA) ; // Two RC chans drive two motors independently
JonFreeman 16:d1e4b9ad3b8b 570 RC_chan_2.energise (RCstick2, MotorB) ;
JonFreeman 8:93203f473f6e 571 break;
JonFreeman 8:93203f473f6e 572 default:
JonFreeman 12:d1d21a2941ef 573 if (ESC_Error.read(FAULT_UNRECOGNISED_STATE)) {
JonFreeman 16:d1e4b9ad3b8b 574 pc.printf ("Unrecognised state %d\r\n", user_settings.rd(COMM_SRC)); // set error flag instead here
JonFreeman 12:d1d21a2941ef 575 ESC_Error.set (FAULT_UNRECOGNISED_STATE, 1);
JonFreeman 12:d1d21a2941ef 576 }
JonFreeman 8:93203f473f6e 577 break;
JonFreeman 16:d1e4b9ad3b8b 578 } // endof switch (user_settings_bytes[COMM_SRC]) {
JonFreeman 12:d1d21a2941ef 579
JonFreeman 16:d1e4b9ad3b8b 580 //#define SERVO_OUT_TEST
JonFreeman 16:d1e4b9ad3b8b 581 //#ifdef SERVO_OUT_TEST
JonFreeman 15:2591e2008323 582 // static double servo_angle = 0.0; // For testing servo outs
JonFreeman 15:2591e2008323 583 // servo out test here December 2018
JonFreeman 15:2591e2008323 584 /*
JonFreeman 15:2591e2008323 585 servo_angle += 0.01;
JonFreeman 15:2591e2008323 586 if (servo_angle > TWOPI)
JonFreeman 15:2591e2008323 587 servo_angle -= TWOPI;
JonFreeman 15:2591e2008323 588 Servo1 = ((sin(servo_angle)+1.0) / 2.0);
JonFreeman 15:2591e2008323 589 Servo2 = ((cos(servo_angle)+1.0) / 2.0);
JonFreeman 15:2591e2008323 590 */
JonFreeman 15:2591e2008323 591 // Yep! Both servo outs work lovely December 2018
JonFreeman 16:d1e4b9ad3b8b 592 //#endif
JonFreeman 8:93203f473f6e 593
JonFreeman 0:435bf84ce48a 594 if (flag_8Hz) { // do slower stuff
JonFreeman 0:435bf84ce48a 595 flag_8Hz = false;
JonFreeman 16:d1e4b9ad3b8b 596 LED = !LED; // Toggle green LED on board, should be seen to fast flash
JonFreeman 8:93203f473f6e 597 if (WatchDogEnable) {
JonFreeman 8:93203f473f6e 598 WatchDog--;
JonFreeman 13:ef7a06fa11de 599 if (WatchDog < 1) { // Deal with WatchDog timer timeout here
JonFreeman 13:ef7a06fa11de 600 WatchDog = 0;
JonFreeman 16:d1e4b9ad3b8b 601 setVI_both (0.0, 0.0); // set motor volts and amps to zero
JonFreeman 16:d1e4b9ad3b8b 602 pc.printf ("TIMEOUT %c\r\n", user_settings.rd(BOARD_ID)); // Brute touch screen controller can do nothing with this
JonFreeman 8:93203f473f6e 603 } // End of dealing with WatchDog timer timeout
JonFreeman 8:93203f473f6e 604 }
JonFreeman 4:21d91465e4b1 605 eighth_sec_count++;
JonFreeman 4:21d91465e4b1 606 if (eighth_sec_count > 6) { // Send some status info out of serial port every second and a bit or thereabouts
JonFreeman 4:21d91465e4b1 607 eighth_sec_count = 0;
JonFreeman 12:d1d21a2941ef 608 ESC_Error.report_any (false); // retain = false - reset error after reporting once
JonFreeman 0:435bf84ce48a 609 }
JonFreeman 0:435bf84ce48a 610 } // End of if(flag_8Hz)
JonFreeman 0:435bf84ce48a 611 } // End of main programme loop
JonFreeman 0:435bf84ce48a 612 }
JonFreeman 11:bfb73f083009 613