STM3 ESC dual brushless motor controller. 10-60v, motor power rating tiny to kW. Ganged or independent motor control As used in 'The Brute' locomotive - www.jons-workshop.com
Dependencies: mbed BufferedSerial Servo FastPWM
main.cpp@12:d1d21a2941ef, 2019-03-04 (annotated)
- Committer:
- JonFreeman
- Date:
- Mon Mar 04 17:51:08 2019 +0000
- Revision:
- 12:d1d21a2941ef
- Parent:
- 11:bfb73f083009
STM3 ESC dual motor controller boards. Always 'Work In Progress', working snapshot March 2019
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
JonFreeman | 10:e40d8724268a | 1 | // Cloned from 'DualBLS2018_06' on 23 November 2018 |
JonFreeman | 0:435bf84ce48a | 2 | #include "mbed.h" |
JonFreeman | 8:93203f473f6e | 3 | //#include "users/mbed_official/code/mbed-dev/file/707f6e361f3e/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F401xE/device/stm32f401xe.h" |
JonFreeman | 11:bfb73f083009 | 4 | #include "DualBLS.h" |
JonFreeman | 11:bfb73f083009 | 5 | |
JonFreeman | 8:93203f473f6e | 6 | #include "stm32f401xe.h" |
JonFreeman | 0:435bf84ce48a | 7 | #include "BufferedSerial.h" |
JonFreeman | 0:435bf84ce48a | 8 | #include "FastPWM.h" |
JonFreeman | 4:21d91465e4b1 | 9 | #include "Servo.h" |
JonFreeman | 10:e40d8724268a | 10 | #include "brushless_motor.h" |
JonFreeman | 11:bfb73f083009 | 11 | #include "Radio_Control_In.h" |
JonFreeman | 5:ca86a7848d54 | 12 | |
JonFreeman | 5:ca86a7848d54 | 13 | /* |
JonFreeman | 10:e40d8724268a | 14 | Brushless_STM3 board |
JonFreeman | 12:d1d21a2941ef | 15 | Jan 2019 * WatchDog implemented. Default is disabled, 'kd' command from TS controller enables and reloads |
JonFreeman | 12:d1d21a2941ef | 16 | * Tidied brushless_motor class, parameter passing now done properly |
JonFreeman | 12:d1d21a2941ef | 17 | * class RControl_In created. Inputs now routed to new pins, can now use two chans both class RControl_In and Servo out |
JonFreeman | 12:d1d21a2941ef | 18 | (buggery board required for new inputs) |
JonFreeman | 12:d1d21a2941ef | 19 | * Added version string |
JonFreeman | 12:d1d21a2941ef | 20 | * Error handler written and included |
JonFreeman | 12:d1d21a2941ef | 21 | * 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 | 22 | * Reorganised EEPROM data - mode setting now much easier, less error prone |
JonFreeman | 12:d1d21a2941ef | 23 | * Maximum speed now one EEPROM option, range 1.0 to 25.0 MPH |
JonFreeman | 12:d1d21a2941ef | 24 | |
JonFreeman | 10:e40d8724268a | 25 | New 29th May 2018 ** |
JonFreeman | 10:e40d8724268a | 26 | LMT01 temperature sensor routed to T1 - and rerouted to PC_13 as InterruptIn on T1 (ports A and B I think) not workable |
JonFreeman | 5:ca86a7848d54 | 27 | */ |
JonFreeman | 5:ca86a7848d54 | 28 | |
JonFreeman | 5:ca86a7848d54 | 29 | |
JonFreeman | 0:435bf84ce48a | 30 | /* STM32F401RE - compile using NUCLEO-F401RE |
JonFreeman | 12:d1d21a2941ef | 31 | // PROJECT - STM3_ESC Dual Brushless Motor Controller - Jon Freeman June 2018. |
JonFreeman | 0:435bf84ce48a | 32 | |
JonFreeman | 0:435bf84ce48a | 33 | AnalogIn to read each motor current |
JonFreeman | 0:435bf84ce48a | 34 | |
JonFreeman | 0:435bf84ce48a | 35 | ____________________________________________________________________________________ |
JonFreeman | 0:435bf84ce48a | 36 | CONTROL PHILOSOPHY |
JonFreeman | 0:435bf84ce48a | 37 | This Bogie drive board software should ensure sensible control when commands supplied are not sensible ! |
JonFreeman | 0:435bf84ce48a | 38 | |
JonFreeman | 0:435bf84ce48a | 39 | That is, smooth transition between Drive, Coast and Brake to be implemented here. |
JonFreeman | 0:435bf84ce48a | 40 | The remote controller must not be relied upon to deliver sensible command sequences. |
JonFreeman | 0:435bf84ce48a | 41 | |
JonFreeman | 0:435bf84ce48a | 42 | So much the better if the remote controller does issue sensible commands, but ... |
JonFreeman | 0:435bf84ce48a | 43 | |
JonFreeman | 0:435bf84ce48a | 44 | ____________________________________________________________________________________ |
JonFreeman | 0:435bf84ce48a | 45 | |
JonFreeman | 0:435bf84ce48a | 46 | |
JonFreeman | 0:435bf84ce48a | 47 | */ |
JonFreeman | 8:93203f473f6e | 48 | |
JonFreeman | 7:6deaeace9a3e | 49 | #if defined (TARGET_NUCLEO_F401RE) // CPU in 64 pin LQFP |
JonFreeman | 9:ac2412df01be | 50 | #include "F401RE.h" // See here for warnings about Servo InterruptIn not working |
JonFreeman | 6:f289a49c1eae | 51 | #endif |
JonFreeman | 7:6deaeace9a3e | 52 | #if defined (TARGET_NUCLEO_F446ZE) // CPU in 144 pin LQFP |
JonFreeman | 10:e40d8724268a | 53 | #include "F446ZE.h" // A thought for future version |
JonFreeman | 6:f289a49c1eae | 54 | #endif |
JonFreeman | 0:435bf84ce48a | 55 | /* Global variable declarations */ |
JonFreeman | 12:d1d21a2941ef | 56 | char const const_version_string[] = {"1.0.0\0"}; // Version string, readable from serial ports |
JonFreeman | 3:ecb00e0e8d68 | 57 | volatile uint32_t fast_sys_timer = 0; // gets incremented by our Ticker ISR every VOLTAGE_READ_INTERVAL_US |
JonFreeman | 5:ca86a7848d54 | 58 | int WatchDog = WATCHDOG_RELOAD + 80; // Allow extra few seconds at powerup |
JonFreeman | 8:93203f473f6e | 59 | bool WatchDogEnable = false; // Must recieve explicit instruction from pc or controller to enable |
JonFreeman | 0:435bf84ce48a | 60 | uint32_t volt_reading = 0, // Global updated by interrupt driven read of Battery Volts |
JonFreeman | 0:435bf84ce48a | 61 | driverpot_reading = 0, // Global updated by interrupt driven read of Drivers Pot |
JonFreeman | 0:435bf84ce48a | 62 | sys_timer = 0, // gets incremented by our Ticker ISR every MAIN_LOOP_REPEAT_TIME_US |
JonFreeman | 5:ca86a7848d54 | 63 | AtoD_Semaphore = 0; |
JonFreeman | 12:d1d21a2941ef | 64 | |
JonFreeman | 11:bfb73f083009 | 65 | bool loop_flag = false; // made true in ISR_loop_timer, picked up and made false again in main programme loop |
JonFreeman | 11:bfb73f083009 | 66 | bool flag_8Hz = false; // As loop_flag but repeats 8 times per sec |
JonFreeman | 6:f289a49c1eae | 67 | bool temp_sensor_exists = false; |
JonFreeman | 12:d1d21a2941ef | 68 | double rpm2mph; |
JonFreeman | 3:ecb00e0e8d68 | 69 | |
JonFreeman | 6:f289a49c1eae | 70 | uint32_t temp_sensor_count = 0, // incremented by every rising edge from LMT01 |
JonFreeman | 12:d1d21a2941ef | 71 | last_temperature_count = 0; // global updated approx every 100ms after each LMT01 conversion completes |
JonFreeman | 0:435bf84ce48a | 72 | /* End of Global variable declarations */ |
JonFreeman | 0:435bf84ce48a | 73 | |
JonFreeman | 0:435bf84ce48a | 74 | Ticker tick_vread; // Device to cause periodic interrupts, used to time voltage readings etc |
JonFreeman | 0:435bf84ce48a | 75 | Ticker loop_timer; // Device to cause periodic interrupts, used to sync iterations of main programme loop |
JonFreeman | 6:f289a49c1eae | 76 | Ticker temperature_find_ticker; |
JonFreeman | 6:f289a49c1eae | 77 | Timer temperature_timer; |
JonFreeman | 12:d1d21a2941ef | 78 | |
JonFreeman | 12:d1d21a2941ef | 79 | #ifdef USING_DC_MOTORS |
JonFreeman | 8:93203f473f6e | 80 | Timer dc_motor_kicker_timer; |
JonFreeman | 8:93203f473f6e | 81 | Timeout motors_restore; |
JonFreeman | 12:d1d21a2941ef | 82 | #endif |
JonFreeman | 12:d1d21a2941ef | 83 | |
JonFreeman | 11:bfb73f083009 | 84 | RControl_In RC_chan_1 (PC_14); |
JonFreeman | 11:bfb73f083009 | 85 | RControl_In RC_chan_2 (PC_15); // Instantiate two radio control input channels and specify which InterruptIn pin |
JonFreeman | 12:d1d21a2941ef | 86 | error_handling_Jan_2019 ESC_Error ; // Provides array usable to store error codes. |
JonFreeman | 0:435bf84ce48a | 87 | |
JonFreeman | 12:d1d21a2941ef | 88 | eeprom_settings mode (SDA_PIN, SCL_PIN) ; // This MUST come before Motors setup |
JonFreeman | 4:21d91465e4b1 | 89 | |
JonFreeman | 0:435bf84ce48a | 90 | //uint32_t HAtot = 0, HBtot = 0, A_Offset = 0, B_Offset = 0; |
JonFreeman | 0:435bf84ce48a | 91 | /* |
JonFreeman | 0:435bf84ce48a | 92 | 5 1 3 2 6 4 SENSOR SEQUENCE |
JonFreeman | 0:435bf84ce48a | 93 | |
JonFreeman | 3:ecb00e0e8d68 | 94 | 1 1 1 1 0 0 0 ---___---___ Hall1 |
JonFreeman | 3:ecb00e0e8d68 | 95 | 2 0 0 1 1 1 0 __---___---_ Hall2 |
JonFreeman | 3:ecb00e0e8d68 | 96 | 4 1 0 0 0 1 1 -___---___-- Hall3 |
JonFreeman | 0:435bf84ce48a | 97 | |
JonFreeman | 0:435bf84ce48a | 98 | UV WV WU VU VW UW OUTPUT SEQUENCE |
JonFreeman | 8:93203f473f6e | 99 | |
JonFreeman | 8:93203f473f6e | 100 | 8th July 2018 |
JonFreeman | 8:93203f473f6e | 101 | Added drive to DC brushed motors. |
JonFreeman | 8:93203f473f6e | 102 | Connect U and W to dc motor, leave V open. |
JonFreeman | 8:93203f473f6e | 103 | |
JonFreeman | 8:93203f473f6e | 104 | Hall codes 0 and 7 not used for brushless motors. Without Hall sensors connected pullup resistors give code 7. Use this for dc motors |
JonFreeman | 8:93203f473f6e | 105 | |
JonFreeman | 0:435bf84ce48a | 106 | */ |
JonFreeman | 3:ecb00e0e8d68 | 107 | const uint16_t A_tabl[] = { // Origial table |
JonFreeman | 0:435bf84ce48a | 108 | 0, 0, 0, 0, 0, 0, 0, 0, // Handbrake |
JonFreeman | 10:e40d8724268a | 109 | 0, AWHVL,AVHUL,AWHUL,AUHWL,AUHVL,AVHWL,AUHWL, // Forward 0, WV1, VU1, WU1, UW1, UV1, VW1, 0, // JP, FR, SG, PWM = 1 0 1 1 Forward1 |
JonFreeman | 10:e40d8724268a | 110 | 0, AVHWL,AUHVL,AUHWL,AWHUL,AVHUL,AWHVL,AWHUL, // Reverse 0, VW1, UV1, UW1, WU1, VU1, WV1, 0, // JP, FR, SG, PWM = 1 1 0 1 Reverse1 |
JonFreeman | 8:93203f473f6e | 111 | 0, BRA,BRA,BRA,BRA,BRA,BRA,BRA, // Regenerative Braking |
JonFreeman | 4:21d91465e4b1 | 112 | KEEP_L_MASK_A, KEEP_H_MASK_A // [32 and 33] |
JonFreeman | 3:ecb00e0e8d68 | 113 | } ; |
JonFreeman | 11:bfb73f083009 | 114 | |
JonFreeman | 0:435bf84ce48a | 115 | const uint16_t B_tabl[] = { |
JonFreeman | 0:435bf84ce48a | 116 | 0, 0, 0, 0, 0, 0, 0, 0, // Handbrake |
JonFreeman | 10:e40d8724268a | 117 | 0, BWHVL,BVHUL,BWHUL,BUHWL,BUHVL,BVHWL,BUHWL, // Forward 0, WV1, VU1, WU1, UW1, UV1, VW1, 0, // JP, FR, SG, PWM = 1 0 1 1 Forward1 |
JonFreeman | 10:e40d8724268a | 118 | 0, BVHWL,BUHVL,BUHWL,BWHUL,BVHUL,BWHVL,BWHUL, // Reverse 0, VW1, UV1, UW1, WU1, VU1, WV1, 0, // JP, FR, SG, PWM = 1 1 0 1 Reverse1 |
JonFreeman | 8:93203f473f6e | 119 | 0, BRB,BRB,BRB,BRB,BRB,BRB,BRB, // Regenerative Braking |
JonFreeman | 4:21d91465e4b1 | 120 | KEEP_L_MASK_B, KEEP_H_MASK_B |
JonFreeman | 3:ecb00e0e8d68 | 121 | } ; |
JonFreeman | 0:435bf84ce48a | 122 | |
JonFreeman | 12:d1d21a2941ef | 123 | brushless_motor MotorA (MOT_A_I_ADC, APWMV, APWMI, A_tabl, _MAH1, _MAH2, _MAH3, PortA, PORT_A_MASK, ISHUNTA); |
JonFreeman | 12:d1d21a2941ef | 124 | brushless_motor MotorB (MOT_B_I_ADC, BPWMV, BPWMI, B_tabl, _MBH1, _MBH2, _MBH3, PortB, PORT_B_MASK, ISHUNTB); |
JonFreeman | 2:04761b196473 | 125 | |
JonFreeman | 5:ca86a7848d54 | 126 | |
JonFreeman | 12:d1d21a2941ef | 127 | extern cli_2019 pcc, tsc; // command line interpreters from pc and touch screen |
JonFreeman | 12:d1d21a2941ef | 128 | |
JonFreeman | 12:d1d21a2941ef | 129 | // Interrupt Service Routines |
JonFreeman | 12:d1d21a2941ef | 130 | void ISR_temperature_find_ticker () // every 960 us, i.e. slightly faster than once per milli sec |
JonFreeman | 12:d1d21a2941ef | 131 | { |
JonFreeman | 12:d1d21a2941ef | 132 | static bool readflag = false; |
JonFreeman | 12:d1d21a2941ef | 133 | int t = temperature_timer.read_ms (); |
JonFreeman | 12:d1d21a2941ef | 134 | if ((t == 5) && (!readflag)) { |
JonFreeman | 12:d1d21a2941ef | 135 | last_temperature_count = temp_sensor_count; |
JonFreeman | 12:d1d21a2941ef | 136 | temp_sensor_count = 0; |
JonFreeman | 12:d1d21a2941ef | 137 | readflag = true; |
JonFreeman | 12:d1d21a2941ef | 138 | } |
JonFreeman | 12:d1d21a2941ef | 139 | if (t == 6) |
JonFreeman | 12:d1d21a2941ef | 140 | readflag = false; |
JonFreeman | 12:d1d21a2941ef | 141 | } |
JonFreeman | 12:d1d21a2941ef | 142 | |
JonFreeman | 12:d1d21a2941ef | 143 | /** void ISR_loop_timer () |
JonFreeman | 12:d1d21a2941ef | 144 | * This ISR responds to Ticker interrupts at a rate of (probably) 32 times per second (check from constant declarations above) |
JonFreeman | 12:d1d21a2941ef | 145 | * This ISR sets global flag 'loop_flag' used to synchronise passes around main programme control loop. |
JonFreeman | 12:d1d21a2941ef | 146 | * Increments global 'sys_timer', usable anywhere as general measure of elapsed time |
JonFreeman | 12:d1d21a2941ef | 147 | */ |
JonFreeman | 12:d1d21a2941ef | 148 | void ISR_loop_timer () // This is Ticker Interrupt Service Routine - loop timer - MAIN_LOOP_REPEAT_TIME_US |
JonFreeman | 8:93203f473f6e | 149 | { |
JonFreeman | 12:d1d21a2941ef | 150 | loop_flag = true; // set flag to allow main programme loop to proceed |
JonFreeman | 12:d1d21a2941ef | 151 | sys_timer++; // Just a handy measure of elapsed time for anything to use |
JonFreeman | 12:d1d21a2941ef | 152 | if ((sys_timer & 0x03) == 0) |
JonFreeman | 12:d1d21a2941ef | 153 | flag_8Hz = true; |
JonFreeman | 8:93203f473f6e | 154 | } |
JonFreeman | 8:93203f473f6e | 155 | |
JonFreeman | 12:d1d21a2941ef | 156 | /** void ISR_voltage_reader () |
JonFreeman | 12:d1d21a2941ef | 157 | * This ISR responds to Ticker interrupts every 'VOLTAGE_READ_INTERVAL_US' micro seconds |
JonFreeman | 12:d1d21a2941ef | 158 | * |
JonFreeman | 12:d1d21a2941ef | 159 | * AtoD_reader() called from convenient point in code to take readings outside of ISRs |
JonFreeman | 12:d1d21a2941ef | 160 | */ |
JonFreeman | 12:d1d21a2941ef | 161 | void ISR_voltage_reader () // This is Ticker Interrupt Service Routine ; few us between readings ; VOLTAGE_READ_INTERVAL_US = 50 |
JonFreeman | 12:d1d21a2941ef | 162 | { |
JonFreeman | 12:d1d21a2941ef | 163 | AtoD_Semaphore++; |
JonFreeman | 12:d1d21a2941ef | 164 | fast_sys_timer++; // Just a handy measure of elapsed time for anything to use |
JonFreeman | 12:d1d21a2941ef | 165 | } |
JonFreeman | 12:d1d21a2941ef | 166 | |
JonFreeman | 12:d1d21a2941ef | 167 | void temp_sensor_isr () // got rising edge from LMT01. ALMOST CERTAIN this misses some |
JonFreeman | 12:d1d21a2941ef | 168 | { |
JonFreeman | 12:d1d21a2941ef | 169 | int t = temperature_timer.read_us (); // Must be being overrun by something, most likely culprit A-D reading ? |
JonFreeman | 12:d1d21a2941ef | 170 | temperature_timer.reset (); |
JonFreeman | 12:d1d21a2941ef | 171 | temp_sensor_count++; |
JonFreeman | 12:d1d21a2941ef | 172 | if (t > 18) // Yes proved some interrupts get missed, this fixes temperature reading |
JonFreeman | 12:d1d21a2941ef | 173 | temp_sensor_count++; |
JonFreeman | 12:d1d21a2941ef | 174 | // T2 = !T2; // scope hanger |
JonFreeman | 12:d1d21a2941ef | 175 | } |
JonFreeman | 12:d1d21a2941ef | 176 | |
JonFreeman | 12:d1d21a2941ef | 177 | // End of Interrupt Service Routines |
JonFreeman | 12:d1d21a2941ef | 178 | |
JonFreeman | 12:d1d21a2941ef | 179 | |
JonFreeman | 8:93203f473f6e | 180 | void setVI (double v, double i) |
JonFreeman | 8:93203f473f6e | 181 | { |
JonFreeman | 4:21d91465e4b1 | 182 | MotorA.set_V_limit (v); // Sets max motor voltage |
JonFreeman | 4:21d91465e4b1 | 183 | MotorA.set_I_limit (i); // Sets max motor current |
JonFreeman | 4:21d91465e4b1 | 184 | MotorB.set_V_limit (v); |
JonFreeman | 4:21d91465e4b1 | 185 | MotorB.set_I_limit (i); |
JonFreeman | 4:21d91465e4b1 | 186 | } |
JonFreeman | 11:bfb73f083009 | 187 | |
JonFreeman | 3:ecb00e0e8d68 | 188 | |
JonFreeman | 5:ca86a7848d54 | 189 | /** |
JonFreeman | 12:d1d21a2941ef | 190 | * void AtoD_reader () // Call to here every VOLTAGE_READ_INTERVAL_US = 50 once loop responds to flag set in isr |
JonFreeman | 12:d1d21a2941ef | 191 | * Not part of ISR |
JonFreeman | 5:ca86a7848d54 | 192 | */ |
JonFreeman | 3:ecb00e0e8d68 | 193 | void AtoD_reader () // Call to here every VOLTAGE_READ_INTERVAL_US = 50 once loop responds to flag set in isr |
JonFreeman | 0:435bf84ce48a | 194 | { |
JonFreeman | 11:bfb73f083009 | 195 | static uint32_t i = 0; |
JonFreeman | 3:ecb00e0e8d68 | 196 | if (MotorA.tickleon) |
JonFreeman | 3:ecb00e0e8d68 | 197 | MotorA.high_side_off (); |
JonFreeman | 3:ecb00e0e8d68 | 198 | if (MotorB.tickleon) |
JonFreeman | 3:ecb00e0e8d68 | 199 | MotorB.high_side_off (); |
JonFreeman | 0:435bf84ce48a | 200 | if (AtoD_Semaphore > 20) { |
JonFreeman | 8:93203f473f6e | 201 | pc.printf ("WARNING - sema cnt %d\r\n", AtoD_Semaphore); |
JonFreeman | 0:435bf84ce48a | 202 | AtoD_Semaphore = 20; |
JonFreeman | 0:435bf84ce48a | 203 | } |
JonFreeman | 0:435bf84ce48a | 204 | while (AtoD_Semaphore > 0) { |
JonFreeman | 0:435bf84ce48a | 205 | AtoD_Semaphore--; |
JonFreeman | 0:435bf84ce48a | 206 | // Code here to sequence through reading voltmeter, demand pot, ammeters |
JonFreeman | 0:435bf84ce48a | 207 | switch (i) { // |
JonFreeman | 0:435bf84ce48a | 208 | case 0: |
JonFreeman | 0:435bf84ce48a | 209 | volt_reading += Ain_SystemVolts.read_u16 (); // Result = Result + New Reading |
JonFreeman | 0:435bf84ce48a | 210 | volt_reading >>= 1; // Result = Result / 2 |
JonFreeman | 0:435bf84ce48a | 211 | break; // i.e. Very simple digital low pass filter |
JonFreeman | 0:435bf84ce48a | 212 | case 1: |
JonFreeman | 0:435bf84ce48a | 213 | driverpot_reading += Ain_DriverPot.read_u16 (); |
JonFreeman | 0:435bf84ce48a | 214 | driverpot_reading >>= 1; |
JonFreeman | 0:435bf84ce48a | 215 | break; |
JonFreeman | 0:435bf84ce48a | 216 | case 2: |
JonFreeman | 12:d1d21a2941ef | 217 | MotorA.sniff_current (); // Initiate ADC current reading |
JonFreeman | 0:435bf84ce48a | 218 | break; |
JonFreeman | 0:435bf84ce48a | 219 | case 3: |
JonFreeman | 11:bfb73f083009 | 220 | MotorB.sniff_current (); |
JonFreeman | 0:435bf84ce48a | 221 | break; |
JonFreeman | 0:435bf84ce48a | 222 | } |
JonFreeman | 0:435bf84ce48a | 223 | i++; // prepare to read the next input in response to the next interrupt |
JonFreeman | 0:435bf84ce48a | 224 | if (i > 3) |
JonFreeman | 0:435bf84ce48a | 225 | i = 0; |
JonFreeman | 3:ecb00e0e8d68 | 226 | } // end of while (AtoD_Semaphore > 0) { |
JonFreeman | 3:ecb00e0e8d68 | 227 | if (MotorA.tickleon) { |
JonFreeman | 3:ecb00e0e8d68 | 228 | MotorA.tickleon--; |
JonFreeman | 5:ca86a7848d54 | 229 | MotorA.motor_set (); // Reactivate any high side switches turned off above |
JonFreeman | 3:ecb00e0e8d68 | 230 | } |
JonFreeman | 3:ecb00e0e8d68 | 231 | if (MotorB.tickleon) { |
JonFreeman | 3:ecb00e0e8d68 | 232 | MotorB.tickleon--; |
JonFreeman | 3:ecb00e0e8d68 | 233 | MotorB.motor_set (); |
JonFreeman | 0:435bf84ce48a | 234 | } |
JonFreeman | 0:435bf84ce48a | 235 | } |
JonFreeman | 0:435bf84ce48a | 236 | |
JonFreeman | 10:e40d8724268a | 237 | /** double Read_Servo1_In () |
JonFreeman | 10:e40d8724268a | 238 | * driverpot_reading is a global 16 bit unsigned int updated in interrupt service routine |
JonFreeman | 10:e40d8724268a | 239 | * ISR also filters signal |
JonFreeman | 10:e40d8724268a | 240 | * This function returns normalised double (range 0.0 to 1.0) representation of driver pot position |
JonFreeman | 10:e40d8724268a | 241 | */ |
JonFreeman | 10:e40d8724268a | 242 | double Read_Servo1_In () |
JonFreeman | 10:e40d8724268a | 243 | { |
JonFreeman | 10:e40d8724268a | 244 | const double xjoin = 0.5, |
JonFreeman | 10:e40d8724268a | 245 | yjoin = 0.35, |
JonFreeman | 10:e40d8724268a | 246 | slope_a = yjoin / xjoin, |
JonFreeman | 10:e40d8724268a | 247 | slope_b = (1.0 - yjoin)/(1.0 - xjoin); |
JonFreeman | 10:e40d8724268a | 248 | // centre = 0.35, // For pot, first (1/3)ish in braking area, position = 1/3 drift, > (1/3)ish drive |
JonFreeman | 10:e40d8724268a | 249 | // halfish = 0.5; // RC stick in the centre value |
JonFreeman | 10:e40d8724268a | 250 | // Bottom half of RC stick movement maps to lowest (1/3)ish pot movement |
JonFreeman | 10:e40d8724268a | 251 | // Higher half of RC stick movement maps to highest (2/3)ish pot movement |
JonFreeman | 10:e40d8724268a | 252 | double t; |
JonFreeman | 10:e40d8724268a | 253 | double demand = RC_chan_1.normalised(); |
JonFreeman | 10:e40d8724268a | 254 | // apply demand = 1.0 - demand here to swap stick move polarity |
JonFreeman | 10:e40d8724268a | 255 | // return pow (demand, SERVOIN_PWR_BENDER); |
JonFreeman | 10:e40d8724268a | 256 | if (demand < 0.0) demand = 0.0; |
JonFreeman | 10:e40d8724268a | 257 | if (demand > 1.0) demand = 1.0; |
JonFreeman | 10:e40d8724268a | 258 | if (demand < xjoin) { |
JonFreeman | 10:e40d8724268a | 259 | demand *= slope_a; |
JonFreeman | 10:e40d8724268a | 260 | } |
JonFreeman | 10:e40d8724268a | 261 | else { |
JonFreeman | 10:e40d8724268a | 262 | t = yjoin + ((demand - xjoin) * slope_b); |
JonFreeman | 10:e40d8724268a | 263 | demand = t; |
JonFreeman | 10:e40d8724268a | 264 | } |
JonFreeman | 10:e40d8724268a | 265 | return demand; |
JonFreeman | 10:e40d8724268a | 266 | } |
JonFreeman | 10:e40d8724268a | 267 | |
JonFreeman | 0:435bf84ce48a | 268 | /** double Read_DriverPot () |
JonFreeman | 0:435bf84ce48a | 269 | * driverpot_reading is a global 16 bit unsigned int updated in interrupt service routine |
JonFreeman | 10:e40d8724268a | 270 | * ISR also filters signal by returning average of most recent two readings |
JonFreeman | 0:435bf84ce48a | 271 | * This function returns normalised double (range 0.0 to 1.0) representation of driver pot position |
JonFreeman | 0:435bf84ce48a | 272 | */ |
JonFreeman | 0:435bf84ce48a | 273 | double Read_DriverPot () |
JonFreeman | 0:435bf84ce48a | 274 | { |
JonFreeman | 5:ca86a7848d54 | 275 | return ((double) driverpot_reading) / 65536.0; // Normalise 0.0 <= control pot <= 1.0 |
JonFreeman | 0:435bf84ce48a | 276 | } |
JonFreeman | 0:435bf84ce48a | 277 | |
JonFreeman | 0:435bf84ce48a | 278 | double Read_BatteryVolts () |
JonFreeman | 0:435bf84ce48a | 279 | { |
JonFreeman | 5:ca86a7848d54 | 280 | return ((double) volt_reading) / 951.0; // divisor fiddled to make voltage reading correct ! |
JonFreeman | 0:435bf84ce48a | 281 | } |
JonFreeman | 0:435bf84ce48a | 282 | |
JonFreeman | 12:d1d21a2941ef | 283 | |
JonFreeman | 8:93203f473f6e | 284 | void mode_set_both_motors (int mode, double val) // called from cli to set fw, re, rb, hb |
JonFreeman | 8:93203f473f6e | 285 | { |
JonFreeman | 2:04761b196473 | 286 | MotorA.set_mode (mode); |
JonFreeman | 2:04761b196473 | 287 | MotorB.set_mode (mode); |
JonFreeman | 12:d1d21a2941ef | 288 | if (mode == MOTOR_REGENBRAKE) { |
JonFreeman | 5:ca86a7848d54 | 289 | if (val > 1.0) |
JonFreeman | 5:ca86a7848d54 | 290 | val = 1.0; |
JonFreeman | 5:ca86a7848d54 | 291 | if (val < 0.0) |
JonFreeman | 5:ca86a7848d54 | 292 | val = 0.0; |
JonFreeman | 5:ca86a7848d54 | 293 | val *= 0.9; // set upper limit, this is essential |
JonFreeman | 5:ca86a7848d54 | 294 | val = sqrt (val); // to linearise effect |
JonFreeman | 5:ca86a7848d54 | 295 | setVI (val, 1.0); |
JonFreeman | 2:04761b196473 | 296 | } |
JonFreeman | 2:04761b196473 | 297 | } |
JonFreeman | 0:435bf84ce48a | 298 | |
JonFreeman | 12:d1d21a2941ef | 299 | |
JonFreeman | 0:435bf84ce48a | 300 | |
JonFreeman | 12:d1d21a2941ef | 301 | #ifdef USING_DC_MOTORS |
JonFreeman | 8:93203f473f6e | 302 | void restorer () |
JonFreeman | 8:93203f473f6e | 303 | { |
JonFreeman | 8:93203f473f6e | 304 | motors_restore.detach (); |
JonFreeman | 8:93203f473f6e | 305 | if (MotorA.dc_motor) { |
JonFreeman | 8:93203f473f6e | 306 | MotorA.set_V_limit (MotorA.last_V); |
JonFreeman | 8:93203f473f6e | 307 | MotorA.set_I_limit (MotorA.last_I); |
JonFreeman | 8:93203f473f6e | 308 | MotorA.motor_set (); |
JonFreeman | 8:93203f473f6e | 309 | } |
JonFreeman | 8:93203f473f6e | 310 | if (MotorB.dc_motor) { |
JonFreeman | 8:93203f473f6e | 311 | MotorB.set_V_limit (MotorB.last_V); |
JonFreeman | 8:93203f473f6e | 312 | MotorB.set_I_limit (MotorB.last_I); |
JonFreeman | 8:93203f473f6e | 313 | MotorB.motor_set (); |
JonFreeman | 8:93203f473f6e | 314 | } |
JonFreeman | 8:93203f473f6e | 315 | } |
JonFreeman | 12:d1d21a2941ef | 316 | #endif |
JonFreeman | 8:93203f473f6e | 317 | |
JonFreeman | 10:e40d8724268a | 318 | void rcin_report () { |
JonFreeman | 10:e40d8724268a | 319 | double c1 = RC_chan_1.normalised(); |
JonFreeman | 10:e40d8724268a | 320 | double c2 = RC_chan_2.normalised(); |
JonFreeman | 10:e40d8724268a | 321 | uint32_t pc1 = RC_chan_1.pulsecount(); |
JonFreeman | 10:e40d8724268a | 322 | uint32_t pc2 = RC_chan_2.pulsecount(); |
JonFreeman | 10:e40d8724268a | 323 | pc.printf ("At rcin_report, Ch1=%.3f, Ch2=%.3f, cnt1 %d, cnt2 %d\r\n", c1, c2, pc1, pc2); |
JonFreeman | 10:e40d8724268a | 324 | // if (c1 < -0.0001) |
JonFreeman | 10:e40d8724268a | 325 | pc.printf ("\t1 period %d, pulsewidth %d\r\n", RC_chan_1.period(), RC_chan_1.pulsewidth()); |
JonFreeman | 10:e40d8724268a | 326 | // if (c2 < -0.0001) |
JonFreeman | 10:e40d8724268a | 327 | pc.printf ("\t2 period %d, pulsewidth %d\r\n", RC_chan_2.period(), RC_chan_2.pulsewidth()); |
JonFreeman | 10:e40d8724268a | 328 | } |
JonFreeman | 8:93203f473f6e | 329 | |
JonFreeman | 11:bfb73f083009 | 330 | bool worth_the_bother (double a, double b) { // Tests size of change. No point updating tiny demand changes |
JonFreeman | 10:e40d8724268a | 331 | double c = a - b; |
JonFreeman | 10:e40d8724268a | 332 | if (c < 0.0) |
JonFreeman | 10:e40d8724268a | 333 | c = 0.0 - c; |
JonFreeman | 10:e40d8724268a | 334 | if (c > 0.02) |
JonFreeman | 10:e40d8724268a | 335 | return true; |
JonFreeman | 10:e40d8724268a | 336 | return false; |
JonFreeman | 10:e40d8724268a | 337 | } |
JonFreeman | 10:e40d8724268a | 338 | |
JonFreeman | 10:e40d8724268a | 339 | void hand_control_state_machine (int source) { // if hand control mode '3', get here @ approx 30Hz to read drivers control pot |
JonFreeman | 10:e40d8724268a | 340 | // if servo1 mode '4', reads input from servo1 instead |
JonFreeman | 10:e40d8724268a | 341 | enum { // states used in hand_control_state_machine() |
JonFreeman | 10:e40d8724268a | 342 | HAND_CONT_IDLE, |
JonFreeman | 10:e40d8724268a | 343 | HAND_CONT_BRAKE_WAIT, |
JonFreeman | 10:e40d8724268a | 344 | HAND_CONT_BRAKE_POT, |
JonFreeman | 10:e40d8724268a | 345 | HAND_CONT_STATE_INTO_BRAKING, |
JonFreeman | 10:e40d8724268a | 346 | HAND_CONT_STATE_BRAKING, |
JonFreeman | 10:e40d8724268a | 347 | HAND_CONT_STATE_INTO_DRIVING, |
JonFreeman | 10:e40d8724268a | 348 | HAND_CONT_STATE_DRIVING |
JonFreeman | 10:e40d8724268a | 349 | } ; |
JonFreeman | 10:e40d8724268a | 350 | |
JonFreeman | 10:e40d8724268a | 351 | static int hand_controller_state = HAND_CONT_IDLE; |
JonFreeman | 10:e40d8724268a | 352 | // static int old_hand_controller_direction = T5; // Nov 2018 reworked thanks to feedback from Rob and Quentin |
JonFreeman | 8:93203f473f6e | 353 | static double brake_effort, drive_effort, pot_position, old_pot_position = 0.0; |
JonFreeman | 10:e40d8724268a | 354 | static int dirbuf[5] = {100,100,100,100,100}; // Initialised with invalid direction such that 'change' is read from switch |
JonFreeman | 10:e40d8724268a | 355 | static int dirbufptr = 0, direction_old = -1, direction_new = -1; |
JonFreeman | 10:e40d8724268a | 356 | const int buflen = sizeof(dirbuf) / sizeof(int); |
JonFreeman | 10:e40d8724268a | 357 | const double Pot_Brake_Range = 0.35; //pow (0.5, SERVOIN_PWR_BENDER); //0.353553 for SERVOIN_PWR_BENDER = 1.5; |
JonFreeman | 10:e40d8724268a | 358 | |
JonFreeman | 10:e40d8724268a | 359 | direction_old = direction_new; |
JonFreeman | 10:e40d8724268a | 360 | |
JonFreeman | 10:e40d8724268a | 361 | // Test for change in direction switch setting. |
JonFreeman | 10:e40d8724268a | 362 | // If whole buffer NEWLY filled with all Fwd or all Rev, state = brake_wait |
JonFreeman | 10:e40d8724268a | 363 | int diracc = 0; |
JonFreeman | 10:e40d8724268a | 364 | if (dirbufptr >= buflen) |
JonFreeman | 10:e40d8724268a | 365 | dirbufptr = 0; |
JonFreeman | 10:e40d8724268a | 366 | dirbuf[dirbufptr++] = T5; // Read direction switch into circular debounce buffer |
JonFreeman | 10:e40d8724268a | 367 | for (int i = 0; i < buflen; i++) |
JonFreeman | 10:e40d8724268a | 368 | diracc += dirbuf[i]; // will = 0 or buflen if direction switch stable |
JonFreeman | 10:e40d8724268a | 369 | if (diracc == buflen || diracc == 0) // if was all 0 or all 1 |
JonFreeman | 10:e40d8724268a | 370 | direction_new = diracc / buflen; |
JonFreeman | 10:e40d8724268a | 371 | if (direction_new != direction_old) |
JonFreeman | 10:e40d8724268a | 372 | hand_controller_state = HAND_CONT_BRAKE_WAIT; // validated change of direction switch |
JonFreeman | 10:e40d8724268a | 373 | |
JonFreeman | 10:e40d8724268a | 374 | switch (source) { |
JonFreeman | 10:e40d8724268a | 375 | case 3: // driver pot is control input |
JonFreeman | 10:e40d8724268a | 376 | pot_position = Read_DriverPot (); // Only place read in the loop, rteads normalised 0.0 to 1.0 |
JonFreeman | 10:e40d8724268a | 377 | break; |
JonFreeman | 10:e40d8724268a | 378 | case 4: // servo 1 input is control input |
JonFreeman | 10:e40d8724268a | 379 | break; |
JonFreeman | 10:e40d8724268a | 380 | default: |
JonFreeman | 10:e40d8724268a | 381 | pot_position = 0.0; |
JonFreeman | 10:e40d8724268a | 382 | break; |
JonFreeman | 8:93203f473f6e | 383 | } |
JonFreeman | 10:e40d8724268a | 384 | |
JonFreeman | 10:e40d8724268a | 385 | switch (hand_controller_state) { |
JonFreeman | 10:e40d8724268a | 386 | case HAND_CONT_IDLE: // Here for first few passes until validated direction switch reading |
JonFreeman | 10:e40d8724268a | 387 | break; |
JonFreeman | 10:e40d8724268a | 388 | |
JonFreeman | 11:bfb73f083009 | 389 | case HAND_CONT_BRAKE_WAIT: // Only get here after direction input changed or newly validated at power on |
JonFreeman | 10:e40d8724268a | 390 | pc.printf ("At HAND_CONT_BRAKE_WAIT\r\n"); |
JonFreeman | 10:e40d8724268a | 391 | brake_effort = 0.05; // Apply braking not too fiercely |
JonFreeman | 12:d1d21a2941ef | 392 | mode_set_both_motors (MOTOR_REGENBRAKE, brake_effort); // Direction change |
JonFreeman | 10:e40d8724268a | 393 | hand_controller_state = HAND_CONT_BRAKE_POT; |
JonFreeman | 10:e40d8724268a | 394 | break; |
JonFreeman | 10:e40d8724268a | 395 | |
JonFreeman | 11:bfb73f083009 | 396 | case HAND_CONT_BRAKE_POT: // Only get here after one pass through HAND_CONT_BRAKE_WAIT but |
JonFreeman | 11:bfb73f083009 | 397 | if (brake_effort < 0.9) { // remain in this state until driver has turned pott fully anticlockwise |
JonFreeman | 11:bfb73f083009 | 398 | brake_effort += 0.05; // ramp braking up to max over about one sec after direction change or validation |
JonFreeman | 12:d1d21a2941ef | 399 | mode_set_both_motors (MOTOR_REGENBRAKE, brake_effort); // Direction change or testing at power on |
JonFreeman | 10:e40d8724268a | 400 | pc.printf ("Brake effort %.2f\r\n", brake_effort); |
JonFreeman | 10:e40d8724268a | 401 | } |
JonFreeman | 10:e40d8724268a | 402 | else { // once braking up to quite hard |
JonFreeman | 10:e40d8724268a | 403 | if (pot_position < 0.02) { // Driver has turned pot fully anticlock |
JonFreeman | 10:e40d8724268a | 404 | hand_controller_state = HAND_CONT_STATE_BRAKING; |
JonFreeman | 10:e40d8724268a | 405 | } |
JonFreeman | 8:93203f473f6e | 406 | } |
JonFreeman | 8:93203f473f6e | 407 | break; |
JonFreeman | 10:e40d8724268a | 408 | |
JonFreeman | 8:93203f473f6e | 409 | case HAND_CONT_STATE_INTO_BRAKING: |
JonFreeman | 8:93203f473f6e | 410 | brake_effort = (Pot_Brake_Range - pot_position) / Pot_Brake_Range; |
JonFreeman | 10:e40d8724268a | 411 | if (brake_effort < 0.0) |
JonFreeman | 10:e40d8724268a | 412 | brake_effort = 0.5; |
JonFreeman | 12:d1d21a2941ef | 413 | mode_set_both_motors (MOTOR_REGENBRAKE, brake_effort); |
JonFreeman | 8:93203f473f6e | 414 | old_pot_position = pot_position; |
JonFreeman | 10:e40d8724268a | 415 | hand_controller_state = HAND_CONT_STATE_BRAKING; |
JonFreeman | 8:93203f473f6e | 416 | pc.printf ("Brake\r\n"); |
JonFreeman | 8:93203f473f6e | 417 | break; |
JonFreeman | 10:e40d8724268a | 418 | |
JonFreeman | 8:93203f473f6e | 419 | case HAND_CONT_STATE_BRAKING: |
JonFreeman | 8:93203f473f6e | 420 | if (pot_position > Pot_Brake_Range) // escape braking into drive |
JonFreeman | 10:e40d8724268a | 421 | hand_controller_state = HAND_CONT_STATE_INTO_DRIVING; |
JonFreeman | 8:93203f473f6e | 422 | else { |
JonFreeman | 8:93203f473f6e | 423 | if (worth_the_bother(pot_position, old_pot_position)) { |
JonFreeman | 8:93203f473f6e | 424 | old_pot_position = pot_position; |
JonFreeman | 8:93203f473f6e | 425 | brake_effort = (Pot_Brake_Range - pot_position) / Pot_Brake_Range; |
JonFreeman | 12:d1d21a2941ef | 426 | mode_set_both_motors (MOTOR_REGENBRAKE, brake_effort); |
JonFreeman | 8:93203f473f6e | 427 | // pc.printf ("Brake %.2f\r\n", brake_effort); |
JonFreeman | 8:93203f473f6e | 428 | } |
JonFreeman | 8:93203f473f6e | 429 | } |
JonFreeman | 8:93203f473f6e | 430 | break; |
JonFreeman | 10:e40d8724268a | 431 | |
JonFreeman | 11:bfb73f083009 | 432 | case HAND_CONT_STATE_INTO_DRIVING: // Only get here after HAND_CONT_STATE_BRAKING |
JonFreeman | 10:e40d8724268a | 433 | pc.printf ("Drive\r\n"); |
JonFreeman | 10:e40d8724268a | 434 | if (direction_new == 1) |
JonFreeman | 12:d1d21a2941ef | 435 | mode_set_both_motors (MOTOR_FORWARD, 0.0); // sets both motors |
JonFreeman | 10:e40d8724268a | 436 | else |
JonFreeman | 12:d1d21a2941ef | 437 | mode_set_both_motors (MOTOR_REVERSE, 0.0); |
JonFreeman | 10:e40d8724268a | 438 | hand_controller_state = HAND_CONT_STATE_DRIVING; |
JonFreeman | 10:e40d8724268a | 439 | break; |
JonFreeman | 10:e40d8724268a | 440 | |
JonFreeman | 8:93203f473f6e | 441 | case HAND_CONT_STATE_DRIVING: |
JonFreeman | 8:93203f473f6e | 442 | if (pot_position < Pot_Brake_Range) // escape braking into drive |
JonFreeman | 10:e40d8724268a | 443 | hand_controller_state = HAND_CONT_STATE_INTO_BRAKING; |
JonFreeman | 8:93203f473f6e | 444 | else { |
JonFreeman | 8:93203f473f6e | 445 | if (worth_the_bother(pot_position, old_pot_position)) { |
JonFreeman | 8:93203f473f6e | 446 | old_pot_position = pot_position; |
JonFreeman | 8:93203f473f6e | 447 | drive_effort = (pot_position - Pot_Brake_Range) / (1.0 - Pot_Brake_Range); |
JonFreeman | 8:93203f473f6e | 448 | setVI (drive_effort, drive_effort); // Wind volts and amps up and down together ??? |
JonFreeman | 8:93203f473f6e | 449 | pc.printf ("Drive %.2f\r\n", drive_effort); |
JonFreeman | 8:93203f473f6e | 450 | } |
JonFreeman | 8:93203f473f6e | 451 | } |
JonFreeman | 8:93203f473f6e | 452 | break; |
JonFreeman | 10:e40d8724268a | 453 | |
JonFreeman | 8:93203f473f6e | 454 | default: |
JonFreeman | 10:e40d8724268a | 455 | pc.printf ("Unhandled Hand Controller state %d\r\n", hand_controller_state); |
JonFreeman | 8:93203f473f6e | 456 | break; |
JonFreeman | 8:93203f473f6e | 457 | } // endof switch (hand_controller_state) { |
JonFreeman | 8:93203f473f6e | 458 | } |
JonFreeman | 8:93203f473f6e | 459 | |
JonFreeman | 0:435bf84ce48a | 460 | int main() |
JonFreeman | 0:435bf84ce48a | 461 | { |
JonFreeman | 4:21d91465e4b1 | 462 | int eighth_sec_count = 0; |
JonFreeman | 10:e40d8724268a | 463 | double servo_angle = 0.0; // For testing servo outs |
JonFreeman | 0:435bf84ce48a | 464 | |
JonFreeman | 6:f289a49c1eae | 465 | Temperature_pin.fall (&temp_sensor_isr); |
JonFreeman | 6:f289a49c1eae | 466 | Temperature_pin.mode (PullUp); |
JonFreeman | 0:435bf84ce48a | 467 | // Setup system timers to cause periodic interrupts to synchronise and automate volt and current readings, loop repeat rate etc |
JonFreeman | 0:435bf84ce48a | 468 | tick_vread.attach_us (&ISR_voltage_reader, VOLTAGE_READ_INTERVAL_US); // Start periodic interrupt generator |
JonFreeman | 0:435bf84ce48a | 469 | loop_timer.attach_us (&ISR_loop_timer, MAIN_LOOP_REPEAT_TIME_US); // Start periodic interrupt generator |
JonFreeman | 6:f289a49c1eae | 470 | temperature_find_ticker.attach_us (&ISR_temperature_find_ticker, 960); |
JonFreeman | 0:435bf84ce48a | 471 | // Done setting up system interrupt timers |
JonFreeman | 6:f289a49c1eae | 472 | temperature_timer.start (); |
JonFreeman | 0:435bf84ce48a | 473 | |
JonFreeman | 12:d1d21a2941ef | 474 | pc.baud (9600); // COM port to pc |
JonFreeman | 12:d1d21a2941ef | 475 | com3.baud (1200); // Once had an idea to use this for IR comms, never tried |
JonFreeman | 12:d1d21a2941ef | 476 | com2.baud (19200); // Opto isolated serial bus connecting 'n' STM3_ESC boards to 1 Brute Touch Screen controller |
JonFreeman | 6:f289a49c1eae | 477 | |
JonFreeman | 12:d1d21a2941ef | 478 | rpm2mph = 60.0 // to Motor Revs per hour; |
JonFreeman | 12:d1d21a2941ef | 479 | * ((double)mode.rd(MOTPIN) / (double)mode.rd(WHEELGEAR)) // Wheel revs per hour |
JonFreeman | 12:d1d21a2941ef | 480 | * PI * ((double)mode.rd(WHEELDIA) / 1000.0) // metres per hour |
JonFreeman | 12:d1d21a2941ef | 481 | * 39.37 / (1760.0 * 36.0); // miles per hour |
JonFreeman | 5:ca86a7848d54 | 482 | |
JonFreeman | 12:d1d21a2941ef | 483 | MotorA.direction_set (mode.rd(MOTADIR)); // modes all setup in class eeprom_settings {} mode ; constructor |
JonFreeman | 12:d1d21a2941ef | 484 | MotorB.direction_set (mode.rd(MOTBDIR)); |
JonFreeman | 12:d1d21a2941ef | 485 | MotorA.poles (mode.rd(MOTAPOLES)); // Returns true if poles 4, 6 or 8. Returns false otherwise |
JonFreeman | 12:d1d21a2941ef | 486 | MotorB.poles (mode.rd(MOTBPOLES)); // Only two calls are here |
JonFreeman | 12:d1d21a2941ef | 487 | MotorA.set_mode (MOTOR_REGENBRAKE); |
JonFreeman | 12:d1d21a2941ef | 488 | MotorB.set_mode (MOTOR_REGENBRAKE); |
JonFreeman | 12:d1d21a2941ef | 489 | setVI (0.9, 0.5); // Power up with moderate regen braking applied |
JonFreeman | 12:d1d21a2941ef | 490 | |
JonFreeman | 6:f289a49c1eae | 491 | // T1 = 0; Now WRONGLY hoped to be InterruptIn counting pulses from LMT01 temperature sensor |
JonFreeman | 5:ca86a7848d54 | 492 | T2 = 0; // T2, T3, T4 As yet unused pins |
JonFreeman | 8:93203f473f6e | 493 | // T3 = 0; |
JonFreeman | 8:93203f473f6e | 494 | // T4 = 0; |
JonFreeman | 5:ca86a7848d54 | 495 | // T5 = 0; now input from fw/re on remote control box |
JonFreeman | 0:435bf84ce48a | 496 | T6 = 0; |
JonFreeman | 5:ca86a7848d54 | 497 | |
JonFreeman | 12:d1d21a2941ef | 498 | if ((last_temperature_count > 160) && (last_temperature_count < 2400)) // in range -40 to +100 degree C |
JonFreeman | 6:f289a49c1eae | 499 | temp_sensor_exists = true; |
JonFreeman | 12:d1d21a2941ef | 500 | #ifdef USING_DC_MOTORS |
JonFreeman | 8:93203f473f6e | 501 | dc_motor_kicker_timer.start (); |
JonFreeman | 12:d1d21a2941ef | 502 | #endif |
JonFreeman | 8:93203f473f6e | 503 | int old_hand_controller_direction = T5; |
JonFreeman | 12:d1d21a2941ef | 504 | if (mode.rd(COMM_SRC) == 3) { // Read fwd/rev switch 'T5', PA15 on 401 |
JonFreeman | 8:93203f473f6e | 505 | pc.printf ("Pot control\r\n"); |
JonFreeman | 8:93203f473f6e | 506 | if (T5) |
JonFreeman | 12:d1d21a2941ef | 507 | mode_set_both_motors (MOTOR_FORWARD, 0.0); // sets both motors |
JonFreeman | 8:93203f473f6e | 508 | else |
JonFreeman | 12:d1d21a2941ef | 509 | mode_set_both_motors (MOTOR_REVERSE, 0.0); |
JonFreeman | 8:93203f473f6e | 510 | } |
JonFreeman | 8:93203f473f6e | 511 | |
JonFreeman | 12:d1d21a2941ef | 512 | pc.printf ("About to start %s!, mode_bytes[COMM_SRC]= %d\r\n", const_version_string, mode.rd(COMM_SRC)); |
JonFreeman | 12:d1d21a2941ef | 513 | pc.printf ("ESC_Error.all_good() ret'd %s\r\n", ESC_Error.all_good() ? "true" : "false"); |
JonFreeman | 12:d1d21a2941ef | 514 | // pc.printf ("SystemCoreClock=%d, MAX_PWM_TICKS=%d\r\n", SystemCoreClock, MAX_PWM_TICKS); |
JonFreeman | 12:d1d21a2941ef | 515 | // pcc.test () ; |
JonFreeman | 12:d1d21a2941ef | 516 | // tsc.test () ; |
JonFreeman | 0:435bf84ce48a | 517 | while (1) { // Loop forever, repeats synchroised by waiting for ticker Interrupt Service Routine to set 'loop_flag' true |
JonFreeman | 0:435bf84ce48a | 518 | while (!loop_flag) { // Most of the time is spent in this loop, repeatedly re-checking for commands from pc port |
JonFreeman | 12:d1d21a2941ef | 519 | pcc.core () ; // Proceed beyond here once loop_timer ticker ISR has set loop_flag true |
JonFreeman | 12:d1d21a2941ef | 520 | tsc.core () ; // Proceed beyond here once loop_timer ticker ISR has set loop_flag true |
JonFreeman | 0:435bf84ce48a | 521 | AtoD_reader (); // Performs A to D conversions at rate set by ticker interrupts |
JonFreeman | 11:bfb73f083009 | 522 | } // 32Hz original setting for loop repeat rate |
JonFreeman | 10:e40d8724268a | 523 | loop_flag = false; // Clear flag set by ticker interrupt handler. WHEN LAST CHECKED this was about every 32ms |
JonFreeman | 10:e40d8724268a | 524 | |
JonFreeman | 10:e40d8724268a | 525 | RC_chan_1.validate_rx(); |
JonFreeman | 10:e40d8724268a | 526 | RC_chan_2.validate_rx(); |
JonFreeman | 10:e40d8724268a | 527 | |
JonFreeman | 12:d1d21a2941ef | 528 | switch (mode.rd(COMM_SRC)) { // Look to selected source of driving command, act on commands from wherever |
JonFreeman | 8:93203f473f6e | 529 | case 0: // Invalid |
JonFreeman | 8:93203f473f6e | 530 | break; |
JonFreeman | 11:bfb73f083009 | 531 | case COM1: // COM1 - Primarily for testing, bypassed by command line interpreter |
JonFreeman | 8:93203f473f6e | 532 | break; |
JonFreeman | 11:bfb73f083009 | 533 | case COM2: // COM2 - Primarily for testing, bypassed by command line interpreter |
JonFreeman | 8:93203f473f6e | 534 | break; |
JonFreeman | 11:bfb73f083009 | 535 | case HAND: // Put all hand controller input stuff here |
JonFreeman | 10:e40d8724268a | 536 | hand_control_state_machine (3); |
JonFreeman | 8:93203f473f6e | 537 | break; // endof hand controller stuff |
JonFreeman | 11:bfb73f083009 | 538 | case RC_IN1: // RC_chan_1 |
JonFreeman | 10:e40d8724268a | 539 | hand_control_state_machine (4); |
JonFreeman | 8:93203f473f6e | 540 | break; |
JonFreeman | 11:bfb73f083009 | 541 | case RC_IN2: // RC_chan_2 |
JonFreeman | 8:93203f473f6e | 542 | break; |
JonFreeman | 8:93203f473f6e | 543 | default: |
JonFreeman | 12:d1d21a2941ef | 544 | if (ESC_Error.read(FAULT_UNRECOGNISED_STATE)) { |
JonFreeman | 12:d1d21a2941ef | 545 | pc.printf ("Unrecognised state %d\r\n", mode.rd(COMM_SRC)); // set error flag instead here |
JonFreeman | 12:d1d21a2941ef | 546 | ESC_Error.set (FAULT_UNRECOGNISED_STATE, 1); |
JonFreeman | 12:d1d21a2941ef | 547 | } |
JonFreeman | 8:93203f473f6e | 548 | break; |
JonFreeman | 8:93203f473f6e | 549 | } // endof switch (mode_bytes[COMM_SRC]) { |
JonFreeman | 8:93203f473f6e | 550 | |
JonFreeman | 12:d1d21a2941ef | 551 | #ifdef USING_DC_MOTORS |
JonFreeman | 8:93203f473f6e | 552 | dc_motor_kicker_timer.reset (); |
JonFreeman | 12:d1d21a2941ef | 553 | #endif |
JonFreeman | 12:d1d21a2941ef | 554 | MotorA.speed_monitor_and_control (); // Needed to keep table updated to give reading in Hall transitions per second |
JonFreeman | 12:d1d21a2941ef | 555 | MotorB.speed_monitor_and_control (); // Read MotorX.PPS to read pulses per sec or MotorX.RPM to read motor RPM |
JonFreeman | 12:d1d21a2941ef | 556 | |
JonFreeman | 12:d1d21a2941ef | 557 | #ifdef USING_DC_MOTORS |
JonFreeman | 8:93203f473f6e | 558 | if (MotorA.dc_motor) { |
JonFreeman | 12:d1d21a2941ef | 559 | // MotorA.raw_V_pwm (1); |
JonFreeman | 8:93203f473f6e | 560 | MotorA.low_side_on (); |
JonFreeman | 8:93203f473f6e | 561 | } |
JonFreeman | 8:93203f473f6e | 562 | if (MotorB.dc_motor) { |
JonFreeman | 12:d1d21a2941ef | 563 | // MotorB.raw_V_pwm (1); |
JonFreeman | 8:93203f473f6e | 564 | MotorB.low_side_on (); |
JonFreeman | 8:93203f473f6e | 565 | } |
JonFreeman | 8:93203f473f6e | 566 | if (MotorA.dc_motor || MotorB.dc_motor) { |
JonFreeman | 8:93203f473f6e | 567 | // motors_restore.attach_us (&restorer, ttime); |
JonFreeman | 8:93203f473f6e | 568 | motors_restore.attach_us (&restorer, 25); |
JonFreeman | 8:93203f473f6e | 569 | } |
JonFreeman | 12:d1d21a2941ef | 570 | #endif |
JonFreeman | 8:93203f473f6e | 571 | |
JonFreeman | 0:435bf84ce48a | 572 | if (flag_8Hz) { // do slower stuff |
JonFreeman | 0:435bf84ce48a | 573 | flag_8Hz = false; |
JonFreeman | 3:ecb00e0e8d68 | 574 | LED = !LED; // Toggle LED on board, should be seen to fast flash |
JonFreeman | 8:93203f473f6e | 575 | if (WatchDogEnable) { |
JonFreeman | 8:93203f473f6e | 576 | WatchDog--; |
JonFreeman | 8:93203f473f6e | 577 | if (WatchDog == 0) { // Deal with WatchDog timer timeout here |
JonFreeman | 8:93203f473f6e | 578 | setVI (0.0, 0.0); // set motor volts and amps to zero |
JonFreeman | 12:d1d21a2941ef | 579 | // 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 |
JonFreeman | 12:d1d21a2941ef | 580 | pc.printf ("TIMEOUT %c\r\n", mode.rd(BOARD_ID)); // Brute touch screen controller can do nothing with this |
JonFreeman | 8:93203f473f6e | 581 | } // End of dealing with WatchDog timer timeout |
JonFreeman | 8:93203f473f6e | 582 | if (WatchDog < 0) |
JonFreeman | 8:93203f473f6e | 583 | WatchDog = 0; |
JonFreeman | 8:93203f473f6e | 584 | } |
JonFreeman | 4:21d91465e4b1 | 585 | eighth_sec_count++; |
JonFreeman | 4:21d91465e4b1 | 586 | if (eighth_sec_count > 6) { // Send some status info out of serial port every second and a bit or thereabouts |
JonFreeman | 4:21d91465e4b1 | 587 | eighth_sec_count = 0; |
JonFreeman | 12:d1d21a2941ef | 588 | ESC_Error.report_any (false); // retain = false - reset error after reporting once |
JonFreeman | 8:93203f473f6e | 589 | /* if (temp_sensor_exists) { |
JonFreeman | 8:93203f473f6e | 590 | double tmprt = (double) last_temp_count; |
JonFreeman | 8:93203f473f6e | 591 | tmprt /= 16.0; |
JonFreeman | 8:93203f473f6e | 592 | tmprt -= 50.0; |
JonFreeman | 8:93203f473f6e | 593 | pc.printf ("Temp %.2f\r\n", tmprt); |
JonFreeman | 8:93203f473f6e | 594 | }*/ |
JonFreeman | 0:435bf84ce48a | 595 | } |
JonFreeman | 11:bfb73f083009 | 596 | #define SERVO_OUT_TEST |
JonFreeman | 11:bfb73f083009 | 597 | #ifdef SERVO_OUT_TEST |
JonFreeman | 10:e40d8724268a | 598 | // servo out test here December 2018 |
JonFreeman | 10:e40d8724268a | 599 | servo_angle += 0.01; |
JonFreeman | 10:e40d8724268a | 600 | if (servo_angle > TWOPI) |
JonFreeman | 10:e40d8724268a | 601 | servo_angle -= TWOPI; |
JonFreeman | 10:e40d8724268a | 602 | Servo1 = ((sin(servo_angle)+1.0) / 2.0); |
JonFreeman | 10:e40d8724268a | 603 | Servo2 = ((cos(servo_angle)+1.0) / 2.0); |
JonFreeman | 10:e40d8724268a | 604 | // Yep! Both servo outs work lovely December 2018 |
JonFreeman | 11:bfb73f083009 | 605 | #endif |
JonFreeman | 0:435bf84ce48a | 606 | } // End of if(flag_8Hz) |
JonFreeman | 0:435bf84ce48a | 607 | } // End of main programme loop |
JonFreeman | 0:435bf84ce48a | 608 | } |
JonFreeman | 11:bfb73f083009 | 609 |