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
main.cpp@10:e40d8724268a, 2019-01-15 (annotated)
- Committer:
- JonFreeman
- Date:
- Tue Jan 15 09:03:57 2019 +0000
- Revision:
- 10:e40d8724268a
- Parent:
- 9:ac2412df01be
- Child:
- 11:bfb73f083009
Buggered serial comms to TS controller
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 | 8:93203f473f6e | 4 | #include "stm32f401xe.h" |
JonFreeman | 0:435bf84ce48a | 5 | #include "DualBLS.h" |
JonFreeman | 0:435bf84ce48a | 6 | #include "BufferedSerial.h" |
JonFreeman | 0:435bf84ce48a | 7 | #include "FastPWM.h" |
JonFreeman | 4:21d91465e4b1 | 8 | #include "Servo.h" |
JonFreeman | 10:e40d8724268a | 9 | #include "brushless_motor.h" |
JonFreeman | 5:ca86a7848d54 | 10 | |
JonFreeman | 5:ca86a7848d54 | 11 | /* |
JonFreeman | 10:e40d8724268a | 12 | Brushless_STM3 board |
JonFreeman | 10:e40d8724268a | 13 | |
JonFreeman | 10:e40d8724268a | 14 | New 29th May 2018 ** |
JonFreeman | 10:e40d8724268a | 15 | 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 | 16 | */ |
JonFreeman | 5:ca86a7848d54 | 17 | |
JonFreeman | 5:ca86a7848d54 | 18 | |
JonFreeman | 0:435bf84ce48a | 19 | /* STM32F401RE - compile using NUCLEO-F401RE |
JonFreeman | 6:f289a49c1eae | 20 | // PROJECT - Dual Brushless Motor Controller - Jon Freeman June 2018. |
JonFreeman | 0:435bf84ce48a | 21 | |
JonFreeman | 0:435bf84ce48a | 22 | AnalogIn to read each motor current |
JonFreeman | 0:435bf84ce48a | 23 | |
JonFreeman | 0:435bf84ce48a | 24 | ____________________________________________________________________________________ |
JonFreeman | 0:435bf84ce48a | 25 | CONTROL PHILOSOPHY |
JonFreeman | 0:435bf84ce48a | 26 | This Bogie drive board software should ensure sensible control when commands supplied are not sensible ! |
JonFreeman | 0:435bf84ce48a | 27 | |
JonFreeman | 0:435bf84ce48a | 28 | That is, smooth transition between Drive, Coast and Brake to be implemented here. |
JonFreeman | 0:435bf84ce48a | 29 | The remote controller must not be relied upon to deliver sensible command sequences. |
JonFreeman | 0:435bf84ce48a | 30 | |
JonFreeman | 0:435bf84ce48a | 31 | So much the better if the remote controller does issue sensible commands, but ... |
JonFreeman | 0:435bf84ce48a | 32 | |
JonFreeman | 0:435bf84ce48a | 33 | ____________________________________________________________________________________ |
JonFreeman | 0:435bf84ce48a | 34 | |
JonFreeman | 0:435bf84ce48a | 35 | |
JonFreeman | 0:435bf84ce48a | 36 | */ |
JonFreeman | 8:93203f473f6e | 37 | |
JonFreeman | 7:6deaeace9a3e | 38 | #if defined (TARGET_NUCLEO_F401RE) // CPU in 64 pin LQFP |
JonFreeman | 9:ac2412df01be | 39 | #include "F401RE.h" // See here for warnings about Servo InterruptIn not working |
JonFreeman | 6:f289a49c1eae | 40 | #endif |
JonFreeman | 7:6deaeace9a3e | 41 | #if defined (TARGET_NUCLEO_F446ZE) // CPU in 144 pin LQFP |
JonFreeman | 10:e40d8724268a | 42 | #include "F446ZE.h" // A thought for future version |
JonFreeman | 6:f289a49c1eae | 43 | #endif |
JonFreeman | 0:435bf84ce48a | 44 | /* Global variable declarations */ |
JonFreeman | 3:ecb00e0e8d68 | 45 | volatile uint32_t fast_sys_timer = 0; // gets incremented by our Ticker ISR every VOLTAGE_READ_INTERVAL_US |
JonFreeman | 5:ca86a7848d54 | 46 | int WatchDog = WATCHDOG_RELOAD + 80; // Allow extra few seconds at powerup |
JonFreeman | 8:93203f473f6e | 47 | bool WatchDogEnable = false; // Must recieve explicit instruction from pc or controller to enable |
JonFreeman | 0:435bf84ce48a | 48 | uint32_t volt_reading = 0, // Global updated by interrupt driven read of Battery Volts |
JonFreeman | 0:435bf84ce48a | 49 | driverpot_reading = 0, // Global updated by interrupt driven read of Drivers Pot |
JonFreeman | 0:435bf84ce48a | 50 | sys_timer = 0, // gets incremented by our Ticker ISR every MAIN_LOOP_REPEAT_TIME_US |
JonFreeman | 5:ca86a7848d54 | 51 | AtoD_Semaphore = 0; |
JonFreeman | 5:ca86a7848d54 | 52 | int IAm; |
JonFreeman | 0:435bf84ce48a | 53 | bool loop_flag = false; // made true in ISR_loop_timer, picked up and made false again in main programme loop |
JonFreeman | 0:435bf84ce48a | 54 | bool flag_8Hz = false; // As loop_flag but repeats 8 times per sec |
JonFreeman | 6:f289a49c1eae | 55 | bool temp_sensor_exists = false; |
JonFreeman | 7:6deaeace9a3e | 56 | bool eeprom_flag; // gets set according to 24LC674 being found or not |
JonFreeman | 7:6deaeace9a3e | 57 | bool mode_good_flag = false; |
JonFreeman | 6:f289a49c1eae | 58 | char mode_bytes[36]; |
JonFreeman | 3:ecb00e0e8d68 | 59 | |
JonFreeman | 6:f289a49c1eae | 60 | uint32_t temp_sensor_count = 0, // incremented by every rising edge from LMT01 |
JonFreeman | 6:f289a49c1eae | 61 | last_temp_count = 0; // global updated approx every 100ms after each LMT01 conversion completes |
JonFreeman | 8:93203f473f6e | 62 | // struct single_bogie_options bogie; |
JonFreeman | 8:93203f473f6e | 63 | double rpm2mph = 0.0; // gets calculated from eeprom mode entries if present |
JonFreeman | 0:435bf84ce48a | 64 | /* End of Global variable declarations */ |
JonFreeman | 0:435bf84ce48a | 65 | |
JonFreeman | 0:435bf84ce48a | 66 | Ticker tick_vread; // Device to cause periodic interrupts, used to time voltage readings etc |
JonFreeman | 0:435bf84ce48a | 67 | Ticker loop_timer; // Device to cause periodic interrupts, used to sync iterations of main programme loop |
JonFreeman | 6:f289a49c1eae | 68 | Ticker temperature_find_ticker; |
JonFreeman | 6:f289a49c1eae | 69 | Timer temperature_timer; |
JonFreeman | 8:93203f473f6e | 70 | Timer dc_motor_kicker_timer; |
JonFreeman | 8:93203f473f6e | 71 | Timeout motors_restore; |
JonFreeman | 0:435bf84ce48a | 72 | |
JonFreeman | 0:435bf84ce48a | 73 | // Interrupt Service Routines |
JonFreeman | 8:93203f473f6e | 74 | void ISR_temperature_find_ticker () // every 960 us, i.e. slightly faster than once per milli sec |
JonFreeman | 8:93203f473f6e | 75 | { |
JonFreeman | 6:f289a49c1eae | 76 | static bool readflag = false; |
JonFreeman | 6:f289a49c1eae | 77 | int t = temperature_timer.read_ms (); |
JonFreeman | 6:f289a49c1eae | 78 | if ((t == 5) && (!readflag)) { |
JonFreeman | 6:f289a49c1eae | 79 | last_temp_count = temp_sensor_count; |
JonFreeman | 6:f289a49c1eae | 80 | temp_sensor_count = 0; |
JonFreeman | 6:f289a49c1eae | 81 | readflag = true; |
JonFreeman | 6:f289a49c1eae | 82 | } |
JonFreeman | 6:f289a49c1eae | 83 | if (t == 6) |
JonFreeman | 6:f289a49c1eae | 84 | readflag = false; |
JonFreeman | 6:f289a49c1eae | 85 | } |
JonFreeman | 0:435bf84ce48a | 86 | |
JonFreeman | 0:435bf84ce48a | 87 | /** void ISR_loop_timer () |
JonFreeman | 0:435bf84ce48a | 88 | * This ISR responds to Ticker interrupts at a rate of (probably) 32 times per second (check from constant declarations above) |
JonFreeman | 0:435bf84ce48a | 89 | * This ISR sets global flag 'loop_flag' used to synchronise passes around main programme control loop. |
JonFreeman | 0:435bf84ce48a | 90 | * Increments global 'sys_timer', usable anywhere as general measure of elapsed time |
JonFreeman | 0:435bf84ce48a | 91 | */ |
JonFreeman | 0:435bf84ce48a | 92 | void ISR_loop_timer () // This is Ticker Interrupt Service Routine - loop timer - MAIN_LOOP_REPEAT_TIME_US |
JonFreeman | 0:435bf84ce48a | 93 | { |
JonFreeman | 0:435bf84ce48a | 94 | loop_flag = true; // set flag to allow main programme loop to proceed |
JonFreeman | 0:435bf84ce48a | 95 | sys_timer++; // Just a handy measure of elapsed time for anything to use |
JonFreeman | 0:435bf84ce48a | 96 | if ((sys_timer & 0x03) == 0) |
JonFreeman | 0:435bf84ce48a | 97 | flag_8Hz = true; |
JonFreeman | 0:435bf84ce48a | 98 | } |
JonFreeman | 0:435bf84ce48a | 99 | |
JonFreeman | 0:435bf84ce48a | 100 | /** void ISR_voltage_reader () |
JonFreeman | 0:435bf84ce48a | 101 | * This ISR responds to Ticker interrupts every 'VOLTAGE_READ_INTERVAL_US' micro seconds |
JonFreeman | 0:435bf84ce48a | 102 | * |
JonFreeman | 0:435bf84ce48a | 103 | * AtoD_reader() called from convenient point in code to take readings outside of ISRs |
JonFreeman | 0:435bf84ce48a | 104 | */ |
JonFreeman | 0:435bf84ce48a | 105 | |
JonFreeman | 5:ca86a7848d54 | 106 | void ISR_voltage_reader () // This is Ticker Interrupt Service Routine ; few us between readings ; VOLTAGE_READ_INTERVAL_US = 50 |
JonFreeman | 0:435bf84ce48a | 107 | { |
JonFreeman | 0:435bf84ce48a | 108 | AtoD_Semaphore++; |
JonFreeman | 2:04761b196473 | 109 | fast_sys_timer++; // Just a handy measure of elapsed time for anything to use |
JonFreeman | 0:435bf84ce48a | 110 | } |
JonFreeman | 0:435bf84ce48a | 111 | |
JonFreeman | 0:435bf84ce48a | 112 | |
JonFreeman | 10:e40d8724268a | 113 | /**class RControl_In |
JonFreeman | 10:e40d8724268a | 114 | Checks for __-__ duration 800-2200us |
JonFreeman | 10:e40d8724268a | 115 | Checks repetition rate in range 5-25ms |
JonFreeman | 10:e40d8724268a | 116 | */ |
JonFreeman | 4:21d91465e4b1 | 117 | class RControl_In |
JonFreeman | 8:93203f473f6e | 118 | { |
JonFreeman | 8:93203f473f6e | 119 | // Read servo style pwm input |
JonFreeman | 0:435bf84ce48a | 120 | Timer t; |
JonFreeman | 10:e40d8724268a | 121 | int32_t pulse_width_us, period_us, pulse_count; |
JonFreeman | 4:21d91465e4b1 | 122 | public: |
JonFreeman | 10:e40d8724268a | 123 | RControl_In () { // Default Constructor |
JonFreeman | 10:e40d8724268a | 124 | pulse_width_us = period_us = pulse_count = 0; |
JonFreeman | 10:e40d8724268a | 125 | rx_active = false; |
JonFreeman | 10:e40d8724268a | 126 | com2.printf ("Setting up Radio_Control_In\r\n"); |
JonFreeman | 4:21d91465e4b1 | 127 | } ; |
JonFreeman | 0:435bf84ce48a | 128 | bool validate_rx () ; |
JonFreeman | 10:e40d8724268a | 129 | void rise () ; // InterruptIn ISRs redirected to these |
JonFreeman | 0:435bf84ce48a | 130 | void fall () ; |
JonFreeman | 10:e40d8724268a | 131 | uint32_t pulsecount () ; |
JonFreeman | 4:21d91465e4b1 | 132 | uint32_t pulsewidth () ; |
JonFreeman | 4:21d91465e4b1 | 133 | uint32_t period () ; |
JonFreeman | 10:e40d8724268a | 134 | double normalised (); // Returns 0.0 <= p <= 1.0 or something else when rc not active |
JonFreeman | 4:21d91465e4b1 | 135 | bool rx_active; |
JonFreeman | 4:21d91465e4b1 | 136 | } ; |
JonFreeman | 0:435bf84ce48a | 137 | |
JonFreeman | 10:e40d8724268a | 138 | |
JonFreeman | 8:93203f473f6e | 139 | uint32_t RControl_In::pulsewidth () |
JonFreeman | 8:93203f473f6e | 140 | { |
JonFreeman | 4:21d91465e4b1 | 141 | return pulse_width_us; |
JonFreeman | 4:21d91465e4b1 | 142 | } |
JonFreeman | 4:21d91465e4b1 | 143 | |
JonFreeman | 10:e40d8724268a | 144 | uint32_t RControl_In::pulsecount () |
JonFreeman | 10:e40d8724268a | 145 | { |
JonFreeman | 10:e40d8724268a | 146 | return pulse_count; |
JonFreeman | 10:e40d8724268a | 147 | } |
JonFreeman | 10:e40d8724268a | 148 | |
JonFreeman | 8:93203f473f6e | 149 | uint32_t RControl_In::period () |
JonFreeman | 8:93203f473f6e | 150 | { |
JonFreeman | 4:21d91465e4b1 | 151 | return period_us; |
JonFreeman | 4:21d91465e4b1 | 152 | } |
JonFreeman | 4:21d91465e4b1 | 153 | |
JonFreeman | 4:21d91465e4b1 | 154 | bool RControl_In::validate_rx () |
JonFreeman | 10:e40d8724268a | 155 | { // Tests for pulse width and repetition rates being believable |
JonFreeman | 10:e40d8724268a | 156 | if ((period_us < 5000) || (period_us > 25000) || (pulse_width_us < 800) || (pulse_width_us > 2200)) |
JonFreeman | 10:e40d8724268a | 157 | { |
JonFreeman | 0:435bf84ce48a | 158 | rx_active = false; |
JonFreeman | 10:e40d8724268a | 159 | pc.printf ("RC per=%d, pw=%d\r\n", period_us, pulse_width_us); |
JonFreeman | 10:e40d8724268a | 160 | } |
JonFreeman | 0:435bf84ce48a | 161 | else |
JonFreeman | 0:435bf84ce48a | 162 | rx_active = true; |
JonFreeman | 0:435bf84ce48a | 163 | return rx_active; |
JonFreeman | 0:435bf84ce48a | 164 | } |
JonFreeman | 0:435bf84ce48a | 165 | |
JonFreeman | 10:e40d8724268a | 166 | double RControl_In::normalised () { |
JonFreeman | 10:e40d8724268a | 167 | if (!validate_rx()) |
JonFreeman | 10:e40d8724268a | 168 | return 0.0; // ** WHAT TO RETURN WHEN RC NOT ACTIVE ? ** |
JonFreeman | 10:e40d8724268a | 169 | double norm = (double) (pulse_width_us - 1000); // left with -200 to + 1200 allowing for some margin |
JonFreeman | 10:e40d8724268a | 170 | norm /= 1000.0; |
JonFreeman | 10:e40d8724268a | 171 | if (norm > 1.0) |
JonFreeman | 10:e40d8724268a | 172 | norm = 1.0; |
JonFreeman | 10:e40d8724268a | 173 | if (norm < 0.0) |
JonFreeman | 10:e40d8724268a | 174 | norm = 0.0; |
JonFreeman | 10:e40d8724268a | 175 | return norm; |
JonFreeman | 10:e40d8724268a | 176 | } |
JonFreeman | 10:e40d8724268a | 177 | |
JonFreeman | 10:e40d8724268a | 178 | void RControl_In::rise () // These may not work as use of PortB as port may bugger InterruptIn use ** THIS IS SO ** |
JonFreeman | 10:e40d8724268a | 179 | { // December 2018 - ** FIXED ** by using PC_14 and 15 instead |
JonFreeman | 10:e40d8724268a | 180 | // t.stop (); |
JonFreeman | 0:435bf84ce48a | 181 | period_us = t.read_us (); |
JonFreeman | 0:435bf84ce48a | 182 | t.reset (); |
JonFreeman | 0:435bf84ce48a | 183 | t.start (); |
JonFreeman | 0:435bf84ce48a | 184 | } |
JonFreeman | 4:21d91465e4b1 | 185 | void RControl_In::fall () |
JonFreeman | 0:435bf84ce48a | 186 | { |
JonFreeman | 0:435bf84ce48a | 187 | pulse_width_us = t.read_us (); |
JonFreeman | 10:e40d8724268a | 188 | pulse_count++; |
JonFreeman | 10:e40d8724268a | 189 | } |
JonFreeman | 10:e40d8724268a | 190 | // end of RControl_In class |
JonFreeman | 10:e40d8724268a | 191 | |
JonFreeman | 10:e40d8724268a | 192 | RControl_In RC_chan_1, RC_chan_2; // Declare two radio control input channels |
JonFreeman | 10:e40d8724268a | 193 | |
JonFreeman | 10:e40d8724268a | 194 | // Radio Control Input Interrupt Handlers |
JonFreeman | 10:e40d8724268a | 195 | void RC_chan_1rise () { |
JonFreeman | 10:e40d8724268a | 196 | RC_chan_1.rise (); |
JonFreeman | 0:435bf84ce48a | 197 | } |
JonFreeman | 10:e40d8724268a | 198 | void RC_chan_1fall () { |
JonFreeman | 10:e40d8724268a | 199 | RC_chan_1.fall (); |
JonFreeman | 10:e40d8724268a | 200 | } |
JonFreeman | 10:e40d8724268a | 201 | void RC_chan_2rise () { |
JonFreeman | 10:e40d8724268a | 202 | RC_chan_2.rise (); |
JonFreeman | 10:e40d8724268a | 203 | } |
JonFreeman | 10:e40d8724268a | 204 | void RC_chan_2fall () { |
JonFreeman | 10:e40d8724268a | 205 | RC_chan_2.fall (); |
JonFreeman | 10:e40d8724268a | 206 | } |
JonFreeman | 10:e40d8724268a | 207 | // End of Radio Control Input Interrupt Handlers |
JonFreeman | 0:435bf84ce48a | 208 | |
JonFreeman | 10:e40d8724268a | 209 | //Servo * Servos[2]; // Is possible to create pointers to Servo but no need to. |
JonFreeman | 4:21d91465e4b1 | 210 | |
JonFreeman | 0:435bf84ce48a | 211 | //uint32_t HAtot = 0, HBtot = 0, A_Offset = 0, B_Offset = 0; |
JonFreeman | 0:435bf84ce48a | 212 | /* |
JonFreeman | 0:435bf84ce48a | 213 | 5 1 3 2 6 4 SENSOR SEQUENCE |
JonFreeman | 0:435bf84ce48a | 214 | |
JonFreeman | 3:ecb00e0e8d68 | 215 | 1 1 1 1 0 0 0 ---___---___ Hall1 |
JonFreeman | 3:ecb00e0e8d68 | 216 | 2 0 0 1 1 1 0 __---___---_ Hall2 |
JonFreeman | 3:ecb00e0e8d68 | 217 | 4 1 0 0 0 1 1 -___---___-- Hall3 |
JonFreeman | 0:435bf84ce48a | 218 | |
JonFreeman | 0:435bf84ce48a | 219 | UV WV WU VU VW UW OUTPUT SEQUENCE |
JonFreeman | 8:93203f473f6e | 220 | |
JonFreeman | 8:93203f473f6e | 221 | 8th July 2018 |
JonFreeman | 8:93203f473f6e | 222 | Added drive to DC brushed motors. |
JonFreeman | 8:93203f473f6e | 223 | Connect U and W to dc motor, leave V open. |
JonFreeman | 8:93203f473f6e | 224 | |
JonFreeman | 8:93203f473f6e | 225 | 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 | 226 | |
JonFreeman | 0:435bf84ce48a | 227 | */ |
JonFreeman | 3:ecb00e0e8d68 | 228 | const uint16_t A_tabl[] = { // Origial table |
JonFreeman | 0:435bf84ce48a | 229 | 0, 0, 0, 0, 0, 0, 0, 0, // Handbrake |
JonFreeman | 10:e40d8724268a | 230 | 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 | 231 | 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 | 232 | 0, BRA,BRA,BRA,BRA,BRA,BRA,BRA, // Regenerative Braking |
JonFreeman | 4:21d91465e4b1 | 233 | KEEP_L_MASK_A, KEEP_H_MASK_A // [32 and 33] |
JonFreeman | 3:ecb00e0e8d68 | 234 | } ; |
JonFreeman | 4:21d91465e4b1 | 235 | InterruptIn * AHarr[] = { |
JonFreeman | 4:21d91465e4b1 | 236 | &MAH1, |
JonFreeman | 4:21d91465e4b1 | 237 | &MAH2, |
JonFreeman | 4:21d91465e4b1 | 238 | &MAH3 |
JonFreeman | 3:ecb00e0e8d68 | 239 | } ; |
JonFreeman | 0:435bf84ce48a | 240 | const uint16_t B_tabl[] = { |
JonFreeman | 0:435bf84ce48a | 241 | 0, 0, 0, 0, 0, 0, 0, 0, // Handbrake |
JonFreeman | 10:e40d8724268a | 242 | 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 | 243 | 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 | 244 | 0, BRB,BRB,BRB,BRB,BRB,BRB,BRB, // Regenerative Braking |
JonFreeman | 4:21d91465e4b1 | 245 | KEEP_L_MASK_B, KEEP_H_MASK_B |
JonFreeman | 3:ecb00e0e8d68 | 246 | } ; |
JonFreeman | 4:21d91465e4b1 | 247 | InterruptIn * BHarr[] = { |
JonFreeman | 4:21d91465e4b1 | 248 | &MBH1, |
JonFreeman | 4:21d91465e4b1 | 249 | &MBH2, |
JonFreeman | 4:21d91465e4b1 | 250 | &MBH3 |
JonFreeman | 0:435bf84ce48a | 251 | } ; |
JonFreeman | 0:435bf84ce48a | 252 | |
JonFreeman | 0:435bf84ce48a | 253 | |
JonFreeman | 10:e40d8724268a | 254 | brushless_motor MotorA (&MotA, &A_MAX_V_PWM, &A_MAX_I_PWM, A_tabl, AHarr); |
JonFreeman | 10:e40d8724268a | 255 | brushless_motor MotorB (&MotB, &B_MAX_V_PWM, &B_MAX_I_PWM, B_tabl, BHarr); |
JonFreeman | 2:04761b196473 | 256 | |
JonFreeman | 5:ca86a7848d54 | 257 | |
JonFreeman | 8:93203f473f6e | 258 | void report_motor_types () // not very good test, shows 'Brushless' if Hall inputs read 1 to 6, 'DC' otherwise |
JonFreeman | 8:93203f473f6e | 259 | { |
JonFreeman | 8:93203f473f6e | 260 | pc.printf ("Mot A is %s, Mot B is %s\r\n", MotorA.motor_is_brushless() ? "Brushless":"DC", MotorB.motor_is_brushless() ? "Brushless":"DC"); |
JonFreeman | 8:93203f473f6e | 261 | } |
JonFreeman | 8:93203f473f6e | 262 | |
JonFreeman | 8:93203f473f6e | 263 | void temp_sensor_isr () // got rising edge from LMT01. ALMOST CERTAIN this misses some |
JonFreeman | 8:93203f473f6e | 264 | { |
JonFreeman | 6:f289a49c1eae | 265 | int t = temperature_timer.read_us (); // Must be being overrun by something, most likely culprit A-D reading ? |
JonFreeman | 6:f289a49c1eae | 266 | temperature_timer.reset (); |
JonFreeman | 5:ca86a7848d54 | 267 | temp_sensor_count++; |
JonFreeman | 6:f289a49c1eae | 268 | if (t > 18) // Yes proved some interrupts get missed, this fixes temperature reading |
JonFreeman | 6:f289a49c1eae | 269 | temp_sensor_count++; |
JonFreeman | 6:f289a49c1eae | 270 | // T2 = !T2; // scope hanger |
JonFreeman | 5:ca86a7848d54 | 271 | } |
JonFreeman | 5:ca86a7848d54 | 272 | |
JonFreeman | 8:93203f473f6e | 273 | |
JonFreeman | 3:ecb00e0e8d68 | 274 | void MAH_isr () |
JonFreeman | 0:435bf84ce48a | 275 | { |
JonFreeman | 3:ecb00e0e8d68 | 276 | uint32_t x = 0; |
JonFreeman | 3:ecb00e0e8d68 | 277 | MotorA.high_side_off (); |
JonFreeman | 8:93203f473f6e | 278 | // T3 = !T3; |
JonFreeman | 3:ecb00e0e8d68 | 279 | if (MAH1 != 0) x |= 1; |
JonFreeman | 3:ecb00e0e8d68 | 280 | if (MAH2 != 0) x |= 2; |
JonFreeman | 3:ecb00e0e8d68 | 281 | if (MAH3 != 0) x |= 4; |
JonFreeman | 3:ecb00e0e8d68 | 282 | MotorA.Hindex[0] = x; // New in [0], old in [1] |
JonFreeman | 0:435bf84ce48a | 283 | MotorA.Hall_change (); |
JonFreeman | 0:435bf84ce48a | 284 | } |
JonFreeman | 0:435bf84ce48a | 285 | |
JonFreeman | 3:ecb00e0e8d68 | 286 | void MBH_isr () |
JonFreeman | 0:435bf84ce48a | 287 | { |
JonFreeman | 3:ecb00e0e8d68 | 288 | uint32_t x = 0; |
JonFreeman | 3:ecb00e0e8d68 | 289 | MotorB.high_side_off (); |
JonFreeman | 3:ecb00e0e8d68 | 290 | if (MBH1 != 0) x |= 1; |
JonFreeman | 3:ecb00e0e8d68 | 291 | if (MBH2 != 0) x |= 2; |
JonFreeman | 3:ecb00e0e8d68 | 292 | if (MBH3 != 0) x |= 4; |
JonFreeman | 3:ecb00e0e8d68 | 293 | MotorB.Hindex[0] = x; |
JonFreeman | 0:435bf84ce48a | 294 | MotorB.Hall_change (); |
JonFreeman | 0:435bf84ce48a | 295 | } |
JonFreeman | 0:435bf84ce48a | 296 | |
JonFreeman | 0:435bf84ce48a | 297 | |
JonFreeman | 0:435bf84ce48a | 298 | // End of Interrupt Service Routines |
JonFreeman | 0:435bf84ce48a | 299 | |
JonFreeman | 8:93203f473f6e | 300 | void setVI (double v, double i) |
JonFreeman | 8:93203f473f6e | 301 | { |
JonFreeman | 4:21d91465e4b1 | 302 | MotorA.set_V_limit (v); // Sets max motor voltage |
JonFreeman | 4:21d91465e4b1 | 303 | MotorA.set_I_limit (i); // Sets max motor current |
JonFreeman | 4:21d91465e4b1 | 304 | MotorB.set_V_limit (v); |
JonFreeman | 4:21d91465e4b1 | 305 | MotorB.set_I_limit (i); |
JonFreeman | 4:21d91465e4b1 | 306 | } |
JonFreeman | 8:93203f473f6e | 307 | void setV (double v) |
JonFreeman | 8:93203f473f6e | 308 | { |
JonFreeman | 2:04761b196473 | 309 | MotorA.set_V_limit (v); |
JonFreeman | 4:21d91465e4b1 | 310 | MotorB.set_V_limit (v); |
JonFreeman | 4:21d91465e4b1 | 311 | } |
JonFreeman | 8:93203f473f6e | 312 | void setI (double i) |
JonFreeman | 8:93203f473f6e | 313 | { |
JonFreeman | 2:04761b196473 | 314 | MotorA.set_I_limit (i); |
JonFreeman | 2:04761b196473 | 315 | MotorB.set_I_limit (i); |
JonFreeman | 0:435bf84ce48a | 316 | } |
JonFreeman | 2:04761b196473 | 317 | |
JonFreeman | 8:93203f473f6e | 318 | void read_RPM (uint32_t * dest) |
JonFreeman | 8:93203f473f6e | 319 | { |
JonFreeman | 5:ca86a7848d54 | 320 | dest[0] = MotorA.RPM; |
JonFreeman | 5:ca86a7848d54 | 321 | dest[1] = MotorB.RPM; |
JonFreeman | 5:ca86a7848d54 | 322 | } |
JonFreeman | 5:ca86a7848d54 | 323 | |
JonFreeman | 8:93203f473f6e | 324 | void read_PPS (uint32_t * dest) |
JonFreeman | 8:93203f473f6e | 325 | { |
JonFreeman | 5:ca86a7848d54 | 326 | dest[0] = MotorA.PPS; |
JonFreeman | 5:ca86a7848d54 | 327 | dest[1] = MotorB.PPS; |
JonFreeman | 5:ca86a7848d54 | 328 | } |
JonFreeman | 5:ca86a7848d54 | 329 | |
JonFreeman | 8:93203f473f6e | 330 | void read_last_VI (double * d) // only for test from cli |
JonFreeman | 8:93203f473f6e | 331 | { |
JonFreeman | 5:ca86a7848d54 | 332 | d[0] = MotorA.last_V; |
JonFreeman | 5:ca86a7848d54 | 333 | d[1] = MotorA.last_I; |
JonFreeman | 5:ca86a7848d54 | 334 | d[2] = MotorB.last_V; |
JonFreeman | 5:ca86a7848d54 | 335 | d[3] = MotorB.last_I; |
JonFreeman | 5:ca86a7848d54 | 336 | } |
JonFreeman | 5:ca86a7848d54 | 337 | |
JonFreeman | 3:ecb00e0e8d68 | 338 | |
JonFreeman | 5:ca86a7848d54 | 339 | /** |
JonFreeman | 5:ca86a7848d54 | 340 | void AtoD_reader () // Call to here every VOLTAGE_READ_INTERVAL_US = 50 once loop responds to flag set in isr |
JonFreeman | 5:ca86a7848d54 | 341 | Not part of ISR |
JonFreeman | 5:ca86a7848d54 | 342 | */ |
JonFreeman | 3:ecb00e0e8d68 | 343 | void AtoD_reader () // Call to here every VOLTAGE_READ_INTERVAL_US = 50 once loop responds to flag set in isr |
JonFreeman | 0:435bf84ce48a | 344 | { |
JonFreeman | 6:f289a49c1eae | 345 | static uint32_t i = 0, tab_ptr = 0; |
JonFreeman | 8:93203f473f6e | 346 | // if (MotorA.dc_motor) { |
JonFreeman | 8:93203f473f6e | 347 | // MotorA.low_side_on (); |
JonFreeman | 8:93203f473f6e | 348 | // } |
JonFreeman | 8:93203f473f6e | 349 | // else { |
JonFreeman | 3:ecb00e0e8d68 | 350 | if (MotorA.tickleon) |
JonFreeman | 3:ecb00e0e8d68 | 351 | MotorA.high_side_off (); |
JonFreeman | 8:93203f473f6e | 352 | // } |
JonFreeman | 8:93203f473f6e | 353 | // if (MotorB.dc_motor) { |
JonFreeman | 8:93203f473f6e | 354 | // MotorB.low_side_on (); |
JonFreeman | 8:93203f473f6e | 355 | // } |
JonFreeman | 8:93203f473f6e | 356 | // else { |
JonFreeman | 3:ecb00e0e8d68 | 357 | if (MotorB.tickleon) |
JonFreeman | 3:ecb00e0e8d68 | 358 | MotorB.high_side_off (); |
JonFreeman | 8:93203f473f6e | 359 | // } |
JonFreeman | 0:435bf84ce48a | 360 | if (AtoD_Semaphore > 20) { |
JonFreeman | 8:93203f473f6e | 361 | pc.printf ("WARNING - sema cnt %d\r\n", AtoD_Semaphore); |
JonFreeman | 0:435bf84ce48a | 362 | AtoD_Semaphore = 20; |
JonFreeman | 0:435bf84ce48a | 363 | } |
JonFreeman | 0:435bf84ce48a | 364 | while (AtoD_Semaphore > 0) { |
JonFreeman | 0:435bf84ce48a | 365 | AtoD_Semaphore--; |
JonFreeman | 0:435bf84ce48a | 366 | // Code here to sequence through reading voltmeter, demand pot, ammeters |
JonFreeman | 0:435bf84ce48a | 367 | switch (i) { // |
JonFreeman | 0:435bf84ce48a | 368 | case 0: |
JonFreeman | 0:435bf84ce48a | 369 | volt_reading += Ain_SystemVolts.read_u16 (); // Result = Result + New Reading |
JonFreeman | 0:435bf84ce48a | 370 | volt_reading >>= 1; // Result = Result / 2 |
JonFreeman | 0:435bf84ce48a | 371 | break; // i.e. Very simple digital low pass filter |
JonFreeman | 0:435bf84ce48a | 372 | case 1: |
JonFreeman | 0:435bf84ce48a | 373 | driverpot_reading += Ain_DriverPot.read_u16 (); |
JonFreeman | 0:435bf84ce48a | 374 | driverpot_reading >>= 1; |
JonFreeman | 0:435bf84ce48a | 375 | break; |
JonFreeman | 0:435bf84ce48a | 376 | case 2: |
JonFreeman | 0:435bf84ce48a | 377 | MotorA.current_samples[tab_ptr] = Motor_A_Current.read_u16 (); // |
JonFreeman | 0:435bf84ce48a | 378 | break; |
JonFreeman | 0:435bf84ce48a | 379 | case 3: |
JonFreeman | 0:435bf84ce48a | 380 | MotorB.current_samples[tab_ptr++] = Motor_B_Current.read_u16 (); // |
JonFreeman | 0:435bf84ce48a | 381 | if (tab_ptr >= CURRENT_SAMPLES_AVERAGED) // Current reading will be lumpy and spikey, so put through moving average filter |
JonFreeman | 0:435bf84ce48a | 382 | tab_ptr = 0; |
JonFreeman | 0:435bf84ce48a | 383 | break; |
JonFreeman | 0:435bf84ce48a | 384 | } |
JonFreeman | 0:435bf84ce48a | 385 | i++; // prepare to read the next input in response to the next interrupt |
JonFreeman | 0:435bf84ce48a | 386 | if (i > 3) |
JonFreeman | 0:435bf84ce48a | 387 | i = 0; |
JonFreeman | 3:ecb00e0e8d68 | 388 | } // end of while (AtoD_Semaphore > 0) { |
JonFreeman | 3:ecb00e0e8d68 | 389 | if (MotorA.tickleon) { |
JonFreeman | 8:93203f473f6e | 390 | // if (MotorA.dc_motor || MotorA.tickleon) { |
JonFreeman | 3:ecb00e0e8d68 | 391 | MotorA.tickleon--; |
JonFreeman | 5:ca86a7848d54 | 392 | MotorA.motor_set (); // Reactivate any high side switches turned off above |
JonFreeman | 3:ecb00e0e8d68 | 393 | } |
JonFreeman | 3:ecb00e0e8d68 | 394 | if (MotorB.tickleon) { |
JonFreeman | 8:93203f473f6e | 395 | // if (MotorB.dc_motor || MotorB.tickleon) { |
JonFreeman | 3:ecb00e0e8d68 | 396 | MotorB.tickleon--; |
JonFreeman | 3:ecb00e0e8d68 | 397 | MotorB.motor_set (); |
JonFreeman | 0:435bf84ce48a | 398 | } |
JonFreeman | 0:435bf84ce48a | 399 | } |
JonFreeman | 0:435bf84ce48a | 400 | |
JonFreeman | 10:e40d8724268a | 401 | /** double Read_Servo1_In () |
JonFreeman | 10:e40d8724268a | 402 | * driverpot_reading is a global 16 bit unsigned int updated in interrupt service routine |
JonFreeman | 10:e40d8724268a | 403 | * ISR also filters signal |
JonFreeman | 10:e40d8724268a | 404 | * This function returns normalised double (range 0.0 to 1.0) representation of driver pot position |
JonFreeman | 10:e40d8724268a | 405 | */ |
JonFreeman | 10:e40d8724268a | 406 | double Read_Servo1_In () |
JonFreeman | 10:e40d8724268a | 407 | { |
JonFreeman | 10:e40d8724268a | 408 | const double xjoin = 0.5, |
JonFreeman | 10:e40d8724268a | 409 | yjoin = 0.35, |
JonFreeman | 10:e40d8724268a | 410 | slope_a = yjoin / xjoin, |
JonFreeman | 10:e40d8724268a | 411 | slope_b = (1.0 - yjoin)/(1.0 - xjoin); |
JonFreeman | 10:e40d8724268a | 412 | // centre = 0.35, // For pot, first (1/3)ish in braking area, position = 1/3 drift, > (1/3)ish drive |
JonFreeman | 10:e40d8724268a | 413 | // halfish = 0.5; // RC stick in the centre value |
JonFreeman | 10:e40d8724268a | 414 | // Bottom half of RC stick movement maps to lowest (1/3)ish pot movement |
JonFreeman | 10:e40d8724268a | 415 | // Higher half of RC stick movement maps to highest (2/3)ish pot movement |
JonFreeman | 10:e40d8724268a | 416 | double t; |
JonFreeman | 10:e40d8724268a | 417 | double demand = RC_chan_1.normalised(); |
JonFreeman | 10:e40d8724268a | 418 | // apply demand = 1.0 - demand here to swap stick move polarity |
JonFreeman | 10:e40d8724268a | 419 | // return pow (demand, SERVOIN_PWR_BENDER); |
JonFreeman | 10:e40d8724268a | 420 | if (demand < 0.0) demand = 0.0; |
JonFreeman | 10:e40d8724268a | 421 | if (demand > 1.0) demand = 1.0; |
JonFreeman | 10:e40d8724268a | 422 | if (demand < xjoin) { |
JonFreeman | 10:e40d8724268a | 423 | demand *= slope_a; |
JonFreeman | 10:e40d8724268a | 424 | } |
JonFreeman | 10:e40d8724268a | 425 | else { |
JonFreeman | 10:e40d8724268a | 426 | t = yjoin + ((demand - xjoin) * slope_b); |
JonFreeman | 10:e40d8724268a | 427 | demand = t; |
JonFreeman | 10:e40d8724268a | 428 | } |
JonFreeman | 10:e40d8724268a | 429 | return demand; |
JonFreeman | 10:e40d8724268a | 430 | } |
JonFreeman | 10:e40d8724268a | 431 | |
JonFreeman | 0:435bf84ce48a | 432 | /** double Read_DriverPot () |
JonFreeman | 0:435bf84ce48a | 433 | * driverpot_reading is a global 16 bit unsigned int updated in interrupt service routine |
JonFreeman | 10:e40d8724268a | 434 | * ISR also filters signal by returning average of most recent two readings |
JonFreeman | 0:435bf84ce48a | 435 | * This function returns normalised double (range 0.0 to 1.0) representation of driver pot position |
JonFreeman | 0:435bf84ce48a | 436 | */ |
JonFreeman | 0:435bf84ce48a | 437 | double Read_DriverPot () |
JonFreeman | 0:435bf84ce48a | 438 | { |
JonFreeman | 5:ca86a7848d54 | 439 | return ((double) driverpot_reading) / 65536.0; // Normalise 0.0 <= control pot <= 1.0 |
JonFreeman | 0:435bf84ce48a | 440 | } |
JonFreeman | 0:435bf84ce48a | 441 | |
JonFreeman | 0:435bf84ce48a | 442 | double Read_BatteryVolts () |
JonFreeman | 0:435bf84ce48a | 443 | { |
JonFreeman | 5:ca86a7848d54 | 444 | return ((double) volt_reading) / 951.0; // divisor fiddled to make voltage reading correct ! |
JonFreeman | 0:435bf84ce48a | 445 | } |
JonFreeman | 0:435bf84ce48a | 446 | |
JonFreeman | 8:93203f473f6e | 447 | void read_supply_vi (double * val) // called from cli |
JonFreeman | 8:93203f473f6e | 448 | { |
JonFreeman | 5:ca86a7848d54 | 449 | val[0] = MotorA.I.ave; |
JonFreeman | 5:ca86a7848d54 | 450 | val[1] = MotorB.I.ave; |
JonFreeman | 5:ca86a7848d54 | 451 | val[2] = Read_BatteryVolts (); |
JonFreeman | 0:435bf84ce48a | 452 | } |
JonFreeman | 0:435bf84ce48a | 453 | |
JonFreeman | 8:93203f473f6e | 454 | void mode_set_both_motors (int mode, double val) // called from cli to set fw, re, rb, hb |
JonFreeman | 8:93203f473f6e | 455 | { |
JonFreeman | 2:04761b196473 | 456 | MotorA.set_mode (mode); |
JonFreeman | 2:04761b196473 | 457 | MotorB.set_mode (mode); |
JonFreeman | 2:04761b196473 | 458 | if (mode == REGENBRAKE) { |
JonFreeman | 5:ca86a7848d54 | 459 | if (val > 1.0) |
JonFreeman | 5:ca86a7848d54 | 460 | val = 1.0; |
JonFreeman | 5:ca86a7848d54 | 461 | if (val < 0.0) |
JonFreeman | 5:ca86a7848d54 | 462 | val = 0.0; |
JonFreeman | 5:ca86a7848d54 | 463 | val *= 0.9; // set upper limit, this is essential |
JonFreeman | 5:ca86a7848d54 | 464 | val = sqrt (val); // to linearise effect |
JonFreeman | 5:ca86a7848d54 | 465 | setVI (val, 1.0); |
JonFreeman | 2:04761b196473 | 466 | } |
JonFreeman | 2:04761b196473 | 467 | } |
JonFreeman | 0:435bf84ce48a | 468 | |
JonFreeman | 7:6deaeace9a3e | 469 | extern void setup_comms () ; |
JonFreeman | 7:6deaeace9a3e | 470 | extern void command_line_interpreter_pc () ; |
JonFreeman | 7:6deaeace9a3e | 471 | extern void command_line_interpreter_loco () ; |
JonFreeman | 0:435bf84ce48a | 472 | extern int check_24LC64 () ; // Call from near top of main() to init i2c bus |
JonFreeman | 0:435bf84ce48a | 473 | extern bool wr_24LC64 (int mem_start_addr, char * source, int length) ; |
JonFreeman | 0:435bf84ce48a | 474 | extern bool rd_24LC64 (int mem_start_addr, char * dest, int length) ; |
JonFreeman | 0:435bf84ce48a | 475 | |
JonFreeman | 5:ca86a7848d54 | 476 | /*struct motorpairoptions { // This to be user settable in eeprom, 32 bytes |
JonFreeman | 3:ecb00e0e8d68 | 477 | uint8_t MotA_dir, // 0 or 1 |
JonFreeman | 3:ecb00e0e8d68 | 478 | MotB_dir, // 0 or 1 |
JonFreeman | 3:ecb00e0e8d68 | 479 | gang, // 0 for separate control (robot mode), 1 for ganged loco bogie mode |
JonFreeman | 3:ecb00e0e8d68 | 480 | serv1, // 0, 1, 2 = Not used, Input, Output |
JonFreeman | 3:ecb00e0e8d68 | 481 | serv2, // 0, 1, 2 = Not used, Input, Output |
JonFreeman | 3:ecb00e0e8d68 | 482 | cmd_source, // 0 Invalid, 1 COM1, 2 COM2, 3 Pot, 4 Servo1, 5 Servo2 |
JonFreeman | 3:ecb00e0e8d68 | 483 | last; |
JonFreeman | 3:ecb00e0e8d68 | 484 | } ; |
JonFreeman | 5:ca86a7848d54 | 485 | */ |
JonFreeman | 8:93203f473f6e | 486 | int I_Am () // Returns boards id number as ASCII char |
JonFreeman | 8:93203f473f6e | 487 | { |
JonFreeman | 5:ca86a7848d54 | 488 | return IAm; |
JonFreeman | 3:ecb00e0e8d68 | 489 | } |
JonFreeman | 3:ecb00e0e8d68 | 490 | |
JonFreeman | 8:93203f473f6e | 491 | double mph (int rpm) |
JonFreeman | 8:93203f473f6e | 492 | { |
JonFreeman | 7:6deaeace9a3e | 493 | if (mode_good_flag) { |
JonFreeman | 7:6deaeace9a3e | 494 | return rpm2mph * (double) rpm; |
JonFreeman | 7:6deaeace9a3e | 495 | } |
JonFreeman | 7:6deaeace9a3e | 496 | return -1.0; |
JonFreeman | 7:6deaeace9a3e | 497 | } |
JonFreeman | 4:21d91465e4b1 | 498 | |
JonFreeman | 8:93203f473f6e | 499 | void restorer () |
JonFreeman | 8:93203f473f6e | 500 | { |
JonFreeman | 8:93203f473f6e | 501 | motors_restore.detach (); |
JonFreeman | 8:93203f473f6e | 502 | if (MotorA.dc_motor) { |
JonFreeman | 8:93203f473f6e | 503 | MotorA.set_V_limit (MotorA.last_V); |
JonFreeman | 8:93203f473f6e | 504 | MotorA.set_I_limit (MotorA.last_I); |
JonFreeman | 8:93203f473f6e | 505 | MotorA.motor_set (); |
JonFreeman | 8:93203f473f6e | 506 | } |
JonFreeman | 8:93203f473f6e | 507 | if (MotorB.dc_motor) { |
JonFreeman | 8:93203f473f6e | 508 | MotorB.set_V_limit (MotorB.last_V); |
JonFreeman | 8:93203f473f6e | 509 | MotorB.set_I_limit (MotorB.last_I); |
JonFreeman | 8:93203f473f6e | 510 | MotorB.motor_set (); |
JonFreeman | 8:93203f473f6e | 511 | } |
JonFreeman | 8:93203f473f6e | 512 | } |
JonFreeman | 8:93203f473f6e | 513 | |
JonFreeman | 8:93203f473f6e | 514 | //int ttime = 3; // resettable via cli 'stt', used to determine suitable force low on period for driving dc motor |
JonFreeman | 8:93203f473f6e | 515 | |
JonFreeman | 8:93203f473f6e | 516 | void prscfuck (int v) { |
JonFreeman | 8:93203f473f6e | 517 | /* |
JonFreeman | 8:93203f473f6e | 518 | int prescaler ( int value ) |
JonFreeman | 8:93203f473f6e | 519 | |
JonFreeman | 8:93203f473f6e | 520 | Set the PWM prescaler. |
JonFreeman | 8:93203f473f6e | 521 | |
JonFreeman | 8:93203f473f6e | 522 | The period of all PWM pins on the same PWM unit have to be reset after using this! |
JonFreeman | 8:93203f473f6e | 523 | |
JonFreeman | 8:93203f473f6e | 524 | Parameters: |
JonFreeman | 8:93203f473f6e | 525 | value - The required prescaler. Special values: 0 = lock current prescaler, -1 = use dynamic prescaler |
JonFreeman | 8:93203f473f6e | 526 | return - The prescaler which was set (can differ from requested prescaler if not possible) |
JonFreeman | 8:93203f473f6e | 527 | |
JonFreeman | 8:93203f473f6e | 528 | Definition at line 82 of file FastPWM_common.cpp. |
JonFreeman | 8:93203f473f6e | 529 | */ |
JonFreeman | 8:93203f473f6e | 530 | if (v < 1) |
JonFreeman | 8:93203f473f6e | 531 | v = 1; |
JonFreeman | 8:93203f473f6e | 532 | int w = A_MAX_V_PWM.prescaler (v); |
JonFreeman | 8:93203f473f6e | 533 | pc.printf ("Attempt setting prescaler %d returned %d\r\n", v, w); |
JonFreeman | 8:93203f473f6e | 534 | } |
JonFreeman | 8:93203f473f6e | 535 | |
JonFreeman | 10:e40d8724268a | 536 | void rcin_report () { |
JonFreeman | 10:e40d8724268a | 537 | double c1 = RC_chan_1.normalised(); |
JonFreeman | 10:e40d8724268a | 538 | double c2 = RC_chan_2.normalised(); |
JonFreeman | 10:e40d8724268a | 539 | uint32_t pc1 = RC_chan_1.pulsecount(); |
JonFreeman | 10:e40d8724268a | 540 | uint32_t pc2 = RC_chan_2.pulsecount(); |
JonFreeman | 10:e40d8724268a | 541 | pc.printf ("At rcin_report, Ch1=%.3f, Ch2=%.3f, cnt1 %d, cnt2 %d\r\n", c1, c2, pc1, pc2); |
JonFreeman | 10:e40d8724268a | 542 | // if (c1 < -0.0001) |
JonFreeman | 10:e40d8724268a | 543 | pc.printf ("\t1 period %d, pulsewidth %d\r\n", RC_chan_1.period(), RC_chan_1.pulsewidth()); |
JonFreeman | 10:e40d8724268a | 544 | // if (c2 < -0.0001) |
JonFreeman | 10:e40d8724268a | 545 | pc.printf ("\t2 period %d, pulsewidth %d\r\n", RC_chan_2.period(), RC_chan_2.pulsewidth()); |
JonFreeman | 10:e40d8724268a | 546 | } |
JonFreeman | 8:93203f473f6e | 547 | |
JonFreeman | 10:e40d8724268a | 548 | bool worth_the_bother (double a, double b) { |
JonFreeman | 10:e40d8724268a | 549 | double c = a - b; |
JonFreeman | 10:e40d8724268a | 550 | if (c < 0.0) |
JonFreeman | 10:e40d8724268a | 551 | c = 0.0 - c; |
JonFreeman | 10:e40d8724268a | 552 | if (c > 0.02) |
JonFreeman | 10:e40d8724268a | 553 | return true; |
JonFreeman | 10:e40d8724268a | 554 | return false; |
JonFreeman | 10:e40d8724268a | 555 | } |
JonFreeman | 10:e40d8724268a | 556 | |
JonFreeman | 10:e40d8724268a | 557 | void hand_control_state_machine (int source) { // if hand control mode '3', get here @ approx 30Hz to read drivers control pot |
JonFreeman | 10:e40d8724268a | 558 | // if servo1 mode '4', reads input from servo1 instead |
JonFreeman | 10:e40d8724268a | 559 | enum { // states used in hand_control_state_machine() |
JonFreeman | 10:e40d8724268a | 560 | HAND_CONT_IDLE, |
JonFreeman | 10:e40d8724268a | 561 | HAND_CONT_BRAKE_WAIT, |
JonFreeman | 10:e40d8724268a | 562 | HAND_CONT_BRAKE_POT, |
JonFreeman | 10:e40d8724268a | 563 | HAND_CONT_STATE_INTO_BRAKING, |
JonFreeman | 10:e40d8724268a | 564 | HAND_CONT_STATE_BRAKING, |
JonFreeman | 10:e40d8724268a | 565 | HAND_CONT_STATE_INTO_DRIVING, |
JonFreeman | 10:e40d8724268a | 566 | HAND_CONT_STATE_DRIVING |
JonFreeman | 10:e40d8724268a | 567 | } ; |
JonFreeman | 10:e40d8724268a | 568 | |
JonFreeman | 10:e40d8724268a | 569 | static int hand_controller_state = HAND_CONT_IDLE; |
JonFreeman | 10:e40d8724268a | 570 | // static int old_hand_controller_direction = T5; // Nov 2018 reworked thanks to feedback from Rob and Quentin |
JonFreeman | 8:93203f473f6e | 571 | static double brake_effort, drive_effort, pot_position, old_pot_position = 0.0; |
JonFreeman | 10:e40d8724268a | 572 | static int dirbuf[5] = {100,100,100,100,100}; // Initialised with invalid direction such that 'change' is read from switch |
JonFreeman | 10:e40d8724268a | 573 | static int dirbufptr = 0, direction_old = -1, direction_new = -1; |
JonFreeman | 10:e40d8724268a | 574 | const int buflen = sizeof(dirbuf) / sizeof(int); |
JonFreeman | 10:e40d8724268a | 575 | const double Pot_Brake_Range = 0.35; //pow (0.5, SERVOIN_PWR_BENDER); //0.353553 for SERVOIN_PWR_BENDER = 1.5; |
JonFreeman | 10:e40d8724268a | 576 | |
JonFreeman | 10:e40d8724268a | 577 | direction_old = direction_new; |
JonFreeman | 10:e40d8724268a | 578 | |
JonFreeman | 10:e40d8724268a | 579 | // Test for change in direction switch setting. |
JonFreeman | 10:e40d8724268a | 580 | // If whole buffer NEWLY filled with all Fwd or all Rev, state = brake_wait |
JonFreeman | 10:e40d8724268a | 581 | int diracc = 0; |
JonFreeman | 10:e40d8724268a | 582 | if (dirbufptr >= buflen) |
JonFreeman | 10:e40d8724268a | 583 | dirbufptr = 0; |
JonFreeman | 10:e40d8724268a | 584 | dirbuf[dirbufptr++] = T5; // Read direction switch into circular debounce buffer |
JonFreeman | 10:e40d8724268a | 585 | for (int i = 0; i < buflen; i++) |
JonFreeman | 10:e40d8724268a | 586 | diracc += dirbuf[i]; // will = 0 or buflen if direction switch stable |
JonFreeman | 10:e40d8724268a | 587 | if (diracc == buflen || diracc == 0) // if was all 0 or all 1 |
JonFreeman | 10:e40d8724268a | 588 | direction_new = diracc / buflen; |
JonFreeman | 10:e40d8724268a | 589 | if (direction_new != direction_old) |
JonFreeman | 10:e40d8724268a | 590 | hand_controller_state = HAND_CONT_BRAKE_WAIT; // validated change of direction switch |
JonFreeman | 10:e40d8724268a | 591 | |
JonFreeman | 10:e40d8724268a | 592 | switch (source) { |
JonFreeman | 10:e40d8724268a | 593 | case 3: // driver pot is control input |
JonFreeman | 10:e40d8724268a | 594 | pot_position = Read_DriverPot (); // Only place read in the loop, rteads normalised 0.0 to 1.0 |
JonFreeman | 10:e40d8724268a | 595 | break; |
JonFreeman | 10:e40d8724268a | 596 | case 4: // servo 1 input is control input |
JonFreeman | 10:e40d8724268a | 597 | break; |
JonFreeman | 10:e40d8724268a | 598 | default: |
JonFreeman | 10:e40d8724268a | 599 | pot_position = 0.0; |
JonFreeman | 10:e40d8724268a | 600 | break; |
JonFreeman | 8:93203f473f6e | 601 | } |
JonFreeman | 10:e40d8724268a | 602 | |
JonFreeman | 10:e40d8724268a | 603 | switch (hand_controller_state) { |
JonFreeman | 10:e40d8724268a | 604 | case HAND_CONT_IDLE: // Here for first few passes until validated direction switch reading |
JonFreeman | 10:e40d8724268a | 605 | break; |
JonFreeman | 10:e40d8724268a | 606 | |
JonFreeman | 10:e40d8724268a | 607 | case HAND_CONT_BRAKE_WAIT: |
JonFreeman | 10:e40d8724268a | 608 | pc.printf ("At HAND_CONT_BRAKE_WAIT\r\n"); |
JonFreeman | 10:e40d8724268a | 609 | brake_effort = 0.05; // Apply braking not too fiercely |
JonFreeman | 10:e40d8724268a | 610 | mode_set_both_motors (REGENBRAKE, brake_effort); // Direction change |
JonFreeman | 10:e40d8724268a | 611 | hand_controller_state = HAND_CONT_BRAKE_POT; |
JonFreeman | 10:e40d8724268a | 612 | break; |
JonFreeman | 10:e40d8724268a | 613 | |
JonFreeman | 10:e40d8724268a | 614 | case HAND_CONT_BRAKE_POT: |
JonFreeman | 10:e40d8724268a | 615 | if (brake_effort < 0.9) { |
JonFreeman | 10:e40d8724268a | 616 | brake_effort += 0.05; // ramp braking up to max over about one sec |
JonFreeman | 10:e40d8724268a | 617 | mode_set_both_motors (REGENBRAKE, brake_effort); // Direction change |
JonFreeman | 10:e40d8724268a | 618 | pc.printf ("Brake effort %.2f\r\n", brake_effort); |
JonFreeman | 10:e40d8724268a | 619 | } |
JonFreeman | 10:e40d8724268a | 620 | else { // once braking up to quite hard |
JonFreeman | 10:e40d8724268a | 621 | if (pot_position < 0.02) { // Driver has turned pot fully anticlock |
JonFreeman | 10:e40d8724268a | 622 | hand_controller_state = HAND_CONT_STATE_BRAKING; |
JonFreeman | 10:e40d8724268a | 623 | } |
JonFreeman | 8:93203f473f6e | 624 | } |
JonFreeman | 8:93203f473f6e | 625 | break; |
JonFreeman | 10:e40d8724268a | 626 | |
JonFreeman | 8:93203f473f6e | 627 | case HAND_CONT_STATE_INTO_BRAKING: |
JonFreeman | 8:93203f473f6e | 628 | brake_effort = (Pot_Brake_Range - pot_position) / Pot_Brake_Range; |
JonFreeman | 10:e40d8724268a | 629 | if (brake_effort < 0.0) |
JonFreeman | 10:e40d8724268a | 630 | brake_effort = 0.5; |
JonFreeman | 8:93203f473f6e | 631 | mode_set_both_motors (REGENBRAKE, brake_effort); |
JonFreeman | 8:93203f473f6e | 632 | old_pot_position = pot_position; |
JonFreeman | 10:e40d8724268a | 633 | hand_controller_state = HAND_CONT_STATE_BRAKING; |
JonFreeman | 8:93203f473f6e | 634 | pc.printf ("Brake\r\n"); |
JonFreeman | 8:93203f473f6e | 635 | break; |
JonFreeman | 10:e40d8724268a | 636 | |
JonFreeman | 8:93203f473f6e | 637 | case HAND_CONT_STATE_BRAKING: |
JonFreeman | 8:93203f473f6e | 638 | if (pot_position > Pot_Brake_Range) // escape braking into drive |
JonFreeman | 10:e40d8724268a | 639 | hand_controller_state = HAND_CONT_STATE_INTO_DRIVING; |
JonFreeman | 8:93203f473f6e | 640 | else { |
JonFreeman | 8:93203f473f6e | 641 | if (worth_the_bother(pot_position, old_pot_position)) { |
JonFreeman | 8:93203f473f6e | 642 | old_pot_position = pot_position; |
JonFreeman | 8:93203f473f6e | 643 | brake_effort = (Pot_Brake_Range - pot_position) / Pot_Brake_Range; |
JonFreeman | 8:93203f473f6e | 644 | mode_set_both_motors (REGENBRAKE, brake_effort); |
JonFreeman | 8:93203f473f6e | 645 | // pc.printf ("Brake %.2f\r\n", brake_effort); |
JonFreeman | 8:93203f473f6e | 646 | } |
JonFreeman | 8:93203f473f6e | 647 | } |
JonFreeman | 8:93203f473f6e | 648 | break; |
JonFreeman | 10:e40d8724268a | 649 | |
JonFreeman | 10:e40d8724268a | 650 | case HAND_CONT_STATE_INTO_DRIVING: |
JonFreeman | 10:e40d8724268a | 651 | pc.printf ("Drive\r\n"); |
JonFreeman | 10:e40d8724268a | 652 | if (direction_new == 1) |
JonFreeman | 10:e40d8724268a | 653 | mode_set_both_motors (FORWARD, 0.0); // sets both motors |
JonFreeman | 10:e40d8724268a | 654 | else |
JonFreeman | 10:e40d8724268a | 655 | mode_set_both_motors (REVERSE, 0.0); |
JonFreeman | 10:e40d8724268a | 656 | hand_controller_state = HAND_CONT_STATE_DRIVING; |
JonFreeman | 10:e40d8724268a | 657 | break; |
JonFreeman | 10:e40d8724268a | 658 | |
JonFreeman | 8:93203f473f6e | 659 | case HAND_CONT_STATE_DRIVING: |
JonFreeman | 8:93203f473f6e | 660 | if (pot_position < Pot_Brake_Range) // escape braking into drive |
JonFreeman | 10:e40d8724268a | 661 | hand_controller_state = HAND_CONT_STATE_INTO_BRAKING; |
JonFreeman | 8:93203f473f6e | 662 | else { |
JonFreeman | 8:93203f473f6e | 663 | if (worth_the_bother(pot_position, old_pot_position)) { |
JonFreeman | 8:93203f473f6e | 664 | old_pot_position = pot_position; |
JonFreeman | 8:93203f473f6e | 665 | drive_effort = (pot_position - Pot_Brake_Range) / (1.0 - Pot_Brake_Range); |
JonFreeman | 8:93203f473f6e | 666 | setVI (drive_effort, drive_effort); // Wind volts and amps up and down together ??? |
JonFreeman | 8:93203f473f6e | 667 | pc.printf ("Drive %.2f\r\n", drive_effort); |
JonFreeman | 8:93203f473f6e | 668 | } |
JonFreeman | 8:93203f473f6e | 669 | } |
JonFreeman | 8:93203f473f6e | 670 | break; |
JonFreeman | 10:e40d8724268a | 671 | |
JonFreeman | 8:93203f473f6e | 672 | default: |
JonFreeman | 10:e40d8724268a | 673 | pc.printf ("Unhandled Hand Controller state %d\r\n", hand_controller_state); |
JonFreeman | 8:93203f473f6e | 674 | break; |
JonFreeman | 8:93203f473f6e | 675 | } // endof switch (hand_controller_state) { |
JonFreeman | 8:93203f473f6e | 676 | } |
JonFreeman | 8:93203f473f6e | 677 | |
JonFreeman | 0:435bf84ce48a | 678 | int main() |
JonFreeman | 0:435bf84ce48a | 679 | { |
JonFreeman | 4:21d91465e4b1 | 680 | int eighth_sec_count = 0; |
JonFreeman | 10:e40d8724268a | 681 | double servo_angle = 0.0; // For testing servo outs |
JonFreeman | 0:435bf84ce48a | 682 | |
JonFreeman | 4:21d91465e4b1 | 683 | MotA = 0; // Output all 0s to Motor drive ports A and B |
JonFreeman | 0:435bf84ce48a | 684 | MotB = 0; |
JonFreeman | 7:6deaeace9a3e | 685 | // MotPtr[0] = &MotorA; // Pointers to motor class objects |
JonFreeman | 7:6deaeace9a3e | 686 | // MotPtr[1] = &MotorB; |
JonFreeman | 8:93203f473f6e | 687 | |
JonFreeman | 6:f289a49c1eae | 688 | Temperature_pin.fall (&temp_sensor_isr); |
JonFreeman | 6:f289a49c1eae | 689 | Temperature_pin.mode (PullUp); |
JonFreeman | 10:e40d8724268a | 690 | #ifdef RC1IN |
JonFreeman | 10:e40d8724268a | 691 | RC_1_in.rise (& RC_chan_1rise) ; |
JonFreeman | 10:e40d8724268a | 692 | RC_1_in.fall (& RC_chan_1fall) ; |
JonFreeman | 10:e40d8724268a | 693 | RC_1_in.mode (PullDown); |
JonFreeman | 10:e40d8724268a | 694 | #endif |
JonFreeman | 10:e40d8724268a | 695 | #ifdef RC2IN |
JonFreeman | 10:e40d8724268a | 696 | RC_2_in.rise (& RC_chan_2rise) ; |
JonFreeman | 10:e40d8724268a | 697 | RC_2_in.fall (& RC_chan_2fall) ; |
JonFreeman | 10:e40d8724268a | 698 | RC_2_in.mode (PullDown); |
JonFreeman | 10:e40d8724268a | 699 | #endif |
JonFreeman | 10:e40d8724268a | 700 | // Servo1_i.mode (PullUp); // These never worked, December 2018 re-trying using PC_14 and 15 |
JonFreeman | 10:e40d8724268a | 701 | // Servo2_i.mode (PullUp); |
JonFreeman | 4:21d91465e4b1 | 702 | |
JonFreeman | 4:21d91465e4b1 | 703 | MAH1.rise (& MAH_isr); // Set up interrupt vectors |
JonFreeman | 3:ecb00e0e8d68 | 704 | MAH1.fall (& MAH_isr); |
JonFreeman | 3:ecb00e0e8d68 | 705 | MAH2.rise (& MAH_isr); |
JonFreeman | 3:ecb00e0e8d68 | 706 | MAH2.fall (& MAH_isr); |
JonFreeman | 3:ecb00e0e8d68 | 707 | MAH3.rise (& MAH_isr); |
JonFreeman | 3:ecb00e0e8d68 | 708 | MAH3.fall (& MAH_isr); |
JonFreeman | 3:ecb00e0e8d68 | 709 | |
JonFreeman | 3:ecb00e0e8d68 | 710 | MBH1.rise (& MBH_isr); |
JonFreeman | 3:ecb00e0e8d68 | 711 | MBH1.fall (& MBH_isr); |
JonFreeman | 3:ecb00e0e8d68 | 712 | MBH2.rise (& MBH_isr); |
JonFreeman | 3:ecb00e0e8d68 | 713 | MBH2.fall (& MBH_isr); |
JonFreeman | 3:ecb00e0e8d68 | 714 | MBH3.rise (& MBH_isr); |
JonFreeman | 3:ecb00e0e8d68 | 715 | MBH3.fall (& MBH_isr); |
JonFreeman | 4:21d91465e4b1 | 716 | |
JonFreeman | 0:435bf84ce48a | 717 | MAH1.mode (PullUp); |
JonFreeman | 0:435bf84ce48a | 718 | MAH2.mode (PullUp); |
JonFreeman | 0:435bf84ce48a | 719 | MAH3.mode (PullUp); |
JonFreeman | 0:435bf84ce48a | 720 | MBH1.mode (PullUp); |
JonFreeman | 0:435bf84ce48a | 721 | MBH2.mode (PullUp); |
JonFreeman | 0:435bf84ce48a | 722 | MBH3.mode (PullUp); |
JonFreeman | 4:21d91465e4b1 | 723 | |
JonFreeman | 0:435bf84ce48a | 724 | // Setup system timers to cause periodic interrupts to synchronise and automate volt and current readings, loop repeat rate etc |
JonFreeman | 0:435bf84ce48a | 725 | tick_vread.attach_us (&ISR_voltage_reader, VOLTAGE_READ_INTERVAL_US); // Start periodic interrupt generator |
JonFreeman | 0:435bf84ce48a | 726 | loop_timer.attach_us (&ISR_loop_timer, MAIN_LOOP_REPEAT_TIME_US); // Start periodic interrupt generator |
JonFreeman | 6:f289a49c1eae | 727 | temperature_find_ticker.attach_us (&ISR_temperature_find_ticker, 960); |
JonFreeman | 0:435bf84ce48a | 728 | // Done setting up system interrupt timers |
JonFreeman | 6:f289a49c1eae | 729 | temperature_timer.start (); |
JonFreeman | 0:435bf84ce48a | 730 | |
JonFreeman | 6:f289a49c1eae | 731 | // const int TXTBUFSIZ = 36; |
JonFreeman | 6:f289a49c1eae | 732 | // char buff[TXTBUFSIZ]; |
JonFreeman | 3:ecb00e0e8d68 | 733 | pc.baud (9600); |
JonFreeman | 4:21d91465e4b1 | 734 | com3.baud (1200); |
JonFreeman | 4:21d91465e4b1 | 735 | com2.baud (19200); |
JonFreeman | 7:6deaeace9a3e | 736 | setup_comms (); |
JonFreeman | 6:f289a49c1eae | 737 | |
JonFreeman | 7:6deaeace9a3e | 738 | IAm = '0'; |
JonFreeman | 5:ca86a7848d54 | 739 | if (check_24LC64() != 0xa0) { // searches for i2c devices, returns address of highest found |
JonFreeman | 0:435bf84ce48a | 740 | pc.printf ("Check for 24LC64 eeprom FAILED\r\n"); |
JonFreeman | 5:ca86a7848d54 | 741 | com2.printf ("Check for 24LC64 eeprom FAILED\r\n"); |
JonFreeman | 7:6deaeace9a3e | 742 | eeprom_flag = false; |
JonFreeman | 8:93203f473f6e | 743 | } else { // Found 24LC64 memory on I2C |
JonFreeman | 7:6deaeace9a3e | 744 | eeprom_flag = true; |
JonFreeman | 5:ca86a7848d54 | 745 | bool k; |
JonFreeman | 6:f289a49c1eae | 746 | k = rd_24LC64 (0, mode_bytes, 32); |
JonFreeman | 5:ca86a7848d54 | 747 | if (!k) |
JonFreeman | 5:ca86a7848d54 | 748 | com2.printf ("Error reading from eeprom\r\n"); |
JonFreeman | 5:ca86a7848d54 | 749 | |
JonFreeman | 8:93203f473f6e | 750 | // int err = 0; |
JonFreeman | 6:f289a49c1eae | 751 | for (int i = 0; i < numof_eeprom_options; i++) { |
JonFreeman | 6:f289a49c1eae | 752 | if ((mode_bytes[i] < option_list[i].min) || (mode_bytes[i] > option_list[i].max)) { |
JonFreeman | 5:ca86a7848d54 | 753 | com2.printf ("EEROM error with %s\r\n", option_list[i].t); |
JonFreeman | 8:93203f473f6e | 754 | pc.printf ("EEROM error with %s\r\n", option_list[i].t); |
JonFreeman | 8:93203f473f6e | 755 | if (i == ID) { // need to force id to '0' |
JonFreeman | 8:93203f473f6e | 756 | pc.printf ("Bad board ID value %d, forcing to \'0\'\r\n"); |
JonFreeman | 8:93203f473f6e | 757 | mode_bytes[ID] = '0'; |
JonFreeman | 8:93203f473f6e | 758 | } |
JonFreeman | 8:93203f473f6e | 759 | // err++; |
JonFreeman | 5:ca86a7848d54 | 760 | } |
JonFreeman | 5:ca86a7848d54 | 761 | // else |
JonFreeman | 5:ca86a7848d54 | 762 | // com2.printf ("%2x Good %s\r\n", buff[i], option_list[i].t); |
JonFreeman | 5:ca86a7848d54 | 763 | } |
JonFreeman | 7:6deaeace9a3e | 764 | rpm2mph = 0.0; |
JonFreeman | 8:93203f473f6e | 765 | if (mode_bytes[WHEELGEAR] == 0) // avoid making div0 error |
JonFreeman | 8:93203f473f6e | 766 | mode_bytes[WHEELGEAR]++; |
JonFreeman | 8:93203f473f6e | 767 | // if (err == 0) { |
JonFreeman | 8:93203f473f6e | 768 | mode_good_flag = true; |
JonFreeman | 8:93203f473f6e | 769 | MotorA.direction_set (mode_bytes[MOTADIR]); |
JonFreeman | 8:93203f473f6e | 770 | MotorB.direction_set (mode_bytes[MOTBDIR]); |
JonFreeman | 8:93203f473f6e | 771 | IAm = mode_bytes[ID]; |
JonFreeman | 8:93203f473f6e | 772 | rpm2mph = 60.0 // to Motor Revs per hour; |
JonFreeman | 8:93203f473f6e | 773 | * ((double)mode_bytes[MOTPIN] / (double)mode_bytes[WHEELGEAR]) // Wheel revs per hour |
JonFreeman | 8:93203f473f6e | 774 | * PI * ((double)mode_bytes[WHEELDIA] / 1000.0) // metres per hour |
JonFreeman | 8:93203f473f6e | 775 | * 39.37 / (1760.0 * 36.0); // miles per hour |
JonFreeman | 8:93203f473f6e | 776 | // } |
JonFreeman | 8:93203f473f6e | 777 | // Alternative ID 1 to 9 |
JonFreeman | 5:ca86a7848d54 | 778 | // com2.printf ("Alternative ID = 0x%2x\r\n", buff[6]); |
JonFreeman | 5:ca86a7848d54 | 779 | } |
JonFreeman | 6:f289a49c1eae | 780 | // T1 = 0; Now WRONGLY hoped to be InterruptIn counting pulses from LMT01 temperature sensor |
JonFreeman | 5:ca86a7848d54 | 781 | T2 = 0; // T2, T3, T4 As yet unused pins |
JonFreeman | 8:93203f473f6e | 782 | // T3 = 0; |
JonFreeman | 8:93203f473f6e | 783 | // T4 = 0; |
JonFreeman | 5:ca86a7848d54 | 784 | // T5 = 0; now input from fw/re on remote control box |
JonFreeman | 0:435bf84ce48a | 785 | T6 = 0; |
JonFreeman | 3:ecb00e0e8d68 | 786 | // MotPtr[0]->set_mode (REGENBRAKE); |
JonFreeman | 2:04761b196473 | 787 | MotorA.set_mode (REGENBRAKE); |
JonFreeman | 2:04761b196473 | 788 | MotorB.set_mode (REGENBRAKE); |
JonFreeman | 5:ca86a7848d54 | 789 | setVI (0.9, 0.5); |
JonFreeman | 5:ca86a7848d54 | 790 | |
JonFreeman | 8:93203f473f6e | 791 | // prscfuck (PWM_PRESECALER_DEFAULT); // Test fucking with fastpwm prescaler |
JonFreeman | 8:93203f473f6e | 792 | |
JonFreeman | 10:e40d8724268a | 793 | // Servos[0] = Servos[1] = NULL; |
JonFreeman | 4:21d91465e4b1 | 794 | // NOTE The ONLY way to get both servos working properly is to NOT use any if (bla) Servo ervo1(PB_8); |
JonFreeman | 4:21d91465e4b1 | 795 | // Only works with unconditional inline code |
JonFreeman | 4:21d91465e4b1 | 796 | // However, setup code for Input by default, |
JonFreeman | 4:21d91465e4b1 | 797 | // This can be used to monitor Servo output also ! |
JonFreeman | 10:e40d8724268a | 798 | |
JonFreeman | 10:e40d8724268a | 799 | // December 2018 ** NEED TO PROVE SERVO OUT WORKS ** YES, DONE. |
JonFreeman | 4:21d91465e4b1 | 800 | Servo Servo1 (PB_8) ; |
JonFreeman | 10:e40d8724268a | 801 | // Servos[0] = & Servo1; |
JonFreeman | 4:21d91465e4b1 | 802 | Servo Servo2 (PB_9) ; |
JonFreeman | 10:e40d8724268a | 803 | // Servos[1] = & Servo2; |
JonFreeman | 8:93203f473f6e | 804 | |
JonFreeman | 7:6deaeace9a3e | 805 | // pc.printf ("last_temp_count = %d\r\n", last_temp_count); // Has had time to do at least 1 conversion |
JonFreeman | 6:f289a49c1eae | 806 | if ((last_temp_count > 160) && (last_temp_count < 2400)) // in range -40 to +100 degree C |
JonFreeman | 6:f289a49c1eae | 807 | temp_sensor_exists = true; |
JonFreeman | 8:93203f473f6e | 808 | /* |
JonFreeman | 8:93203f473f6e | 809 | // Setup Complete ! Can now start main control forever loop. |
JonFreeman | 8:93203f473f6e | 810 | // March 16th 2018 thoughts !!! |
JonFreeman | 8:93203f473f6e | 811 | // Control from one of several sources and types as selected in options bytes in eeprom. |
JonFreeman | 8:93203f473f6e | 812 | // Control could be from e.g. Pot, Com2, Servos etc. |
JonFreeman | 8:93203f473f6e | 813 | // Suggest e.g. |
JonFreeman | 8:93203f473f6e | 814 | */ /* |
JonFreeman | 3:ecb00e0e8d68 | 815 | switch (mode_byte) { // executed once only upon startup |
JonFreeman | 3:ecb00e0e8d68 | 816 | case POT: |
JonFreeman | 3:ecb00e0e8d68 | 817 | while (1) { // forever loop |
JonFreeman | 3:ecb00e0e8d68 | 818 | call common_stuff (); |
JonFreeman | 3:ecb00e0e8d68 | 819 | ... |
JonFreeman | 3:ecb00e0e8d68 | 820 | } |
JonFreeman | 3:ecb00e0e8d68 | 821 | break; |
JonFreeman | 3:ecb00e0e8d68 | 822 | case COM2: |
JonFreeman | 3:ecb00e0e8d68 | 823 | while (1) { // forever loop |
JonFreeman | 3:ecb00e0e8d68 | 824 | call common_stuff (); |
JonFreeman | 3:ecb00e0e8d68 | 825 | ... |
JonFreeman | 3:ecb00e0e8d68 | 826 | } |
JonFreeman | 3:ecb00e0e8d68 | 827 | break; |
JonFreeman | 3:ecb00e0e8d68 | 828 | } |
JonFreeman | 3:ecb00e0e8d68 | 829 | */ |
JonFreeman | 7:6deaeace9a3e | 830 | // pc.printf ("Ready to go!, wheel gear in position %d\r\n", WHEELGEAR); |
JonFreeman | 8:93203f473f6e | 831 | dc_motor_kicker_timer.start (); |
JonFreeman | 8:93203f473f6e | 832 | int old_hand_controller_direction = T5; |
JonFreeman | 8:93203f473f6e | 833 | if (mode_bytes[COMM_SRC] == 3) { // Read fwd/rev switch 'T5', PA15 on 401 |
JonFreeman | 8:93203f473f6e | 834 | pc.printf ("Pot control\r\n"); |
JonFreeman | 8:93203f473f6e | 835 | if (T5) |
JonFreeman | 8:93203f473f6e | 836 | mode_set_both_motors (FORWARD, 0.0); // sets both motors |
JonFreeman | 8:93203f473f6e | 837 | else |
JonFreeman | 8:93203f473f6e | 838 | mode_set_both_motors (REVERSE, 0.0); |
JonFreeman | 8:93203f473f6e | 839 | } |
JonFreeman | 8:93203f473f6e | 840 | |
JonFreeman | 8:93203f473f6e | 841 | pc.printf ("About to start!, mode_bytes[COMM_SRC]= %d\r\n", mode_bytes[COMM_SRC]); |
JonFreeman | 10:e40d8724268a | 842 | // const double pwr = 1.5;SERVOIN_PWR_BENDER |
JonFreeman | 10:e40d8724268a | 843 | // for (double i = 0.0; i < 1.05; i+= 0.1) |
JonFreeman | 10:e40d8724268a | 844 | // pc.printf ("%f ^ %f = %f\r\n", i, SERVOIN_PWR_BENDER, pow(i, SERVOIN_PWR_BENDER)); |
JonFreeman | 10:e40d8724268a | 845 | |
JonFreeman | 10:e40d8724268a | 846 | // pc.printf ("PortA=%lx\r\n", PortA); |
JonFreeman | 8:93203f473f6e | 847 | |
JonFreeman | 0:435bf84ce48a | 848 | while (1) { // Loop forever, repeats synchroised by waiting for ticker Interrupt Service Routine to set 'loop_flag' true |
JonFreeman | 0:435bf84ce48a | 849 | while (!loop_flag) { // Most of the time is spent in this loop, repeatedly re-checking for commands from pc port |
JonFreeman | 7:6deaeace9a3e | 850 | command_line_interpreter_pc () ; // Proceed beyond here once loop_timer ticker ISR has set loop_flag true |
JonFreeman | 7:6deaeace9a3e | 851 | command_line_interpreter_loco () ; // Proceed beyond here once loop_timer ticker ISR has set loop_flag true |
JonFreeman | 0:435bf84ce48a | 852 | AtoD_reader (); // Performs A to D conversions at rate set by ticker interrupts |
JonFreeman | 0:435bf84ce48a | 853 | } |
JonFreeman | 10:e40d8724268a | 854 | loop_flag = false; // Clear flag set by ticker interrupt handler. WHEN LAST CHECKED this was about every 32ms |
JonFreeman | 10:e40d8724268a | 855 | |
JonFreeman | 10:e40d8724268a | 856 | RC_chan_1.validate_rx(); |
JonFreeman | 10:e40d8724268a | 857 | RC_chan_2.validate_rx(); |
JonFreeman | 10:e40d8724268a | 858 | |
JonFreeman | 8:93203f473f6e | 859 | switch (mode_bytes[COMM_SRC]) { // Look to selected source of driving command, act on commands from wherever |
JonFreeman | 8:93203f473f6e | 860 | case 0: // Invalid |
JonFreeman | 8:93203f473f6e | 861 | break; |
JonFreeman | 8:93203f473f6e | 862 | case 1: // COM1 - Primarily for testing, bypassed by command line interpreter |
JonFreeman | 8:93203f473f6e | 863 | break; |
JonFreeman | 8:93203f473f6e | 864 | case 2: // COM2 - Primarily for testing, bypassed by command line interpreter |
JonFreeman | 8:93203f473f6e | 865 | break; |
JonFreeman | 8:93203f473f6e | 866 | case 3: // Put all hand controller input stuff here |
JonFreeman | 10:e40d8724268a | 867 | hand_control_state_machine (3); |
JonFreeman | 8:93203f473f6e | 868 | break; // endof hand controller stuff |
JonFreeman | 8:93203f473f6e | 869 | case 4: // Servo1 |
JonFreeman | 10:e40d8724268a | 870 | hand_control_state_machine (4); |
JonFreeman | 8:93203f473f6e | 871 | break; |
JonFreeman | 8:93203f473f6e | 872 | case 5: // Servo2 |
JonFreeman | 8:93203f473f6e | 873 | break; |
JonFreeman | 8:93203f473f6e | 874 | default: |
JonFreeman | 8:93203f473f6e | 875 | pc.printf ("Unrecognised state %d\r\n", mode_bytes[COMM_SRC]); |
JonFreeman | 8:93203f473f6e | 876 | break; |
JonFreeman | 8:93203f473f6e | 877 | } // endof switch (mode_bytes[COMM_SRC]) { |
JonFreeman | 8:93203f473f6e | 878 | |
JonFreeman | 8:93203f473f6e | 879 | dc_motor_kicker_timer.reset (); |
JonFreeman | 5:ca86a7848d54 | 880 | MotorA.pulses_per_sec (); // Needed to keep table updated to give reading in Hall transitions per second |
JonFreeman | 5:ca86a7848d54 | 881 | MotorB.pulses_per_sec (); // Read MotorX.PPS to read pulses per sec or MotorX.RPM to read motor RPM |
JonFreeman | 8:93203f473f6e | 882 | // T4 = !T4; // toggle to hang scope on to verify loop execution |
JonFreeman | 0:435bf84ce48a | 883 | // do stuff |
JonFreeman | 8:93203f473f6e | 884 | if (MotorA.dc_motor) { |
JonFreeman | 8:93203f473f6e | 885 | MotorA.raw_V_pwm (1); |
JonFreeman | 8:93203f473f6e | 886 | MotorA.low_side_on (); |
JonFreeman | 8:93203f473f6e | 887 | } |
JonFreeman | 8:93203f473f6e | 888 | if (MotorB.dc_motor) { |
JonFreeman | 8:93203f473f6e | 889 | MotorB.raw_V_pwm (1); |
JonFreeman | 8:93203f473f6e | 890 | MotorB.low_side_on (); |
JonFreeman | 8:93203f473f6e | 891 | } |
JonFreeman | 8:93203f473f6e | 892 | if (MotorA.dc_motor || MotorB.dc_motor) { |
JonFreeman | 8:93203f473f6e | 893 | // motors_restore.attach_us (&restorer, ttime); |
JonFreeman | 8:93203f473f6e | 894 | motors_restore.attach_us (&restorer, 25); |
JonFreeman | 8:93203f473f6e | 895 | } |
JonFreeman | 8:93203f473f6e | 896 | |
JonFreeman | 0:435bf84ce48a | 897 | if (flag_8Hz) { // do slower stuff |
JonFreeman | 0:435bf84ce48a | 898 | flag_8Hz = false; |
JonFreeman | 3:ecb00e0e8d68 | 899 | LED = !LED; // Toggle LED on board, should be seen to fast flash |
JonFreeman | 8:93203f473f6e | 900 | if (WatchDogEnable) { |
JonFreeman | 8:93203f473f6e | 901 | WatchDog--; |
JonFreeman | 8:93203f473f6e | 902 | if (WatchDog == 0) { // Deal with WatchDog timer timeout here |
JonFreeman | 8:93203f473f6e | 903 | setVI (0.0, 0.0); // set motor volts and amps to zero |
JonFreeman | 8:93203f473f6e | 904 | com2.printf ("TIMEOUT %2x\r\n", (I_Am() & 0x0f)); // Potential problem of multiple units reporting at same time overcome by adding board number to WATCHDOG_RELOAD |
JonFreeman | 8:93203f473f6e | 905 | } // End of dealing with WatchDog timer timeout |
JonFreeman | 8:93203f473f6e | 906 | if (WatchDog < 0) |
JonFreeman | 8:93203f473f6e | 907 | WatchDog = 0; |
JonFreeman | 8:93203f473f6e | 908 | } |
JonFreeman | 4:21d91465e4b1 | 909 | eighth_sec_count++; |
JonFreeman | 4:21d91465e4b1 | 910 | if (eighth_sec_count > 6) { // Send some status info out of serial port every second and a bit or thereabouts |
JonFreeman | 4:21d91465e4b1 | 911 | eighth_sec_count = 0; |
JonFreeman | 6:f289a49c1eae | 912 | MotorA.current_calc (); // Updates readings in MotorA.I.min, MotorA.I.ave and MotorA.I.max |
JonFreeman | 6:f289a49c1eae | 913 | MotorB.current_calc (); |
JonFreeman | 8:93203f473f6e | 914 | /* if (temp_sensor_exists) { |
JonFreeman | 8:93203f473f6e | 915 | double tmprt = (double) last_temp_count; |
JonFreeman | 8:93203f473f6e | 916 | tmprt /= 16.0; |
JonFreeman | 8:93203f473f6e | 917 | tmprt -= 50.0; |
JonFreeman | 8:93203f473f6e | 918 | pc.printf ("Temp %.2f\r\n", tmprt); |
JonFreeman | 8:93203f473f6e | 919 | }*/ |
JonFreeman | 5:ca86a7848d54 | 920 | // com2.printf ("V=%+.2f, Pot=%+.2f, HA %d, HB %d, IAmin %d, IAave %d, IAmax %d, IB %d, Arpm %d, Brpm %d\r\n", Read_BatteryVolts(), Read_DriverPot(), MotorA.read_Halls (), MotorB.read_Halls (), MotorA.I.min, MotorA.I.ave, MotorA.I.max, MotorB.I.ave, (Apps * 60) / 24, (Bpps * 60) / 24); |
JonFreeman | 0:435bf84ce48a | 921 | } |
JonFreeman | 10:e40d8724268a | 922 | |
JonFreeman | 10:e40d8724268a | 923 | // servo out test here December 2018 |
JonFreeman | 10:e40d8724268a | 924 | servo_angle += 0.01; |
JonFreeman | 10:e40d8724268a | 925 | if (servo_angle > TWOPI) |
JonFreeman | 10:e40d8724268a | 926 | servo_angle -= TWOPI; |
JonFreeman | 10:e40d8724268a | 927 | Servo1 = ((sin(servo_angle)+1.0) / 2.0); |
JonFreeman | 10:e40d8724268a | 928 | Servo2 = ((cos(servo_angle)+1.0) / 2.0); |
JonFreeman | 10:e40d8724268a | 929 | // Yep! Both servo outs work lovely December 2018 |
JonFreeman | 10:e40d8724268a | 930 | |
JonFreeman | 0:435bf84ce48a | 931 | } // End of if(flag_8Hz) |
JonFreeman | 0:435bf84ce48a | 932 | } // End of main programme loop |
JonFreeman | 0:435bf84ce48a | 933 | } |