Dual Brushless Motor ESC, 10-62V, up to 50A per motor. Motors ganged or independent, multiple control input methods, cycle-by-cycle current limit, speed mode and torque mode control. Motors tiny to kW. Speed limit and other parameters easily set in firmware. As used in 'The Brushless Brutalist' locomotive - www.jons-workshop.com. See also Model Engineer magazine June-October 2019.

Dependencies:   mbed BufferedSerial Servo PCT2075 FastPWM

Update 17th August 2020 Radio control inputs completed

Committer:
JonFreeman
Date:
Sun Jun 17 06:59:37 2018 +0000
Revision:
7:6deaeace9a3e
Parent:
6:f289a49c1eae
Child:
8:93203f473f6e
Firmware for STM3 Twin Brushless Motor Electronic Speed Controller; Snapshot at 17th June 2018

Who changed what in which revision?

UserRevisionLine numberNew contents of line
JonFreeman 0:435bf84ce48a 1 #include "mbed.h"
JonFreeman 0:435bf84ce48a 2 #include "DualBLS.h"
JonFreeman 0:435bf84ce48a 3 #include "BufferedSerial.h"
JonFreeman 0:435bf84ce48a 4 #include "FastPWM.h"
JonFreeman 4:21d91465e4b1 5 #include "Servo.h"
JonFreeman 5:ca86a7848d54 6
JonFreeman 5:ca86a7848d54 7 /*
JonFreeman 5:ca86a7848d54 8 New 29th May 2018 - YET TO CODE FOR - Fwd/Rev line from possible remote hand control box has signal routed to T5
JonFreeman 7:6deaeace9a3e 9 Also new 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 10 */
JonFreeman 5:ca86a7848d54 11
JonFreeman 5:ca86a7848d54 12
JonFreeman 0:435bf84ce48a 13 /* STM32F401RE - compile using NUCLEO-F401RE
JonFreeman 6:f289a49c1eae 14 // PROJECT - Dual Brushless Motor Controller - Jon Freeman June 2018.
JonFreeman 0:435bf84ce48a 15
JonFreeman 0:435bf84ce48a 16 AnalogIn to read each motor current
JonFreeman 0:435bf84ce48a 17
JonFreeman 0:435bf84ce48a 18 ____________________________________________________________________________________
JonFreeman 0:435bf84ce48a 19 CONTROL PHILOSOPHY
JonFreeman 0:435bf84ce48a 20 This Bogie drive board software should ensure sensible control when commands supplied are not sensible !
JonFreeman 0:435bf84ce48a 21
JonFreeman 0:435bf84ce48a 22 That is, smooth transition between Drive, Coast and Brake to be implemented here.
JonFreeman 0:435bf84ce48a 23 The remote controller must not be relied upon to deliver sensible command sequences.
JonFreeman 0:435bf84ce48a 24
JonFreeman 0:435bf84ce48a 25 So much the better if the remote controller does issue sensible commands, but ...
JonFreeman 0:435bf84ce48a 26
JonFreeman 0:435bf84ce48a 27 ____________________________________________________________________________________
JonFreeman 0:435bf84ce48a 28
JonFreeman 0:435bf84ce48a 29
JonFreeman 0:435bf84ce48a 30 */
JonFreeman 7:6deaeace9a3e 31 #if defined (TARGET_NUCLEO_F401RE) // CPU in 64 pin LQFP
JonFreeman 7:6deaeace9a3e 32 #include "F401RE.h"
JonFreeman 6:f289a49c1eae 33 #endif
JonFreeman 7:6deaeace9a3e 34 #if defined (TARGET_NUCLEO_F446ZE) // CPU in 144 pin LQFP
JonFreeman 7:6deaeace9a3e 35 #include "F446ZE.h"
JonFreeman 6:f289a49c1eae 36 #endif
JonFreeman 0:435bf84ce48a 37 /* Global variable declarations */
JonFreeman 3:ecb00e0e8d68 38 volatile uint32_t fast_sys_timer = 0; // gets incremented by our Ticker ISR every VOLTAGE_READ_INTERVAL_US
JonFreeman 5:ca86a7848d54 39 int WatchDog = WATCHDOG_RELOAD + 80; // Allow extra few seconds at powerup
JonFreeman 0:435bf84ce48a 40 uint32_t volt_reading = 0, // Global updated by interrupt driven read of Battery Volts
JonFreeman 0:435bf84ce48a 41 driverpot_reading = 0, // Global updated by interrupt driven read of Drivers Pot
JonFreeman 0:435bf84ce48a 42 sys_timer = 0, // gets incremented by our Ticker ISR every MAIN_LOOP_REPEAT_TIME_US
JonFreeman 5:ca86a7848d54 43 AtoD_Semaphore = 0;
JonFreeman 5:ca86a7848d54 44 int IAm;
JonFreeman 0:435bf84ce48a 45 bool loop_flag = false; // made true in ISR_loop_timer, picked up and made false again in main programme loop
JonFreeman 0:435bf84ce48a 46 bool flag_8Hz = false; // As loop_flag but repeats 8 times per sec
JonFreeman 6:f289a49c1eae 47 bool temp_sensor_exists = false;
JonFreeman 7:6deaeace9a3e 48 bool eeprom_flag; // gets set according to 24LC674 being found or not
JonFreeman 7:6deaeace9a3e 49 bool mode_good_flag = false;
JonFreeman 6:f289a49c1eae 50 char mode_bytes[36];
JonFreeman 3:ecb00e0e8d68 51
JonFreeman 6:f289a49c1eae 52 uint32_t temp_sensor_count = 0, // incremented by every rising edge from LMT01
JonFreeman 6:f289a49c1eae 53 last_temp_count = 0; // global updated approx every 100ms after each LMT01 conversion completes
JonFreeman 6:f289a49c1eae 54 // struct single_bogie_options bogie;
JonFreeman 7:6deaeace9a3e 55 double rpm2mph = 0.0; // gets calculated from eeprom mode entries if present
JonFreeman 0:435bf84ce48a 56 /* End of Global variable declarations */
JonFreeman 0:435bf84ce48a 57
JonFreeman 0:435bf84ce48a 58 Ticker tick_vread; // Device to cause periodic interrupts, used to time voltage readings etc
JonFreeman 0:435bf84ce48a 59 Ticker loop_timer; // Device to cause periodic interrupts, used to sync iterations of main programme loop
JonFreeman 6:f289a49c1eae 60 Ticker temperature_find_ticker;
JonFreeman 6:f289a49c1eae 61 Timer temperature_timer;
JonFreeman 0:435bf84ce48a 62
JonFreeman 0:435bf84ce48a 63 // Interrupt Service Routines
JonFreeman 6:f289a49c1eae 64 void ISR_temperature_find_ticker () { // every 960 us, i.e. slightly faster than once per milli sec
JonFreeman 6:f289a49c1eae 65 static bool readflag = false;
JonFreeman 6:f289a49c1eae 66 int t = temperature_timer.read_ms ();
JonFreeman 6:f289a49c1eae 67 if ((t == 5) && (!readflag)) {
JonFreeman 6:f289a49c1eae 68 last_temp_count = temp_sensor_count;
JonFreeman 6:f289a49c1eae 69 temp_sensor_count = 0;
JonFreeman 6:f289a49c1eae 70 readflag = true;
JonFreeman 6:f289a49c1eae 71 }
JonFreeman 6:f289a49c1eae 72 if (t == 6)
JonFreeman 6:f289a49c1eae 73 readflag = false;
JonFreeman 6:f289a49c1eae 74 }
JonFreeman 0:435bf84ce48a 75
JonFreeman 0:435bf84ce48a 76 /** void ISR_loop_timer ()
JonFreeman 0:435bf84ce48a 77 * This ISR responds to Ticker interrupts at a rate of (probably) 32 times per second (check from constant declarations above)
JonFreeman 0:435bf84ce48a 78 * This ISR sets global flag 'loop_flag' used to synchronise passes around main programme control loop.
JonFreeman 0:435bf84ce48a 79 * Increments global 'sys_timer', usable anywhere as general measure of elapsed time
JonFreeman 0:435bf84ce48a 80 */
JonFreeman 0:435bf84ce48a 81 void ISR_loop_timer () // This is Ticker Interrupt Service Routine - loop timer - MAIN_LOOP_REPEAT_TIME_US
JonFreeman 0:435bf84ce48a 82 {
JonFreeman 0:435bf84ce48a 83 loop_flag = true; // set flag to allow main programme loop to proceed
JonFreeman 0:435bf84ce48a 84 sys_timer++; // Just a handy measure of elapsed time for anything to use
JonFreeman 0:435bf84ce48a 85 if ((sys_timer & 0x03) == 0)
JonFreeman 0:435bf84ce48a 86 flag_8Hz = true;
JonFreeman 0:435bf84ce48a 87 }
JonFreeman 0:435bf84ce48a 88
JonFreeman 0:435bf84ce48a 89 /** void ISR_voltage_reader ()
JonFreeman 0:435bf84ce48a 90 * This ISR responds to Ticker interrupts every 'VOLTAGE_READ_INTERVAL_US' micro seconds
JonFreeman 0:435bf84ce48a 91 *
JonFreeman 0:435bf84ce48a 92 * AtoD_reader() called from convenient point in code to take readings outside of ISRs
JonFreeman 0:435bf84ce48a 93 */
JonFreeman 0:435bf84ce48a 94
JonFreeman 5:ca86a7848d54 95 void ISR_voltage_reader () // This is Ticker Interrupt Service Routine ; few us between readings ; VOLTAGE_READ_INTERVAL_US = 50
JonFreeman 0:435bf84ce48a 96 {
JonFreeman 0:435bf84ce48a 97 AtoD_Semaphore++;
JonFreeman 2:04761b196473 98 fast_sys_timer++; // Just a handy measure of elapsed time for anything to use
JonFreeman 0:435bf84ce48a 99 }
JonFreeman 0:435bf84ce48a 100
JonFreeman 0:435bf84ce48a 101
JonFreeman 4:21d91465e4b1 102 class RControl_In
JonFreeman 4:21d91465e4b1 103 { // Read servo style pwm input
JonFreeman 0:435bf84ce48a 104 Timer t;
JonFreeman 0:435bf84ce48a 105 int32_t clock_old, clock_new;
JonFreeman 0:435bf84ce48a 106 int32_t pulse_width_us, period_us;
JonFreeman 4:21d91465e4b1 107 public:
JonFreeman 4:21d91465e4b1 108 RControl_In () {
JonFreeman 0:435bf84ce48a 109 pulse_width_us = period_us = 0;
JonFreeman 4:21d91465e4b1 110 com2.printf ("Setting up Radio_Congtrol_In\r\n");
JonFreeman 4:21d91465e4b1 111 } ;
JonFreeman 0:435bf84ce48a 112 bool validate_rx () ;
JonFreeman 0:435bf84ce48a 113 void rise () ;
JonFreeman 0:435bf84ce48a 114 void fall () ;
JonFreeman 4:21d91465e4b1 115 uint32_t pulsewidth () ;
JonFreeman 4:21d91465e4b1 116 uint32_t period () ;
JonFreeman 4:21d91465e4b1 117 bool rx_active;
JonFreeman 4:21d91465e4b1 118 } ;
JonFreeman 0:435bf84ce48a 119
JonFreeman 4:21d91465e4b1 120 uint32_t RControl_In::pulsewidth () {
JonFreeman 4:21d91465e4b1 121 return pulse_width_us;
JonFreeman 4:21d91465e4b1 122 }
JonFreeman 4:21d91465e4b1 123
JonFreeman 4:21d91465e4b1 124 uint32_t RControl_In::period () {
JonFreeman 4:21d91465e4b1 125 return period_us;
JonFreeman 4:21d91465e4b1 126 }
JonFreeman 4:21d91465e4b1 127
JonFreeman 4:21d91465e4b1 128 bool RControl_In::validate_rx ()
JonFreeman 0:435bf84ce48a 129 {
JonFreeman 0:435bf84ce48a 130 if ((clock() - clock_new) > 4)
JonFreeman 0:435bf84ce48a 131 rx_active = false;
JonFreeman 0:435bf84ce48a 132 else
JonFreeman 0:435bf84ce48a 133 rx_active = true;
JonFreeman 0:435bf84ce48a 134 return rx_active;
JonFreeman 0:435bf84ce48a 135 }
JonFreeman 0:435bf84ce48a 136
JonFreeman 6:f289a49c1eae 137 void RControl_In::rise () // These may not work as use of PortB as port may bugger InterruptIn use
JonFreeman 0:435bf84ce48a 138 {
JonFreeman 0:435bf84ce48a 139 t.stop ();
JonFreeman 0:435bf84ce48a 140 period_us = t.read_us ();
JonFreeman 0:435bf84ce48a 141 t.reset ();
JonFreeman 0:435bf84ce48a 142 t.start ();
JonFreeman 0:435bf84ce48a 143 }
JonFreeman 4:21d91465e4b1 144 void RControl_In::fall ()
JonFreeman 0:435bf84ce48a 145 {
JonFreeman 0:435bf84ce48a 146 pulse_width_us = t.read_us ();
JonFreeman 0:435bf84ce48a 147 clock_old = clock_new;
JonFreeman 0:435bf84ce48a 148 clock_new = clock();
JonFreeman 0:435bf84ce48a 149 if ((clock_new - clock_old) < 4)
JonFreeman 0:435bf84ce48a 150 rx_active = true;
JonFreeman 0:435bf84ce48a 151 }
JonFreeman 4:21d91465e4b1 152
JonFreeman 0:435bf84ce48a 153
JonFreeman 4:21d91465e4b1 154 Servo * Servos[2];
JonFreeman 4:21d91465e4b1 155
JonFreeman 0:435bf84ce48a 156 //uint32_t HAtot = 0, HBtot = 0, A_Offset = 0, B_Offset = 0;
JonFreeman 0:435bf84ce48a 157 /*
JonFreeman 0:435bf84ce48a 158 5 1 3 2 6 4 SENSOR SEQUENCE
JonFreeman 0:435bf84ce48a 159
JonFreeman 3:ecb00e0e8d68 160 1 1 1 1 0 0 0 ---___---___ Hall1
JonFreeman 3:ecb00e0e8d68 161 2 0 0 1 1 1 0 __---___---_ Hall2
JonFreeman 3:ecb00e0e8d68 162 4 1 0 0 0 1 1 -___---___-- Hall3
JonFreeman 0:435bf84ce48a 163
JonFreeman 0:435bf84ce48a 164 UV WV WU VU VW UW OUTPUT SEQUENCE
JonFreeman 0:435bf84ce48a 165 */
JonFreeman 3:ecb00e0e8d68 166 const uint16_t A_tabl[] = { // Origial table
JonFreeman 0:435bf84ce48a 167 0, 0, 0, 0, 0, 0, 0, 0, // Handbrake
JonFreeman 0:435bf84ce48a 168 0, AWV,AVU,AWU,AUW,AUV,AVW, 0, // Forward 0, WV1, VU1, WU1, UW1, UV1, VW1, 0, // JP, FR, SG, PWM = 1 0 1 1 Forward1
JonFreeman 0:435bf84ce48a 169 0, AVW,AUV,AUW,AWU,AVU,AWV, 0, // Reverse 0, VW1, UV1, UW1, WU1, VU1, WV1, 0, // JP, FR, SG, PWM = 1 1 0 1 Reverse1
JonFreeman 3:ecb00e0e8d68 170 0, BRA,BRA,BRA,BRA,BRA,BRA,0, // Regenerative Braking
JonFreeman 4:21d91465e4b1 171 KEEP_L_MASK_A, KEEP_H_MASK_A // [32 and 33]
JonFreeman 3:ecb00e0e8d68 172 } ;
JonFreeman 4:21d91465e4b1 173 InterruptIn * AHarr[] = {
JonFreeman 4:21d91465e4b1 174 &MAH1,
JonFreeman 4:21d91465e4b1 175 &MAH2,
JonFreeman 4:21d91465e4b1 176 &MAH3
JonFreeman 3:ecb00e0e8d68 177 } ;
JonFreeman 0:435bf84ce48a 178 const uint16_t B_tabl[] = {
JonFreeman 0:435bf84ce48a 179 0, 0, 0, 0, 0, 0, 0, 0, // Handbrake
JonFreeman 0:435bf84ce48a 180 0, BWV,BVU,BWU,BUW,BUV,BVW, 0, // Forward 0, WV1, VU1, WU1, UW1, UV1, VW1, 0, // JP, FR, SG, PWM = 1 0 1 1 Forward1
JonFreeman 0:435bf84ce48a 181 0, BVW,BUV,BUW,BWU,BVU,BWV, 0, // Reverse 0, VW1, UV1, UW1, WU1, VU1, WV1, 0, // JP, FR, SG, PWM = 1 1 0 1 Reverse1
JonFreeman 3:ecb00e0e8d68 182 0, BRB,BRB,BRB,BRB,BRB,BRB,0, // Regenerative Braking
JonFreeman 4:21d91465e4b1 183 KEEP_L_MASK_B, KEEP_H_MASK_B
JonFreeman 3:ecb00e0e8d68 184 } ;
JonFreeman 4:21d91465e4b1 185 InterruptIn * BHarr[] = {
JonFreeman 4:21d91465e4b1 186 &MBH1,
JonFreeman 4:21d91465e4b1 187 &MBH2,
JonFreeman 4:21d91465e4b1 188 &MBH3
JonFreeman 0:435bf84ce48a 189 } ;
JonFreeman 0:435bf84ce48a 190
JonFreeman 0:435bf84ce48a 191 class motor
JonFreeman 0:435bf84ce48a 192 {
JonFreeman 5:ca86a7848d54 193 uint32_t Hall_total, visible_mode, inner_mode, edge_count_table[MAIN_LOOP_ITERATION_Hz]; // to contain one seconds worth
JonFreeman 3:ecb00e0e8d68 194 uint32_t latest_pulses_per_sec, Hall_tab_ptr, direction, ppstmp;
JonFreeman 3:ecb00e0e8d68 195 bool moving_flag;
JonFreeman 0:435bf84ce48a 196 const uint16_t * lut;
JonFreeman 0:435bf84ce48a 197 FastPWM * maxV, * maxI;
JonFreeman 0:435bf84ce48a 198 PortOut * Motor_Port;
JonFreeman 2:04761b196473 199 InterruptIn * Hall1, * Hall2, * Hall3;
JonFreeman 0:435bf84ce48a 200 public:
JonFreeman 2:04761b196473 201 struct currents {
JonFreeman 2:04761b196473 202 uint32_t max, min, ave;
JonFreeman 2:04761b196473 203 } I;
JonFreeman 3:ecb00e0e8d68 204 int32_t angle_cnt;
JonFreeman 0:435bf84ce48a 205 uint32_t current_samples[CURRENT_SAMPLES_AVERAGED]; // Circular buffer where latest current readings get stored
JonFreeman 3:ecb00e0e8d68 206 uint32_t Hindex[2], tickleon, encoder_error_cnt;
JonFreeman 5:ca86a7848d54 207 uint32_t RPM, PPS;
JonFreeman 5:ca86a7848d54 208 double last_V, last_I;
JonFreeman 0:435bf84ce48a 209 motor () {} ; // Default constructor
JonFreeman 4:21d91465e4b1 210 motor (PortOut * , FastPWM * , FastPWM * , const uint16_t *, InterruptIn **) ;
JonFreeman 0:435bf84ce48a 211 void set_V_limit (double) ; // Sets max motor voltage
JonFreeman 0:435bf84ce48a 212 void set_I_limit (double) ; // Sets max motor current
JonFreeman 0:435bf84ce48a 213 void Hall_change () ; // Called in response to edge on Hall input pin
JonFreeman 3:ecb00e0e8d68 214 void motor_set () ; // Energise Port with data determined by Hall sensors
JonFreeman 3:ecb00e0e8d68 215 void direction_set (int) ; // sets 'direction' with bit pattern to eor with FORWARD or REVERSE in set_mode
JonFreeman 3:ecb00e0e8d68 216 bool set_mode (int); // sets mode to HANDBRAKE, FORWARD, REVERSE or REGENBRAKE
JonFreeman 3:ecb00e0e8d68 217 bool is_moving () ; // Returns true if one or more Hall transitions within last 31.25 milli secs
JonFreeman 3:ecb00e0e8d68 218 void current_calc () ; // Updates 3 uint32_t I.min, I.ave, I.max
JonFreeman 3:ecb00e0e8d68 219 uint32_t pulses_per_sec () ; // call this once per main loop pass to keep count = edges per sec
JonFreeman 3:ecb00e0e8d68 220 int read_Halls () ; // Returns 3 bits of latest Hall sensor outputs
JonFreeman 3:ecb00e0e8d68 221 void high_side_off () ;
JonFreeman 3:ecb00e0e8d68 222 } ; //MotorA, MotorB, or even Motor[2];
JonFreeman 0:435bf84ce48a 223
JonFreeman 4:21d91465e4b1 224 motor MotorA (&MotA, &A_MAX_V_PWM, &A_MAX_I_PWM, A_tabl, AHarr);
JonFreeman 4:21d91465e4b1 225 motor MotorB (&MotB, &B_MAX_V_PWM, &B_MAX_I_PWM, B_tabl, BHarr);
JonFreeman 0:435bf84ce48a 226
JonFreeman 7:6deaeace9a3e 227 //motor * MotPtr[8]; // Array of pointers to some number of motor objects
JonFreeman 3:ecb00e0e8d68 228
JonFreeman 4:21d91465e4b1 229 motor::motor (PortOut * P , FastPWM * _maxV_ , FastPWM * _maxI_ , const uint16_t * lutptr, InterruptIn ** Hall) // Constructor
JonFreeman 3:ecb00e0e8d68 230 { // Constructor
JonFreeman 0:435bf84ce48a 231 maxV = _maxV_;
JonFreeman 0:435bf84ce48a 232 maxI = _maxI_;
JonFreeman 3:ecb00e0e8d68 233 Hall_total = 0; // mode can be only 0, 8, 16 or 24, lut row select for Handbrake, Forward, Reverse, or Regen Braking
JonFreeman 0:435bf84ce48a 234 latest_pulses_per_sec = 0;
JonFreeman 0:435bf84ce48a 235 for (int i = 0; i < MAIN_LOOP_ITERATION_Hz; i++)
JonFreeman 0:435bf84ce48a 236 edge_count_table[i] = 0;
JonFreeman 0:435bf84ce48a 237 if (lutptr != A_tabl && lutptr != B_tabl)
JonFreeman 0:435bf84ce48a 238 pc.printf ("Fatal in 'motor' constructor, Invalid lut address\r\n");
JonFreeman 3:ecb00e0e8d68 239 Hall_tab_ptr = 0;
JonFreeman 0:435bf84ce48a 240 Motor_Port = P;
JonFreeman 0:435bf84ce48a 241 pc.printf ("In motor constructor, Motor port = %lx\r\n", P);
JonFreeman 0:435bf84ce48a 242 maxV->period_ticks (MAX_PWM_TICKS + 1); // around 18 kHz
JonFreeman 0:435bf84ce48a 243 maxI->period_ticks (MAX_PWM_TICKS + 1);
JonFreeman 0:435bf84ce48a 244 maxV->pulsewidth_ticks (MAX_PWM_TICKS / 20);
JonFreeman 0:435bf84ce48a 245 maxI->pulsewidth_ticks (MAX_PWM_TICKS / 30);
JonFreeman 5:ca86a7848d54 246 visible_mode = REGENBRAKE;
JonFreeman 5:ca86a7848d54 247 inner_mode = REGENBRAKE;
JonFreeman 0:435bf84ce48a 248 lut = lutptr;
JonFreeman 3:ecb00e0e8d68 249 Hindex[0] = Hindex[1] = read_Halls ();
JonFreeman 3:ecb00e0e8d68 250 ppstmp = 0;
JonFreeman 3:ecb00e0e8d68 251 tickleon = 0;
JonFreeman 3:ecb00e0e8d68 252 direction = 0;
JonFreeman 3:ecb00e0e8d68 253 angle_cnt = 0; // Incremented or decremented on each Hall event according to actual measured direction of travel
JonFreeman 3:ecb00e0e8d68 254 encoder_error_cnt = 0; // Incremented when Hall transition not recognised as either direction
JonFreeman 4:21d91465e4b1 255 Hall1 = Hall[0];
JonFreeman 4:21d91465e4b1 256 Hall2 = Hall[1];
JonFreeman 4:21d91465e4b1 257 Hall3 = Hall[2];
JonFreeman 5:ca86a7848d54 258 PPS = 0;
JonFreeman 5:ca86a7848d54 259 RPM = 0;
JonFreeman 5:ca86a7848d54 260 last_V = last_I = 0.0;
JonFreeman 3:ecb00e0e8d68 261 }
JonFreeman 3:ecb00e0e8d68 262
JonFreeman 5:ca86a7848d54 263 /**
JonFreeman 5:ca86a7848d54 264 void motor::direction_set (int dir) {
JonFreeman 5:ca86a7848d54 265 Used to set direction according to mode data from eeprom
JonFreeman 5:ca86a7848d54 266 */
JonFreeman 3:ecb00e0e8d68 267 void motor::direction_set (int dir) {
JonFreeman 3:ecb00e0e8d68 268 if (dir != 0)
JonFreeman 3:ecb00e0e8d68 269 dir = FORWARD | REVERSE; // bits used in eor
JonFreeman 3:ecb00e0e8d68 270 direction = dir;
JonFreeman 0:435bf84ce48a 271 }
JonFreeman 0:435bf84ce48a 272
JonFreeman 1:0fabe6fdb55b 273 int motor::read_Halls () {
JonFreeman 2:04761b196473 274 int x = 0;
JonFreeman 2:04761b196473 275 if (*Hall1 != 0) x |= 1;
JonFreeman 2:04761b196473 276 if (*Hall2 != 0) x |= 2;
JonFreeman 2:04761b196473 277 if (*Hall3 != 0) x |= 4;
JonFreeman 2:04761b196473 278 return x;
JonFreeman 2:04761b196473 279 }
JonFreeman 2:04761b196473 280
JonFreeman 3:ecb00e0e8d68 281 void motor::high_side_off () {
JonFreeman 3:ecb00e0e8d68 282 uint16_t p = *Motor_Port;
JonFreeman 3:ecb00e0e8d68 283 p &= lut[32]; // KEEP_L_MASK_A or B
JonFreeman 3:ecb00e0e8d68 284 *Motor_Port = p;
JonFreeman 1:0fabe6fdb55b 285 }
JonFreeman 1:0fabe6fdb55b 286
JonFreeman 0:435bf84ce48a 287 void motor::current_calc ()
JonFreeman 0:435bf84ce48a 288 {
JonFreeman 0:435bf84ce48a 289 I.min = 0x0fffffff; // samples are 16 bit
JonFreeman 0:435bf84ce48a 290 I.max = 0;
JonFreeman 0:435bf84ce48a 291 I.ave = 0;
JonFreeman 0:435bf84ce48a 292 uint16_t sample;
JonFreeman 0:435bf84ce48a 293 for (int i = 0; i < CURRENT_SAMPLES_AVERAGED; i++) {
JonFreeman 0:435bf84ce48a 294 sample = current_samples[i];
JonFreeman 0:435bf84ce48a 295 I.ave += sample;
JonFreeman 0:435bf84ce48a 296 if (I.min > sample)
JonFreeman 0:435bf84ce48a 297 I.min = sample;
JonFreeman 0:435bf84ce48a 298 if (I.max < sample)
JonFreeman 0:435bf84ce48a 299 I.max = sample;
JonFreeman 0:435bf84ce48a 300 }
JonFreeman 0:435bf84ce48a 301 I.ave /= CURRENT_SAMPLES_AVERAGED;
JonFreeman 0:435bf84ce48a 302 }
JonFreeman 0:435bf84ce48a 303
JonFreeman 0:435bf84ce48a 304 void motor::set_V_limit (double p) // Sets max motor voltage
JonFreeman 0:435bf84ce48a 305 {
JonFreeman 0:435bf84ce48a 306 if (p < 0.0)
JonFreeman 0:435bf84ce48a 307 p = 0.0;
JonFreeman 0:435bf84ce48a 308 if (p > 1.0)
JonFreeman 0:435bf84ce48a 309 p = 1.0;
JonFreeman 5:ca86a7848d54 310 last_V = p; // for read by diagnostics
JonFreeman 0:435bf84ce48a 311 p *= 0.95; // need limit, ffi see MCP1630 data
JonFreeman 0:435bf84ce48a 312 p = 1.0 - p; // because pwm is wrong way up
JonFreeman 0:435bf84ce48a 313 maxV->pulsewidth_ticks ((int)(p * MAX_PWM_TICKS)); // PWM output to MCP1630 inverted motor pwm as MCP1630 inverts
JonFreeman 0:435bf84ce48a 314 }
JonFreeman 0:435bf84ce48a 315
JonFreeman 0:435bf84ce48a 316 void motor::set_I_limit (double p) // Sets max motor current. pwm integrated to dc ref voltage level
JonFreeman 0:435bf84ce48a 317 {
JonFreeman 0:435bf84ce48a 318 int a;
JonFreeman 0:435bf84ce48a 319 if (p < 0.0)
JonFreeman 0:435bf84ce48a 320 p = 0.0;
JonFreeman 0:435bf84ce48a 321 if (p > 1.0)
JonFreeman 0:435bf84ce48a 322 p = 1.0;
JonFreeman 5:ca86a7848d54 323 last_I = p;
JonFreeman 0:435bf84ce48a 324 a = (int)(p * MAX_PWM_TICKS);
JonFreeman 0:435bf84ce48a 325 if (a > MAX_PWM_TICKS)
JonFreeman 0:435bf84ce48a 326 a = MAX_PWM_TICKS;
JonFreeman 0:435bf84ce48a 327 if (a < 0)
JonFreeman 0:435bf84ce48a 328 a = 0;
JonFreeman 0:435bf84ce48a 329 maxI->pulsewidth_ticks (a); // PWM
JonFreeman 0:435bf84ce48a 330 }
JonFreeman 0:435bf84ce48a 331
JonFreeman 3:ecb00e0e8d68 332 uint32_t motor::pulses_per_sec () // call this once per 'MAIN_LOOP_REPEAT_TIME_US= 31250' main loop pass to keep count = edges per sec
JonFreeman 3:ecb00e0e8d68 333 { // Can also test for motor running or not here
JonFreeman 3:ecb00e0e8d68 334 if (ppstmp == Hall_total) {
JonFreeman 3:ecb00e0e8d68 335 moving_flag = false; // Zero Hall transitions since previous call - motor not moving
JonFreeman 4:21d91465e4b1 336 tickleon = TICKLE_TIMES;
JonFreeman 3:ecb00e0e8d68 337 }
JonFreeman 3:ecb00e0e8d68 338 else {
JonFreeman 3:ecb00e0e8d68 339 moving_flag = true;
JonFreeman 3:ecb00e0e8d68 340 ppstmp = Hall_total;
JonFreeman 3:ecb00e0e8d68 341 }
JonFreeman 3:ecb00e0e8d68 342 latest_pulses_per_sec = ppstmp - edge_count_table[Hall_tab_ptr];
JonFreeman 3:ecb00e0e8d68 343 edge_count_table[Hall_tab_ptr] = ppstmp;
JonFreeman 0:435bf84ce48a 344 Hall_tab_ptr++;
JonFreeman 0:435bf84ce48a 345 if (Hall_tab_ptr >= MAIN_LOOP_ITERATION_Hz)
JonFreeman 0:435bf84ce48a 346 Hall_tab_ptr = 0;
JonFreeman 5:ca86a7848d54 347 PPS = latest_pulses_per_sec;
JonFreeman 5:ca86a7848d54 348 RPM = (latest_pulses_per_sec * 60) / 24;
JonFreeman 0:435bf84ce48a 349 return latest_pulses_per_sec;
JonFreeman 0:435bf84ce48a 350 }
JonFreeman 0:435bf84ce48a 351
JonFreeman 3:ecb00e0e8d68 352 bool motor::is_moving ()
JonFreeman 3:ecb00e0e8d68 353 {
JonFreeman 3:ecb00e0e8d68 354 return moving_flag;
JonFreeman 3:ecb00e0e8d68 355 }
JonFreeman 3:ecb00e0e8d68 356
JonFreeman 5:ca86a7848d54 357 /**
JonFreeman 5:ca86a7848d54 358 bool motor::set_mode (int m)
JonFreeman 5:ca86a7848d54 359 Use to set motor to one mode of HANDBRAKE, FORWARD, REVERSE, REGENBRAKE.
JonFreeman 5:ca86a7848d54 360 If this causes change of mode, also sets V and I to zero.
JonFreeman 5:ca86a7848d54 361 */
JonFreeman 0:435bf84ce48a 362 bool motor::set_mode (int m)
JonFreeman 0:435bf84ce48a 363 {
JonFreeman 2:04761b196473 364 if ((m != HANDBRAKE) && (m != FORWARD) && (m != REVERSE) && (m !=REGENBRAKE)) {
JonFreeman 2:04761b196473 365 pc.printf ("Error in set_mode, invalid mode %d\r\n", m);
JonFreeman 0:435bf84ce48a 366 return false;
JonFreeman 2:04761b196473 367 }
JonFreeman 5:ca86a7848d54 368 if (visible_mode != m) { // Mode change, kill volts and amps to be safe
JonFreeman 5:ca86a7848d54 369 set_V_limit (0.0);
JonFreeman 5:ca86a7848d54 370 set_I_limit (0.0);
JonFreeman 5:ca86a7848d54 371 visible_mode = m;
JonFreeman 5:ca86a7848d54 372 }
JonFreeman 3:ecb00e0e8d68 373 if (m == FORWARD || m == REVERSE)
JonFreeman 3:ecb00e0e8d68 374 m ^= direction;
JonFreeman 5:ca86a7848d54 375 inner_mode = m; // idea is to use inner_mode only in lut addressing, keep 'visible_mode' true regardless of setup data in eeprom
JonFreeman 0:435bf84ce48a 376 return true;
JonFreeman 0:435bf84ce48a 377 }
JonFreeman 0:435bf84ce48a 378
JonFreeman 0:435bf84ce48a 379 void motor::Hall_change ()
JonFreeman 0:435bf84ce48a 380 {
JonFreeman 3:ecb00e0e8d68 381 const int32_t delta_theta_lut[] = // Looks up -1 for forward move detected, +1 for reverse move detected, 0 for error or unknown
JonFreeman 3:ecb00e0e8d68 382 {
JonFreeman 3:ecb00e0e8d68 383 0, 0, 0, 0, 0, 0, 0, 0, // Previous Hindex was 0
JonFreeman 3:ecb00e0e8d68 384 0, 0, 0,-1, 0, 1, 0, 0, // Previous Hindex was 1
JonFreeman 3:ecb00e0e8d68 385 0, 0, 0, 1, 0, 0,-1, 0, // Previous Hindex was 2
JonFreeman 3:ecb00e0e8d68 386 0, 1,-1, 0, 0, 0, 0, 0, // Previous Hindex was 3
JonFreeman 3:ecb00e0e8d68 387 0, 0, 0, 0, 0,-1, 1, 0, // Previous Hindex was 4
JonFreeman 3:ecb00e0e8d68 388 0,-1, 0, 0, 1, 0, 0, 0, // Previous Hindex was 5
JonFreeman 3:ecb00e0e8d68 389 0, 0, 1, 0,-1, 0, 0, 0, // Previous Hindex was 6
JonFreeman 3:ecb00e0e8d68 390 0, 0, 0, 0, 0, 0, 0, 0, // Previous Hindex was 7
JonFreeman 3:ecb00e0e8d68 391 } ;
JonFreeman 3:ecb00e0e8d68 392 int32_t delta_theta = delta_theta_lut[(Hindex[1] << 3) | Hindex[0]];
JonFreeman 3:ecb00e0e8d68 393 if (delta_theta == 0)
JonFreeman 3:ecb00e0e8d68 394 encoder_error_cnt++;
JonFreeman 3:ecb00e0e8d68 395 else
JonFreeman 3:ecb00e0e8d68 396 angle_cnt += delta_theta;
JonFreeman 5:ca86a7848d54 397 *Motor_Port = lut[inner_mode | Hindex[0]]; // changed mode to inner_mode 27/04/18
JonFreeman 0:435bf84ce48a 398 Hall_total++;
JonFreeman 3:ecb00e0e8d68 399 Hindex[1] = Hindex[0];
JonFreeman 0:435bf84ce48a 400 }
JonFreeman 2:04761b196473 401
JonFreeman 5:ca86a7848d54 402
JonFreeman 6:f289a49c1eae 403 void temp_sensor_isr () { // got rising edge from LMT01. ALMOST CERTAIN this misses some
JonFreeman 6:f289a49c1eae 404 int t = temperature_timer.read_us (); // Must be being overrun by something, most likely culprit A-D reading ?
JonFreeman 6:f289a49c1eae 405 temperature_timer.reset ();
JonFreeman 5:ca86a7848d54 406 temp_sensor_count++;
JonFreeman 6:f289a49c1eae 407 if (t > 18) // Yes proved some interrupts get missed, this fixes temperature reading
JonFreeman 6:f289a49c1eae 408 temp_sensor_count++;
JonFreeman 6:f289a49c1eae 409 // T2 = !T2; // scope hanger
JonFreeman 5:ca86a7848d54 410 }
JonFreeman 5:ca86a7848d54 411
JonFreeman 3:ecb00e0e8d68 412 void MAH_isr ()
JonFreeman 0:435bf84ce48a 413 {
JonFreeman 3:ecb00e0e8d68 414 uint32_t x = 0;
JonFreeman 3:ecb00e0e8d68 415 MotorA.high_side_off ();
JonFreeman 3:ecb00e0e8d68 416 T3 = !T3;
JonFreeman 3:ecb00e0e8d68 417 if (MAH1 != 0) x |= 1;
JonFreeman 3:ecb00e0e8d68 418 if (MAH2 != 0) x |= 2;
JonFreeman 3:ecb00e0e8d68 419 if (MAH3 != 0) x |= 4;
JonFreeman 3:ecb00e0e8d68 420 MotorA.Hindex[0] = x; // New in [0], old in [1]
JonFreeman 0:435bf84ce48a 421 MotorA.Hall_change ();
JonFreeman 0:435bf84ce48a 422 }
JonFreeman 0:435bf84ce48a 423
JonFreeman 3:ecb00e0e8d68 424 void MBH_isr ()
JonFreeman 0:435bf84ce48a 425 {
JonFreeman 3:ecb00e0e8d68 426 uint32_t x = 0;
JonFreeman 3:ecb00e0e8d68 427 MotorB.high_side_off ();
JonFreeman 3:ecb00e0e8d68 428 if (MBH1 != 0) x |= 1;
JonFreeman 3:ecb00e0e8d68 429 if (MBH2 != 0) x |= 2;
JonFreeman 3:ecb00e0e8d68 430 if (MBH3 != 0) x |= 4;
JonFreeman 3:ecb00e0e8d68 431 MotorB.Hindex[0] = x;
JonFreeman 0:435bf84ce48a 432 MotorB.Hall_change ();
JonFreeman 0:435bf84ce48a 433 }
JonFreeman 0:435bf84ce48a 434
JonFreeman 0:435bf84ce48a 435
JonFreeman 0:435bf84ce48a 436 // End of Interrupt Service Routines
JonFreeman 0:435bf84ce48a 437
JonFreeman 3:ecb00e0e8d68 438 void motor::motor_set ()
JonFreeman 3:ecb00e0e8d68 439 {
JonFreeman 3:ecb00e0e8d68 440 Hindex[0] = read_Halls ();
JonFreeman 5:ca86a7848d54 441 *Motor_Port = lut[inner_mode | Hindex[0]];
JonFreeman 3:ecb00e0e8d68 442 }
JonFreeman 3:ecb00e0e8d68 443
JonFreeman 2:04761b196473 444 void setVI (double v, double i) {
JonFreeman 4:21d91465e4b1 445 MotorA.set_V_limit (v); // Sets max motor voltage
JonFreeman 4:21d91465e4b1 446 MotorA.set_I_limit (i); // Sets max motor current
JonFreeman 4:21d91465e4b1 447 MotorB.set_V_limit (v);
JonFreeman 4:21d91465e4b1 448 MotorB.set_I_limit (i);
JonFreeman 4:21d91465e4b1 449 }
JonFreeman 4:21d91465e4b1 450 void setV (double v) {
JonFreeman 2:04761b196473 451 MotorA.set_V_limit (v);
JonFreeman 4:21d91465e4b1 452 MotorB.set_V_limit (v);
JonFreeman 4:21d91465e4b1 453 }
JonFreeman 4:21d91465e4b1 454 void setI (double i) {
JonFreeman 2:04761b196473 455 MotorA.set_I_limit (i);
JonFreeman 2:04761b196473 456 MotorB.set_I_limit (i);
JonFreeman 0:435bf84ce48a 457 }
JonFreeman 2:04761b196473 458
JonFreeman 5:ca86a7848d54 459 void read_RPM (uint32_t * dest) {
JonFreeman 5:ca86a7848d54 460 dest[0] = MotorA.RPM;
JonFreeman 5:ca86a7848d54 461 dest[1] = MotorB.RPM;
JonFreeman 5:ca86a7848d54 462 }
JonFreeman 5:ca86a7848d54 463
JonFreeman 5:ca86a7848d54 464 void read_PPS (uint32_t * dest) {
JonFreeman 5:ca86a7848d54 465 dest[0] = MotorA.PPS;
JonFreeman 5:ca86a7848d54 466 dest[1] = MotorB.PPS;
JonFreeman 5:ca86a7848d54 467 }
JonFreeman 5:ca86a7848d54 468
JonFreeman 5:ca86a7848d54 469 void read_last_VI (double * d) { // only for test from cli
JonFreeman 5:ca86a7848d54 470 d[0] = MotorA.last_V;
JonFreeman 5:ca86a7848d54 471 d[1] = MotorA.last_I;
JonFreeman 5:ca86a7848d54 472 d[2] = MotorB.last_V;
JonFreeman 5:ca86a7848d54 473 d[3] = MotorB.last_I;
JonFreeman 5:ca86a7848d54 474 }
JonFreeman 5:ca86a7848d54 475
JonFreeman 3:ecb00e0e8d68 476
JonFreeman 5:ca86a7848d54 477 /**
JonFreeman 5:ca86a7848d54 478 void AtoD_reader () // Call to here every VOLTAGE_READ_INTERVAL_US = 50 once loop responds to flag set in isr
JonFreeman 5:ca86a7848d54 479 Not part of ISR
JonFreeman 5:ca86a7848d54 480 */
JonFreeman 3:ecb00e0e8d68 481 void AtoD_reader () // Call to here every VOLTAGE_READ_INTERVAL_US = 50 once loop responds to flag set in isr
JonFreeman 0:435bf84ce48a 482 {
JonFreeman 6:f289a49c1eae 483 static uint32_t i = 0, tab_ptr = 0;
JonFreeman 3:ecb00e0e8d68 484
JonFreeman 3:ecb00e0e8d68 485 if (MotorA.tickleon)
JonFreeman 3:ecb00e0e8d68 486 MotorA.high_side_off ();
JonFreeman 3:ecb00e0e8d68 487 if (MotorB.tickleon)
JonFreeman 3:ecb00e0e8d68 488 MotorB.high_side_off ();
JonFreeman 0:435bf84ce48a 489 if (AtoD_Semaphore > 20) {
JonFreeman 0:435bf84ce48a 490 pc.printf ("WARNING - silly semaphore count %d, limiting to sensible\r\n", AtoD_Semaphore);
JonFreeman 0:435bf84ce48a 491 AtoD_Semaphore = 20;
JonFreeman 0:435bf84ce48a 492 }
JonFreeman 0:435bf84ce48a 493 while (AtoD_Semaphore > 0) {
JonFreeman 0:435bf84ce48a 494 AtoD_Semaphore--;
JonFreeman 0:435bf84ce48a 495 // Code here to sequence through reading voltmeter, demand pot, ammeters
JonFreeman 0:435bf84ce48a 496 switch (i) { //
JonFreeman 0:435bf84ce48a 497 case 0:
JonFreeman 0:435bf84ce48a 498 volt_reading += Ain_SystemVolts.read_u16 (); // Result = Result + New Reading
JonFreeman 0:435bf84ce48a 499 volt_reading >>= 1; // Result = Result / 2
JonFreeman 0:435bf84ce48a 500 break; // i.e. Very simple digital low pass filter
JonFreeman 0:435bf84ce48a 501 case 1:
JonFreeman 0:435bf84ce48a 502 driverpot_reading += Ain_DriverPot.read_u16 ();
JonFreeman 0:435bf84ce48a 503 driverpot_reading >>= 1;
JonFreeman 0:435bf84ce48a 504 break;
JonFreeman 0:435bf84ce48a 505 case 2:
JonFreeman 0:435bf84ce48a 506 MotorA.current_samples[tab_ptr] = Motor_A_Current.read_u16 (); //
JonFreeman 0:435bf84ce48a 507 break;
JonFreeman 0:435bf84ce48a 508 case 3:
JonFreeman 0:435bf84ce48a 509 MotorB.current_samples[tab_ptr++] = Motor_B_Current.read_u16 (); //
JonFreeman 0:435bf84ce48a 510 if (tab_ptr >= CURRENT_SAMPLES_AVERAGED) // Current reading will be lumpy and spikey, so put through moving average filter
JonFreeman 0:435bf84ce48a 511 tab_ptr = 0;
JonFreeman 0:435bf84ce48a 512 break;
JonFreeman 0:435bf84ce48a 513 }
JonFreeman 0:435bf84ce48a 514 i++; // prepare to read the next input in response to the next interrupt
JonFreeman 0:435bf84ce48a 515 if (i > 3)
JonFreeman 0:435bf84ce48a 516 i = 0;
JonFreeman 3:ecb00e0e8d68 517 } // end of while (AtoD_Semaphore > 0) {
JonFreeman 3:ecb00e0e8d68 518 if (MotorA.tickleon) {
JonFreeman 3:ecb00e0e8d68 519 MotorA.tickleon--;
JonFreeman 5:ca86a7848d54 520 MotorA.motor_set (); // Reactivate any high side switches turned off above
JonFreeman 3:ecb00e0e8d68 521 }
JonFreeman 3:ecb00e0e8d68 522 if (MotorB.tickleon) {
JonFreeman 3:ecb00e0e8d68 523 MotorB.tickleon--;
JonFreeman 3:ecb00e0e8d68 524 MotorB.motor_set ();
JonFreeman 0:435bf84ce48a 525 }
JonFreeman 0:435bf84ce48a 526 }
JonFreeman 0:435bf84ce48a 527
JonFreeman 0:435bf84ce48a 528 /** double Read_DriverPot ()
JonFreeman 0:435bf84ce48a 529 * driverpot_reading is a global 16 bit unsigned int updated in interrupt service routine
JonFreeman 0:435bf84ce48a 530 * ISR also filters signal
JonFreeman 0:435bf84ce48a 531 * This function returns normalised double (range 0.0 to 1.0) representation of driver pot position
JonFreeman 0:435bf84ce48a 532 */
JonFreeman 0:435bf84ce48a 533 double Read_DriverPot ()
JonFreeman 0:435bf84ce48a 534 {
JonFreeman 5:ca86a7848d54 535 return ((double) driverpot_reading) / 65536.0; // Normalise 0.0 <= control pot <= 1.0
JonFreeman 0:435bf84ce48a 536 }
JonFreeman 0:435bf84ce48a 537
JonFreeman 0:435bf84ce48a 538 double Read_BatteryVolts ()
JonFreeman 0:435bf84ce48a 539 {
JonFreeman 5:ca86a7848d54 540 return ((double) volt_reading) / 951.0; // divisor fiddled to make voltage reading correct !
JonFreeman 0:435bf84ce48a 541 }
JonFreeman 0:435bf84ce48a 542
JonFreeman 5:ca86a7848d54 543 void read_supply_vi (double * val) { // called from cli
JonFreeman 5:ca86a7848d54 544 val[0] = MotorA.I.ave;
JonFreeman 5:ca86a7848d54 545 val[1] = MotorB.I.ave;
JonFreeman 5:ca86a7848d54 546 val[2] = Read_BatteryVolts ();
JonFreeman 0:435bf84ce48a 547 }
JonFreeman 0:435bf84ce48a 548
JonFreeman 5:ca86a7848d54 549 void mode_set (int mode, double val) { // called from cli to set fw, re, rb, hb
JonFreeman 2:04761b196473 550 MotorA.set_mode (mode);
JonFreeman 2:04761b196473 551 MotorB.set_mode (mode);
JonFreeman 2:04761b196473 552 if (mode == REGENBRAKE) {
JonFreeman 5:ca86a7848d54 553 if (val > 1.0)
JonFreeman 5:ca86a7848d54 554 val = 1.0;
JonFreeman 5:ca86a7848d54 555 if (val < 0.0)
JonFreeman 5:ca86a7848d54 556 val = 0.0;
JonFreeman 5:ca86a7848d54 557 val *= 0.9; // set upper limit, this is essential
JonFreeman 5:ca86a7848d54 558 val = sqrt (val); // to linearise effect
JonFreeman 5:ca86a7848d54 559 setVI (val, 1.0);
JonFreeman 2:04761b196473 560 }
JonFreeman 2:04761b196473 561 }
JonFreeman 0:435bf84ce48a 562
JonFreeman 7:6deaeace9a3e 563 extern void setup_comms () ;
JonFreeman 7:6deaeace9a3e 564 extern void command_line_interpreter_pc () ;
JonFreeman 7:6deaeace9a3e 565 extern void command_line_interpreter_loco () ;
JonFreeman 0:435bf84ce48a 566 extern int check_24LC64 () ; // Call from near top of main() to init i2c bus
JonFreeman 0:435bf84ce48a 567 extern bool wr_24LC64 (int mem_start_addr, char * source, int length) ;
JonFreeman 0:435bf84ce48a 568 extern bool rd_24LC64 (int mem_start_addr, char * dest, int length) ;
JonFreeman 0:435bf84ce48a 569
JonFreeman 5:ca86a7848d54 570 /*struct motorpairoptions { // This to be user settable in eeprom, 32 bytes
JonFreeman 3:ecb00e0e8d68 571 uint8_t MotA_dir, // 0 or 1
JonFreeman 3:ecb00e0e8d68 572 MotB_dir, // 0 or 1
JonFreeman 3:ecb00e0e8d68 573 gang, // 0 for separate control (robot mode), 1 for ganged loco bogie mode
JonFreeman 3:ecb00e0e8d68 574 serv1, // 0, 1, 2 = Not used, Input, Output
JonFreeman 3:ecb00e0e8d68 575 serv2, // 0, 1, 2 = Not used, Input, Output
JonFreeman 3:ecb00e0e8d68 576 cmd_source, // 0 Invalid, 1 COM1, 2 COM2, 3 Pot, 4 Servo1, 5 Servo2
JonFreeman 3:ecb00e0e8d68 577 last;
JonFreeman 3:ecb00e0e8d68 578 } ;
JonFreeman 5:ca86a7848d54 579 */
JonFreeman 3:ecb00e0e8d68 580 int I_Am () { // Returns boards id number as ASCII char
JonFreeman 5:ca86a7848d54 581 return IAm;
JonFreeman 3:ecb00e0e8d68 582 }
JonFreeman 3:ecb00e0e8d68 583
JonFreeman 7:6deaeace9a3e 584 double mph (int rpm) {
JonFreeman 7:6deaeace9a3e 585 if (mode_good_flag) {
JonFreeman 7:6deaeace9a3e 586 return rpm2mph * (double) rpm;
JonFreeman 7:6deaeace9a3e 587 }
JonFreeman 7:6deaeace9a3e 588 return -1.0;
JonFreeman 7:6deaeace9a3e 589 }
JonFreeman 4:21d91465e4b1 590
JonFreeman 0:435bf84ce48a 591 int main()
JonFreeman 0:435bf84ce48a 592 {
JonFreeman 4:21d91465e4b1 593 int eighth_sec_count = 0;
JonFreeman 0:435bf84ce48a 594
JonFreeman 4:21d91465e4b1 595 MotA = 0; // Output all 0s to Motor drive ports A and B
JonFreeman 0:435bf84ce48a 596 MotB = 0;
JonFreeman 7:6deaeace9a3e 597 // MotPtr[0] = &MotorA; // Pointers to motor class objects
JonFreeman 7:6deaeace9a3e 598 // MotPtr[1] = &MotorB;
JonFreeman 5:ca86a7848d54 599
JonFreeman 6:f289a49c1eae 600 Temperature_pin.fall (&temp_sensor_isr);
JonFreeman 6:f289a49c1eae 601 Temperature_pin.mode (PullUp);
JonFreeman 4:21d91465e4b1 602
JonFreeman 4:21d91465e4b1 603 MAH1.rise (& MAH_isr); // Set up interrupt vectors
JonFreeman 3:ecb00e0e8d68 604 MAH1.fall (& MAH_isr);
JonFreeman 3:ecb00e0e8d68 605 MAH2.rise (& MAH_isr);
JonFreeman 3:ecb00e0e8d68 606 MAH2.fall (& MAH_isr);
JonFreeman 3:ecb00e0e8d68 607 MAH3.rise (& MAH_isr);
JonFreeman 3:ecb00e0e8d68 608 MAH3.fall (& MAH_isr);
JonFreeman 3:ecb00e0e8d68 609
JonFreeman 3:ecb00e0e8d68 610 MBH1.rise (& MBH_isr);
JonFreeman 3:ecb00e0e8d68 611 MBH1.fall (& MBH_isr);
JonFreeman 3:ecb00e0e8d68 612 MBH2.rise (& MBH_isr);
JonFreeman 3:ecb00e0e8d68 613 MBH2.fall (& MBH_isr);
JonFreeman 3:ecb00e0e8d68 614 MBH3.rise (& MBH_isr);
JonFreeman 3:ecb00e0e8d68 615 MBH3.fall (& MBH_isr);
JonFreeman 4:21d91465e4b1 616
JonFreeman 0:435bf84ce48a 617 MAH1.mode (PullUp);
JonFreeman 0:435bf84ce48a 618 MAH2.mode (PullUp);
JonFreeman 0:435bf84ce48a 619 MAH3.mode (PullUp);
JonFreeman 0:435bf84ce48a 620 MBH1.mode (PullUp);
JonFreeman 0:435bf84ce48a 621 MBH2.mode (PullUp);
JonFreeman 0:435bf84ce48a 622 MBH3.mode (PullUp);
JonFreeman 4:21d91465e4b1 623 Servo1_i.mode (PullUp);
JonFreeman 4:21d91465e4b1 624 Servo2_i.mode (PullUp);
JonFreeman 4:21d91465e4b1 625
JonFreeman 0:435bf84ce48a 626 // Setup system timers to cause periodic interrupts to synchronise and automate volt and current readings, loop repeat rate etc
JonFreeman 0:435bf84ce48a 627 tick_vread.attach_us (&ISR_voltage_reader, VOLTAGE_READ_INTERVAL_US); // Start periodic interrupt generator
JonFreeman 0:435bf84ce48a 628 loop_timer.attach_us (&ISR_loop_timer, MAIN_LOOP_REPEAT_TIME_US); // Start periodic interrupt generator
JonFreeman 6:f289a49c1eae 629 temperature_find_ticker.attach_us (&ISR_temperature_find_ticker, 960);
JonFreeman 0:435bf84ce48a 630 // Done setting up system interrupt timers
JonFreeman 6:f289a49c1eae 631 temperature_timer.start ();
JonFreeman 0:435bf84ce48a 632
JonFreeman 6:f289a49c1eae 633 // const int TXTBUFSIZ = 36;
JonFreeman 6:f289a49c1eae 634 // char buff[TXTBUFSIZ];
JonFreeman 3:ecb00e0e8d68 635 pc.baud (9600);
JonFreeman 4:21d91465e4b1 636 com3.baud (1200);
JonFreeman 4:21d91465e4b1 637 com2.baud (19200);
JonFreeman 7:6deaeace9a3e 638 setup_comms ();
JonFreeman 6:f289a49c1eae 639
JonFreeman 7:6deaeace9a3e 640 IAm = '0';
JonFreeman 5:ca86a7848d54 641 if (check_24LC64() != 0xa0) { // searches for i2c devices, returns address of highest found
JonFreeman 0:435bf84ce48a 642 pc.printf ("Check for 24LC64 eeprom FAILED\r\n");
JonFreeman 5:ca86a7848d54 643 com2.printf ("Check for 24LC64 eeprom FAILED\r\n");
JonFreeman 7:6deaeace9a3e 644 eeprom_flag = false;
JonFreeman 0:435bf84ce48a 645 }
JonFreeman 5:ca86a7848d54 646 else { // Found 24LC64 memory on I2C
JonFreeman 7:6deaeace9a3e 647 eeprom_flag = true;
JonFreeman 5:ca86a7848d54 648 bool k;
JonFreeman 6:f289a49c1eae 649 k = rd_24LC64 (0, mode_bytes, 32);
JonFreeman 5:ca86a7848d54 650 if (!k)
JonFreeman 5:ca86a7848d54 651 com2.printf ("Error reading from eeprom\r\n");
JonFreeman 5:ca86a7848d54 652
JonFreeman 5:ca86a7848d54 653 int err = 0;
JonFreeman 6:f289a49c1eae 654 for (int i = 0; i < numof_eeprom_options; i++) {
JonFreeman 6:f289a49c1eae 655 if ((mode_bytes[i] < option_list[i].min) || (mode_bytes[i] > option_list[i].max)) {
JonFreeman 5:ca86a7848d54 656 com2.printf ("EEROM error with %s\r\n", option_list[i].t);
JonFreeman 5:ca86a7848d54 657 err++;
JonFreeman 5:ca86a7848d54 658 }
JonFreeman 5:ca86a7848d54 659 // else
JonFreeman 5:ca86a7848d54 660 // com2.printf ("%2x Good %s\r\n", buff[i], option_list[i].t);
JonFreeman 5:ca86a7848d54 661 }
JonFreeman 7:6deaeace9a3e 662 rpm2mph = 0.0;
JonFreeman 5:ca86a7848d54 663 if (err == 0) {
JonFreeman 7:6deaeace9a3e 664 mode_good_flag = true;
JonFreeman 6:f289a49c1eae 665 MotorA.direction_set (mode_bytes[MOTADIR]);
JonFreeman 6:f289a49c1eae 666 MotorB.direction_set (mode_bytes[MOTBDIR]);
JonFreeman 6:f289a49c1eae 667 IAm = mode_bytes[ID];
JonFreeman 7:6deaeace9a3e 668 rpm2mph = 60.0 // to Motor Revs per hour;
JonFreeman 7:6deaeace9a3e 669 * ((double)mode_bytes[MOTPIN] / (double)mode_bytes[WHEELGEAR]) // Wheel revs per hour
JonFreeman 7:6deaeace9a3e 670 * PI * ((double)mode_bytes[WHEELDIA] / 1000.0) // metres per hour
JonFreeman 7:6deaeace9a3e 671 * 39.37 / (1760.0 * 36.0); // miles per hour
JonFreeman 5:ca86a7848d54 672 }
JonFreeman 5:ca86a7848d54 673 // Alternative ID 1 to 9
JonFreeman 5:ca86a7848d54 674 // com2.printf ("Alternative ID = 0x%2x\r\n", buff[6]);
JonFreeman 5:ca86a7848d54 675 }
JonFreeman 6:f289a49c1eae 676 // T1 = 0; Now WRONGLY hoped to be InterruptIn counting pulses from LMT01 temperature sensor
JonFreeman 5:ca86a7848d54 677 T2 = 0; // T2, T3, T4 As yet unused pins
JonFreeman 0:435bf84ce48a 678 T3 = 0;
JonFreeman 0:435bf84ce48a 679 T4 = 0;
JonFreeman 5:ca86a7848d54 680 // T5 = 0; now input from fw/re on remote control box
JonFreeman 0:435bf84ce48a 681 T6 = 0;
JonFreeman 3:ecb00e0e8d68 682 // MotPtr[0]->set_mode (REGENBRAKE);
JonFreeman 2:04761b196473 683 MotorA.set_mode (REGENBRAKE);
JonFreeman 2:04761b196473 684 MotorB.set_mode (REGENBRAKE);
JonFreeman 5:ca86a7848d54 685 setVI (0.9, 0.5);
JonFreeman 5:ca86a7848d54 686
JonFreeman 4:21d91465e4b1 687 Servos[0] = Servos[1] = NULL;
JonFreeman 4:21d91465e4b1 688 // NOTE The ONLY way to get both servos working properly is to NOT use any if (bla) Servo ervo1(PB_8);
JonFreeman 4:21d91465e4b1 689 // Only works with unconditional inline code
JonFreeman 4:21d91465e4b1 690 // However, setup code for Input by default,
JonFreeman 4:21d91465e4b1 691 // This can be used to monitor Servo output also !
JonFreeman 4:21d91465e4b1 692 Servo Servo1 (PB_8) ;
JonFreeman 4:21d91465e4b1 693 Servos[0] = & Servo1;
JonFreeman 4:21d91465e4b1 694 Servo Servo2 (PB_9) ;
JonFreeman 4:21d91465e4b1 695 Servos[1] = & Servo2;
JonFreeman 6:f289a49c1eae 696
JonFreeman 7:6deaeace9a3e 697 // pc.printf ("last_temp_count = %d\r\n", last_temp_count); // Has had time to do at least 1 conversion
JonFreeman 6:f289a49c1eae 698 if ((last_temp_count > 160) && (last_temp_count < 2400)) // in range -40 to +100 degree C
JonFreeman 6:f289a49c1eae 699 temp_sensor_exists = true;
JonFreeman 4:21d91465e4b1 700 /*
JonFreeman 0:435bf84ce48a 701 // Setup Complete ! Can now start main control forever loop.
JonFreeman 3:ecb00e0e8d68 702 // March 16th 2018 thoughts !!!
JonFreeman 3:ecb00e0e8d68 703 // Control from one of several sources and types as selected in options bytes in eeprom.
JonFreeman 3:ecb00e0e8d68 704 // Control could be from e.g. Pot, Com2, Servos etc.
JonFreeman 3:ecb00e0e8d68 705 // Suggest e.g.
JonFreeman 4:21d91465e4b1 706 */ /*
JonFreeman 3:ecb00e0e8d68 707 switch (mode_byte) { // executed once only upon startup
JonFreeman 3:ecb00e0e8d68 708 case POT:
JonFreeman 3:ecb00e0e8d68 709 while (1) { // forever loop
JonFreeman 3:ecb00e0e8d68 710 call common_stuff ();
JonFreeman 3:ecb00e0e8d68 711 ...
JonFreeman 3:ecb00e0e8d68 712 }
JonFreeman 3:ecb00e0e8d68 713 break;
JonFreeman 3:ecb00e0e8d68 714 case COM2:
JonFreeman 3:ecb00e0e8d68 715 while (1) { // forever loop
JonFreeman 3:ecb00e0e8d68 716 call common_stuff ();
JonFreeman 3:ecb00e0e8d68 717 ...
JonFreeman 3:ecb00e0e8d68 718 }
JonFreeman 3:ecb00e0e8d68 719 break;
JonFreeman 3:ecb00e0e8d68 720 }
JonFreeman 3:ecb00e0e8d68 721 */
JonFreeman 7:6deaeace9a3e 722 // pc.printf ("Ready to go!, wheel gear in position %d\r\n", WHEELGEAR);
JonFreeman 7:6deaeace9a3e 723 pc.printf ("About to start!\r\n");
JonFreeman 0:435bf84ce48a 724 while (1) { // Loop forever, repeats synchroised by waiting for ticker Interrupt Service Routine to set 'loop_flag' true
JonFreeman 0:435bf84ce48a 725 while (!loop_flag) { // Most of the time is spent in this loop, repeatedly re-checking for commands from pc port
JonFreeman 7:6deaeace9a3e 726 command_line_interpreter_pc () ; // Proceed beyond here once loop_timer ticker ISR has set loop_flag true
JonFreeman 7:6deaeace9a3e 727 command_line_interpreter_loco () ; // Proceed beyond here once loop_timer ticker ISR has set loop_flag true
JonFreeman 0:435bf84ce48a 728 AtoD_reader (); // Performs A to D conversions at rate set by ticker interrupts
JonFreeman 0:435bf84ce48a 729 }
JonFreeman 0:435bf84ce48a 730 loop_flag = false; // Clear flag set by ticker interrupt handler
JonFreeman 5:ca86a7848d54 731 MotorA.pulses_per_sec (); // Needed to keep table updated to give reading in Hall transitions per second
JonFreeman 5:ca86a7848d54 732 MotorB.pulses_per_sec (); // Read MotorX.PPS to read pulses per sec or MotorX.RPM to read motor RPM
JonFreeman 3:ecb00e0e8d68 733 T4 = !T4; // toggle to hang scope on to verify loop execution
JonFreeman 0:435bf84ce48a 734 // do stuff
JonFreeman 0:435bf84ce48a 735 if (flag_8Hz) { // do slower stuff
JonFreeman 0:435bf84ce48a 736 flag_8Hz = false;
JonFreeman 3:ecb00e0e8d68 737 LED = !LED; // Toggle LED on board, should be seen to fast flash
JonFreeman 5:ca86a7848d54 738 WatchDog--;
JonFreeman 5:ca86a7848d54 739 if (WatchDog == 0) { // Deal with WatchDog timer timeout here
JonFreeman 5:ca86a7848d54 740 setVI (0.0, 0.0); // set motor volts and amps to zero
JonFreeman 5:ca86a7848d54 741 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 5:ca86a7848d54 742 } // End of dealing with WatchDog timer timeout
JonFreeman 5:ca86a7848d54 743 if (WatchDog < 0)
JonFreeman 5:ca86a7848d54 744 WatchDog = 0;
JonFreeman 4:21d91465e4b1 745 eighth_sec_count++;
JonFreeman 4:21d91465e4b1 746 if (eighth_sec_count > 6) { // Send some status info out of serial port every second and a bit or thereabouts
JonFreeman 4:21d91465e4b1 747 eighth_sec_count = 0;
JonFreeman 6:f289a49c1eae 748 MotorA.current_calc (); // Updates readings in MotorA.I.min, MotorA.I.ave and MotorA.I.max
JonFreeman 6:f289a49c1eae 749 MotorB.current_calc ();
JonFreeman 7:6deaeace9a3e 750 /* if (temp_sensor_exists) {
JonFreeman 6:f289a49c1eae 751 double tmprt = (double) last_temp_count;
JonFreeman 6:f289a49c1eae 752 tmprt /= 16.0;
JonFreeman 6:f289a49c1eae 753 tmprt -= 50.0;
JonFreeman 6:f289a49c1eae 754 pc.printf ("Temp %.2f\r\n", tmprt);
JonFreeman 7:6deaeace9a3e 755 }*/
JonFreeman 5:ca86a7848d54 756 // 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 757 }
JonFreeman 0:435bf84ce48a 758 } // End of if(flag_8Hz)
JonFreeman 0:435bf84ce48a 759 } // End of main programme loop
JonFreeman 0:435bf84ce48a 760 }