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
STM3_ESC.h@17:cc9b854295d6, 2020-08-16 (annotated)
- 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?
User | Revision | Line number | New contents of line |
---|---|---|---|
JonFreeman | 14:acaa1add097b | 1 | /* |
JonFreeman | 14:acaa1add097b | 2 | STM3_ESC Electronic Speed Controller board, drives Two Brushless Motors, full Four Quadrant Control. |
JonFreeman | 14:acaa1add097b | 3 | Jon Freeman B. Eng Hons |
JonFreeman | 16:d1e4b9ad3b8b | 4 | 2015 - 2020 |
JonFreeman | 14:acaa1add097b | 5 | */ |
JonFreeman | 11:bfb73f083009 | 6 | #include "mbed.h" |
JonFreeman | 11:bfb73f083009 | 7 | |
JonFreeman | 11:bfb73f083009 | 8 | #ifndef MBED_DUALBLS_H |
JonFreeman | 11:bfb73f083009 | 9 | #define MBED_DUALBLS_H |
JonFreeman | 11:bfb73f083009 | 10 | |
JonFreeman | 16:d1e4b9ad3b8b | 11 | //#define USING_DC_MOTORS // NO LONGER SUPPORTED Uncomment this to play with Dinosaur DC motors - WARNING deprecated feature |
JonFreeman | 3:ecb00e0e8d68 | 12 | |
JonFreeman | 14:acaa1add097b | 13 | //#define TEMP_SENSOR_ENABLE // - WARNING deprecated feature, sensor chosen imposed heavy burden on cpu, future looks to simpler analogue type |
JonFreeman | 13:ef7a06fa11de | 14 | |
JonFreeman | 12:d1d21a2941ef | 15 | #include "BufferedSerial.h" |
JonFreeman | 16:d1e4b9ad3b8b | 16 | const uint32_t MOTOR_HANDBRAKE = 0, |
JonFreeman | 16:d1e4b9ad3b8b | 17 | MOTOR_FORWARD = 8, |
JonFreeman | 16:d1e4b9ad3b8b | 18 | MOTOR_REVERSE = 16, |
JonFreeman | 16:d1e4b9ad3b8b | 19 | MOTOR_REGENBRAKE = 24; |
JonFreeman | 12:d1d21a2941ef | 20 | |
JonFreeman | 16:d1e4b9ad3b8b | 21 | const uint32_t TIMEOUT_SECONDS = 2, |
JonFreeman | 5:ca86a7848d54 | 22 | |
JonFreeman | 5:ca86a7848d54 | 23 | /* Please Do Not Alter these */ |
JonFreeman | 16:d1e4b9ad3b8b | 24 | PWM_PRESECALER_DEFAULT = 2, |
JonFreeman | 8:93203f473f6e | 25 | VOLTAGE_READ_INTERVAL_US = 50, // Interrupts timed every 50 micro sec, runs around loop performing 1 A-D conversion per pass |
JonFreeman | 5:ca86a7848d54 | 26 | MAIN_LOOP_REPEAT_TIME_US = 31250, // 31250 us, with TACHO_TAB_SIZE = 32 means tacho_ticks_per_time is tacho_ticks_per_second |
JonFreeman | 5:ca86a7848d54 | 27 | MAIN_LOOP_ITERATION_Hz = 1000000 / MAIN_LOOP_REPEAT_TIME_US, |
JonFreeman | 8:93203f473f6e | 28 | PWM_HZ = 15000, // chosen to be above cutoff frequency of average human ear |
JonFreeman | 12:d1d21a2941ef | 29 | // PWM_HZ = 8000, // chosen to be above cutoff frequency of average human ear - clearly audible annoying noise |
JonFreeman | 8:93203f473f6e | 30 | MAX_PWM_TICKS = (SystemCoreClock / (PWM_HZ * PWM_PRESECALER_DEFAULT)), |
JonFreeman | 16:d1e4b9ad3b8b | 31 | // TICKLE_TIMES = 100 , |
JonFreeman | 16:d1e4b9ad3b8b | 32 | TICKLE_TIMES = 10 , // Massively reduced May 2020 in connection with handbrake implementation. |
JonFreeman | 5:ca86a7848d54 | 33 | WATCHDOG_RELOAD = (TIMEOUT_SECONDS * 8); // WatchDog counter ticked down in 8Hz loop |
JonFreeman | 5:ca86a7848d54 | 34 | |
JonFreeman | 3:ecb00e0e8d68 | 35 | const double PI = 4.0 * atan(1.0), |
JonFreeman | 3:ecb00e0e8d68 | 36 | TWOPI = 8.0 * atan(1.0); |
JonFreeman | 16:d1e4b9ad3b8b | 37 | /* End of Please Do Not Alter these */ |
JonFreeman | 5:ca86a7848d54 | 38 | |
JonFreeman | 16:d1e4b9ad3b8b | 39 | enum {COM_SOURCES, COM1, COM2, HAND, RC_IN1, RC_IN2, RC_IN_BOTH, THEEND} ; // RC_IN_BOTH new Dec 2019 |
JonFreeman | 11:bfb73f083009 | 40 | |
JonFreeman | 16:d1e4b9ad3b8b | 41 | // List user settable firmware bytes in EEROM |
JonFreeman | 16:d1e4b9ad3b8b | 42 | enum {MOTADIR, MOTBDIR, MOTAPOLES, MOTBPOLES, ISHUNTA, ISHUNTB, POT_REGBRAKE_RANGE, SVO1, SVO2, RCIN1, RCIN2, |
JonFreeman | 12:d1d21a2941ef | 43 | COMM_SRC, BOARD_ID, TOP_SPEED, WHEELDIA, MOTPIN, WHEELGEAR, |
JonFreeman | 16:d1e4b9ad3b8b | 44 | RCI1_TRIM, RCI2_TRIM, RCIN_REGBRAKE_RANGE, RCIN_STICK_ATTACK, // RC in trims new Dec 2019 |
JonFreeman | 16:d1e4b9ad3b8b | 45 | RCIN1REVERSE, RCIN2REVERSE, NOM_SYSTEM_VOLTS, BRAKE_EFFECTIVENESS, BAUD, FUT11, |
JonFreeman | 16:d1e4b9ad3b8b | 46 | FUT12, FUT13, FUT14, FUT16,} ; // These represent byte address offsets in 24LC64 rom user settable firmware settings |
JonFreeman | 5:ca86a7848d54 | 47 | |
JonFreeman | 16:d1e4b9ad3b8b | 48 | enum { // List of fault numbers currently dealt with by error handler |
JonFreeman | 12:d1d21a2941ef | 49 | FAULT_0, |
JonFreeman | 12:d1d21a2941ef | 50 | FAULT_EEPROM, |
JonFreeman | 12:d1d21a2941ef | 51 | FAULT_BOARD_ID, |
JonFreeman | 12:d1d21a2941ef | 52 | FAULT_COM_LINE_LEN, |
JonFreeman | 12:d1d21a2941ef | 53 | FAULT_COM_LINE_NOMATCH, |
JonFreeman | 12:d1d21a2941ef | 54 | FAULT_COM_LINE_LEN_PC, |
JonFreeman | 12:d1d21a2941ef | 55 | FAULT_COM_LINE_LEN_TS, |
JonFreeman | 12:d1d21a2941ef | 56 | FAULT_COM_LINE_NOMATCH_PC, |
JonFreeman | 12:d1d21a2941ef | 57 | FAULT_COM_LINE_NOMATCH_TS, |
JonFreeman | 12:d1d21a2941ef | 58 | FAULT_UNRECOGNISED_STATE, |
JonFreeman | 12:d1d21a2941ef | 59 | FAULT_MAX, |
JonFreeman | 12:d1d21a2941ef | 60 | NUMOF_REPORTABLE_TS_ERRORS |
JonFreeman | 14:acaa1add097b | 61 | } ; // List of fault numbers currently dealt with by error handler |
JonFreeman | 12:d1d21a2941ef | 62 | |
JonFreeman | 12:d1d21a2941ef | 63 | class error_handling_Jan_2019 |
JonFreeman | 12:d1d21a2941ef | 64 | { |
JonFreeman | 12:d1d21a2941ef | 65 | int32_t ESC_fault[NUMOF_REPORTABLE_TS_ERRORS] ; // Some number of reportable error codes, accessible through set and read members |
JonFreeman | 12:d1d21a2941ef | 66 | public: |
JonFreeman | 12:d1d21a2941ef | 67 | error_handling_Jan_2019 () { // default constructor |
JonFreeman | 12:d1d21a2941ef | 68 | for (int i = 0; i < (sizeof(ESC_fault) / sizeof(int32_t)); i++) |
JonFreeman | 12:d1d21a2941ef | 69 | ESC_fault[i] = 0; |
JonFreeman | 12:d1d21a2941ef | 70 | } |
JonFreeman | 12:d1d21a2941ef | 71 | void set (uint32_t, int32_t) ; |
JonFreeman | 12:d1d21a2941ef | 72 | void clr (uint32_t) ; |
JonFreeman | 12:d1d21a2941ef | 73 | uint32_t read (uint32_t) ; |
JonFreeman | 12:d1d21a2941ef | 74 | bool all_good () ; |
JonFreeman | 12:d1d21a2941ef | 75 | void report_any (bool) ; // retain ? true or false |
JonFreeman | 6:f289a49c1eae | 76 | } ; |
JonFreeman | 10:e40d8724268a | 77 | |
JonFreeman | 12:d1d21a2941ef | 78 | enum {SOURCE_PC, SOURCE_TS} ; |
JonFreeman | 16:d1e4b9ad3b8b | 79 | const int BROADCAST = '\r'; |
JonFreeman | 16:d1e4b9ad3b8b | 80 | const int MAX_CLI_PARAMS = 12; |
JonFreeman | 12:d1d21a2941ef | 81 | const int MAX_CMD_LEN = 220; |
JonFreeman | 12:d1d21a2941ef | 82 | |
JonFreeman | 14:acaa1add097b | 83 | struct parameters { // Used in serial comms with pc and other controller (e.g. touch-screen) |
JonFreeman | 12:d1d21a2941ef | 84 | struct kb_command const * command_list; |
JonFreeman | 12:d1d21a2941ef | 85 | BufferedSerial * com; // pc or com2 |
JonFreeman | 12:d1d21a2941ef | 86 | int32_t position_in_list, numof_dbls, target_unit, source, numof_menu_items; |
JonFreeman | 16:d1e4b9ad3b8b | 87 | double dbl[MAX_CLI_PARAMS]; |
JonFreeman | 12:d1d21a2941ef | 88 | bool respond, resp_always; |
JonFreeman | 12:d1d21a2941ef | 89 | } ; |
JonFreeman | 12:d1d21a2941ef | 90 | |
JonFreeman | 16:d1e4b9ad3b8b | 91 | enum {ZONE_BRAKE, ZONE_COAST, ZONE_DRIVE} ; |
JonFreeman | 16:d1e4b9ad3b8b | 92 | class RC_stick_info { // info read concerning stick positions |
JonFreeman | 16:d1e4b9ad3b8b | 93 | public: |
JonFreeman | 16:d1e4b9ad3b8b | 94 | double raw, // range 0.0 to 1.0 after clipping and correction |
JonFreeman | 16:d1e4b9ad3b8b | 95 | deflection; // how far from centre or min positon |
JonFreeman | 16:d1e4b9ad3b8b | 96 | double brake_effort, // braking effort |
JonFreeman | 16:d1e4b9ad3b8b | 97 | drive_effort; // driving effort |
JonFreeman | 16:d1e4b9ad3b8b | 98 | uint32_t zone; // in drive, coast or braking zone (if drive, direction in 'stick_implied_motor_direction') |
JonFreeman | 16:d1e4b9ad3b8b | 99 | uint32_t chan_mode; |
JonFreeman | 16:d1e4b9ad3b8b | 100 | int32_t stick_implied_motor_direction; // -1, 0, +1 but could be in drive, coast or brake regions |
JonFreeman | 16:d1e4b9ad3b8b | 101 | bool active; |
JonFreeman | 16:d1e4b9ad3b8b | 102 | RC_stick_info () { |
JonFreeman | 16:d1e4b9ad3b8b | 103 | active = false; |
JonFreeman | 16:d1e4b9ad3b8b | 104 | chan_mode = 0; |
JonFreeman | 16:d1e4b9ad3b8b | 105 | zone = ZONE_COAST; |
JonFreeman | 16:d1e4b9ad3b8b | 106 | drive_effort = brake_effort = 0.0; |
JonFreeman | 16:d1e4b9ad3b8b | 107 | } |
JonFreeman | 16:d1e4b9ad3b8b | 108 | } ; |
JonFreeman | 16:d1e4b9ad3b8b | 109 | |
JonFreeman | 14:acaa1add097b | 110 | class cli_2019 { // cli Command Line Interpreter, two off, 1 for pc comms, other touch-screen controller comms |
JonFreeman | 12:d1d21a2941ef | 111 | struct kb_command const * commandlist ; |
JonFreeman | 16:d1e4b9ad3b8b | 112 | uint32_t clindex; |
JonFreeman | 16:d1e4b9ad3b8b | 113 | char cmdline[MAX_CMD_LEN + 8]; |
JonFreeman | 16:d1e4b9ad3b8b | 114 | char * cmdline_ptr; |
JonFreeman | 12:d1d21a2941ef | 115 | parameters a ; |
JonFreeman | 12:d1d21a2941ef | 116 | public: |
JonFreeman | 16:d1e4b9ad3b8b | 117 | cli_2019 (BufferedSerial * comport, kb_command const * list, uint32_t list_len, uint32_t source) { |
JonFreeman | 12:d1d21a2941ef | 118 | a.com = comport ; |
JonFreeman | 12:d1d21a2941ef | 119 | a.command_list = commandlist = list ; |
JonFreeman | 12:d1d21a2941ef | 120 | a.numof_menu_items = list_len ; |
JonFreeman | 12:d1d21a2941ef | 121 | a.source = source ; |
JonFreeman | 12:d1d21a2941ef | 122 | cmdline_ptr = cmdline; |
JonFreeman | 12:d1d21a2941ef | 123 | clindex = 0; |
JonFreeman | 12:d1d21a2941ef | 124 | if (source == SOURCE_PC) |
JonFreeman | 12:d1d21a2941ef | 125 | a.resp_always = true; |
JonFreeman | 12:d1d21a2941ef | 126 | else |
JonFreeman | 12:d1d21a2941ef | 127 | a.resp_always = false; |
JonFreeman | 12:d1d21a2941ef | 128 | } ; |
JonFreeman | 16:d1e4b9ad3b8b | 129 | void core () ; |
JonFreeman | 16:d1e4b9ad3b8b | 130 | void flush () ; // Used to clear startup transient garbage |
JonFreeman | 12:d1d21a2941ef | 131 | } ; |
JonFreeman | 12:d1d21a2941ef | 132 | |
JonFreeman | 16:d1e4b9ad3b8b | 133 | const uint32_t MAX_I2C_DEVICES = 6; |
JonFreeman | 16:d1e4b9ad3b8b | 134 | |
JonFreeman | 12:d1d21a2941ef | 135 | class eeprom_settings { |
JonFreeman | 12:d1d21a2941ef | 136 | I2C i2c; |
JonFreeman | 12:d1d21a2941ef | 137 | uint32_t errors; |
JonFreeman | 14:acaa1add097b | 138 | uint32_t i2c_device_count; |
JonFreeman | 16:d1e4b9ad3b8b | 139 | uint32_t i2c_device_list[MAX_I2C_DEVICES+1]; // max 12 i2c devices |
JonFreeman | 12:d1d21a2941ef | 140 | char settings [36]; |
JonFreeman | 16:d1e4b9ad3b8b | 141 | double rpm2mphdbl, user_brake_rangedbl, Vnomdbl, brake_eff, top_speeddbl; |
JonFreeman | 16:d1e4b9ad3b8b | 142 | bool rd_24LC64 (uint32_t start_addr, char * dest, uint32_t length) ; |
JonFreeman | 16:d1e4b9ad3b8b | 143 | bool wr_24LC64 (uint32_t start_addr, char * dest, uint32_t length) ; |
JonFreeman | 12:d1d21a2941ef | 144 | bool set_24LC64_internal_address (int start_addr) ; |
JonFreeman | 12:d1d21a2941ef | 145 | bool ack_poll () ; |
JonFreeman | 16:d1e4b9ad3b8b | 146 | void update_dbls () ; |
JonFreeman | 12:d1d21a2941ef | 147 | public: |
JonFreeman | 12:d1d21a2941ef | 148 | eeprom_settings (PinName sda, PinName scl); // Constructor |
JonFreeman | 14:acaa1add097b | 149 | bool do_we_have_i2c (uint32_t x) ; |
JonFreeman | 12:d1d21a2941ef | 150 | char rd (uint32_t) ; // Read one setup char value from private buffer 'settings' |
JonFreeman | 12:d1d21a2941ef | 151 | bool wr (char, uint32_t) ; // Write one setup char value to private buffer 'settings' |
JonFreeman | 12:d1d21a2941ef | 152 | bool save () ; // Write 'settings' buffer to EEPROM |
JonFreeman | 12:d1d21a2941ef | 153 | bool set_defaults (); // Put default settings into EEPROM and local buffer |
JonFreeman | 12:d1d21a2941ef | 154 | uint32_t errs () ; // Return errors |
JonFreeman | 16:d1e4b9ad3b8b | 155 | const char * t (uint32_t); |
JonFreeman | 16:d1e4b9ad3b8b | 156 | uint32_t min (uint32_t) ; |
JonFreeman | 16:d1e4b9ad3b8b | 157 | uint32_t max (uint32_t) ; |
JonFreeman | 16:d1e4b9ad3b8b | 158 | uint32_t def (uint32_t) ; |
JonFreeman | 16:d1e4b9ad3b8b | 159 | bool in_range (char val, uint32_t p) ; |
JonFreeman | 16:d1e4b9ad3b8b | 160 | void edit (double * dbl, uint32_t numof_dbls) ; |
JonFreeman | 16:d1e4b9ad3b8b | 161 | double user_brake_range () ; |
JonFreeman | 16:d1e4b9ad3b8b | 162 | double brake_effectiveness () ; |
JonFreeman | 16:d1e4b9ad3b8b | 163 | double top_speed () ; |
JonFreeman | 16:d1e4b9ad3b8b | 164 | double Vnom () ; |
JonFreeman | 16:d1e4b9ad3b8b | 165 | double rpm2mph () ; |
JonFreeman | 16:d1e4b9ad3b8b | 166 | double rpm2mph (double) ; |
JonFreeman | 16:d1e4b9ad3b8b | 167 | uint32_t baud (); |
JonFreeman | 12:d1d21a2941ef | 168 | } ; |
JonFreeman | 12:d1d21a2941ef | 169 | |
JonFreeman | 16:d1e4b9ad3b8b | 170 | struct optpar { |
JonFreeman | 16:d1e4b9ad3b8b | 171 | int32_t min, max, de_fault; // min, max, default |
JonFreeman | 16:d1e4b9ad3b8b | 172 | const char * txt; // description |
JonFreeman | 16:d1e4b9ad3b8b | 173 | } ; |
JonFreeman | 12:d1d21a2941ef | 174 | |
JonFreeman | 11:bfb73f083009 | 175 | #endif |