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:
Tue Jun 09 09:20:19 2020 +0000
Revision:
16:d1e4b9ad3b8b
Parent:
14:acaa1add097b
About to tidy i2c stuff

Who changed what in which revision?

UserRevisionLine numberNew contents of line
JonFreeman 11:bfb73f083009 1 #include "mbed.h"
JonFreeman 11:bfb73f083009 2 #include "BufferedSerial.h"
JonFreeman 11:bfb73f083009 3 #include "Radio_Control_In.h"
JonFreeman 16:d1e4b9ad3b8b 4 #include "STM3_ESC.h"
JonFreeman 11:bfb73f083009 5 /**class RControl_In
JonFreeman 11:bfb73f083009 6 Jon Freeman
JonFreeman 11:bfb73f083009 7 Jan 2019
JonFreeman 11:bfb73f083009 8
JonFreeman 11:bfb73f083009 9 Checks for __-__ duration 800-2200us
JonFreeman 11:bfb73f083009 10 Checks repetition rate in range 5-25ms
JonFreeman 11:bfb73f083009 11 */
JonFreeman 11:bfb73f083009 12 extern BufferedSerial pc;
JonFreeman 16:d1e4b9ad3b8b 13 //extern eeprom_settings user_settings ;
JonFreeman 11:bfb73f083009 14
JonFreeman 11:bfb73f083009 15 // RControl_In::RControl_In () { // Default Constructor
JonFreeman 11:bfb73f083009 16 // pulse_width_us = period_us = pulse_count = 0;
JonFreeman 11:bfb73f083009 17 // lost_chan_return_value = 0.0;
JonFreeman 11:bfb73f083009 18 // } ;
JonFreeman 11:bfb73f083009 19 // RControl_In::RControl_In (PinName inp) : pulse_in(inp) { // Default Constructor
JonFreeman 11:bfb73f083009 20 // pulse_width_us = period_us = pulse_count = 0;
JonFreeman 11:bfb73f083009 21 // lost_chan_return_value = 0.0;
JonFreeman 11:bfb73f083009 22 // } ;
JonFreeman 11:bfb73f083009 23 /**
JonFreeman 11:bfb73f083009 24 */
JonFreeman 11:bfb73f083009 25 void RControl_In::set_lost_chan_return_value (double d) {
JonFreeman 11:bfb73f083009 26 lost_chan_return_value = d;
JonFreeman 11:bfb73f083009 27 }
JonFreeman 11:bfb73f083009 28
JonFreeman 11:bfb73f083009 29 uint32_t RControl_In::pulsewidth ()
JonFreeman 11:bfb73f083009 30 {
JonFreeman 11:bfb73f083009 31 return pulse_width_us;
JonFreeman 11:bfb73f083009 32 }
JonFreeman 11:bfb73f083009 33
JonFreeman 11:bfb73f083009 34 uint32_t RControl_In::pulsecount ()
JonFreeman 11:bfb73f083009 35 {
JonFreeman 11:bfb73f083009 36 return pulse_count;
JonFreeman 11:bfb73f083009 37 }
JonFreeman 11:bfb73f083009 38
JonFreeman 11:bfb73f083009 39 uint32_t RControl_In::period ()
JonFreeman 11:bfb73f083009 40 {
JonFreeman 11:bfb73f083009 41 return period_us;
JonFreeman 11:bfb73f083009 42 }
JonFreeman 11:bfb73f083009 43
JonFreeman 11:bfb73f083009 44 bool RControl_In::validate_rx ()
JonFreeman 11:bfb73f083009 45 { // Tests for pulse width and repetition rates being believable
JonFreeman 11:bfb73f083009 46 return !((period_us < 5000) || (period_us > 25000) || (pulse_width_us < 800) || (pulse_width_us > 2200));
JonFreeman 11:bfb73f083009 47 }
JonFreeman 11:bfb73f083009 48
JonFreeman 16:d1e4b9ad3b8b 49 bool RControl_In::energise (struct RC_stick_info & stick, struct brushless_motor & motor) { // December 2019
JonFreeman 16:d1e4b9ad3b8b 50 if (stick.active) {
JonFreeman 16:d1e4b9ad3b8b 51 if (stick.zone == ZONE_DRIVE) {
JonFreeman 16:d1e4b9ad3b8b 52 motor.set_mode (stick.stick_implied_motor_direction == 1 ? MOTOR_FORWARD : MOTOR_REVERSE);
JonFreeman 16:d1e4b9ad3b8b 53 motor.set_V_limit (stick.drive_effort);
JonFreeman 16:d1e4b9ad3b8b 54 motor.set_I_limit (stick.drive_effort); // This could be 1.0, or other options
JonFreeman 16:d1e4b9ad3b8b 55 }
JonFreeman 16:d1e4b9ad3b8b 56 if (stick.zone == ZONE_BRAKE) {
JonFreeman 16:d1e4b9ad3b8b 57 motor.brake (stick.brake_effort);
JonFreeman 16:d1e4b9ad3b8b 58 }
JonFreeman 16:d1e4b9ad3b8b 59 }
JonFreeman 16:d1e4b9ad3b8b 60 return stick.active;
JonFreeman 16:d1e4b9ad3b8b 61 }
JonFreeman 16:d1e4b9ad3b8b 62
JonFreeman 16:d1e4b9ad3b8b 63 bool RControl_In::read (class RC_stick_info & stick) { // December 2019
JonFreeman 16:d1e4b9ad3b8b 64 double dtmp;
JonFreeman 16:d1e4b9ad3b8b 65 uint32_t old_zone = stick.zone;
JonFreeman 16:d1e4b9ad3b8b 66 stick.chan_mode = get_chanmode(); // 0 disabled, 1 uni-dir, or 2 bi-dir
JonFreeman 16:d1e4b9ad3b8b 67 stick.active = validate_rx(); // True if RC Rx delivering believable pulse duration and timing
JonFreeman 16:d1e4b9ad3b8b 68 if (stick.active && (stick.chan_mode < 1 || stick.chan_mode > 2)) { // Should signal an error here
JonFreeman 16:d1e4b9ad3b8b 69 stick.active = false;
JonFreeman 16:d1e4b9ad3b8b 70 }
JonFreeman 16:d1e4b9ad3b8b 71 if (stick.active) {
JonFreeman 16:d1e4b9ad3b8b 72 stick.raw = (double) (pulse_width_us - 1000); // Read pulse width from Rx, left with -200.0 to + 1200.0 allowing for some margin
JonFreeman 16:d1e4b9ad3b8b 73 stick.raw /= 1000.0; // pulse width varies between typ 1000 to 2000 micro seconds
JonFreeman 16:d1e4b9ad3b8b 74 stick.raw += range_offset; // range now normalised to 0.0 <= raw <= 1.0
JonFreeman 16:d1e4b9ad3b8b 75 if (stick.raw > 1.0) stick.raw = 1.0;
JonFreeman 16:d1e4b9ad3b8b 76 if (stick.raw < 0.0) stick.raw = 0.0; // clipped to strict limits 0.0 and 1.0
JonFreeman 16:d1e4b9ad3b8b 77 if (stick_sense != 0)
JonFreeman 16:d1e4b9ad3b8b 78 stick.raw = 1.0 - stick.raw; // user setting allows for stick sense reversal
JonFreeman 16:d1e4b9ad3b8b 79 stick.deflection = stick.raw;
JonFreeman 16:d1e4b9ad3b8b 80 stick.stick_implied_motor_direction = +1; // -1 Reverse, 0 Stopped, +1 Forward
JonFreeman 16:d1e4b9ad3b8b 81 if (stick.chan_mode == 2) { // Bi-directional centre zero stick mode selected by user
JonFreeman 16:d1e4b9ad3b8b 82 stick.deflection = (stick.raw * 2.0) - 1.0; // range here -1.0 <= deflection <= +1.0
JonFreeman 16:d1e4b9ad3b8b 83 if (stick.deflection < 0.0) {
JonFreeman 16:d1e4b9ad3b8b 84 stick.deflection = 0.0 - stick.deflection; // range inverted if negative, direction info separated out
JonFreeman 16:d1e4b9ad3b8b 85 stick.stick_implied_motor_direction = -1; // -1 Reverse, 0 Stopped, +1 Forward (almost never 0)
JonFreeman 16:d1e4b9ad3b8b 86 } // endof deflection < 0.0
JonFreeman 16:d1e4b9ad3b8b 87 } // endof if chan_mode == 2
JonFreeman 16:d1e4b9ad3b8b 88 // Now find zone from deflection
JonFreeman 16:d1e4b9ad3b8b 89 stick.zone = ZONE_COAST;
JonFreeman 16:d1e4b9ad3b8b 90 if (stick.deflection < (brake_segment - 0.02)) // size of brake_segment user settable
JonFreeman 16:d1e4b9ad3b8b 91 stick.zone = ZONE_BRAKE;
JonFreeman 16:d1e4b9ad3b8b 92 if (stick.deflection > (brake_segment + 0.02)) // Tiny 'freewheel' COAST band between drive and brake
JonFreeman 16:d1e4b9ad3b8b 93 stick.zone = ZONE_DRIVE;
JonFreeman 16:d1e4b9ad3b8b 94 if (old_zone != ZONE_COAST && old_zone != stick.zone) //
JonFreeman 16:d1e4b9ad3b8b 95 stick.zone = ZONE_COAST; // Ensures transitions between BRAKE and DRIVE go via COAST
JonFreeman 16:d1e4b9ad3b8b 96 switch (stick.zone) {
JonFreeman 16:d1e4b9ad3b8b 97 case ZONE_COAST:
JonFreeman 16:d1e4b9ad3b8b 98 stick.drive_effort = 0.0;
JonFreeman 16:d1e4b9ad3b8b 99 stick.brake_effort = 0.0;
JonFreeman 16:d1e4b9ad3b8b 100 break;
JonFreeman 16:d1e4b9ad3b8b 101 case ZONE_BRAKE:
JonFreeman 16:d1e4b9ad3b8b 102 stick.brake_effort = (brake_segment - stick.deflection) / brake_segment; // 1.0 at zero deflection, reducing to 0.0 on boundary with DRIVE
JonFreeman 16:d1e4b9ad3b8b 103 stick.drive_effort = 0.0;
JonFreeman 16:d1e4b9ad3b8b 104 break;
JonFreeman 16:d1e4b9ad3b8b 105 case ZONE_DRIVE:
JonFreeman 16:d1e4b9ad3b8b 106 stick.brake_effort = 0.0;
JonFreeman 16:d1e4b9ad3b8b 107 dtmp = (stick.deflection - brake_segment) / (1.0 - brake_segment);
JonFreeman 16:d1e4b9ad3b8b 108 if (dtmp > stick.drive_effort) { // Stick has moved in increasing demand direction
JonFreeman 16:d1e4b9ad3b8b 109 stick.drive_effort *= (1.0 - stick_attack); // Apply 'viscous damping' to demand increases for smoother operation
JonFreeman 16:d1e4b9ad3b8b 110 stick.drive_effort += (dtmp * stick_attack); // Low pass filter, time constant variable by choosing 'stick_attack' value %age
JonFreeman 16:d1e4b9ad3b8b 111 }
JonFreeman 16:d1e4b9ad3b8b 112 else // Reduction or no increase in demanded drive effort
JonFreeman 16:d1e4b9ad3b8b 113 stick.drive_effort = dtmp; // Reduce demand immediately, i.e. no viscous damping on reduced demand
JonFreeman 16:d1e4b9ad3b8b 114 break;
JonFreeman 16:d1e4b9ad3b8b 115 } // endof switch
JonFreeman 16:d1e4b9ad3b8b 116 } // endof if active
JonFreeman 16:d1e4b9ad3b8b 117 else { // stick Not active
JonFreeman 16:d1e4b9ad3b8b 118 stick.zone = ZONE_BRAKE;
JonFreeman 16:d1e4b9ad3b8b 119 stick.raw = 0.0;
JonFreeman 16:d1e4b9ad3b8b 120 stick.deflection = 0.0;
JonFreeman 16:d1e4b9ad3b8b 121 } // endof not active
JonFreeman 16:d1e4b9ad3b8b 122 return stick.active;
JonFreeman 16:d1e4b9ad3b8b 123 }
JonFreeman 16:d1e4b9ad3b8b 124
JonFreeman 16:d1e4b9ad3b8b 125
JonFreeman 16:d1e4b9ad3b8b 126 void RControl_In::set_offset (signed char offs, char brake_pcent, char attack) { // Takes user_settings[RCIx_TRIM]
JonFreeman 16:d1e4b9ad3b8b 127 brake_segment = ((double) brake_pcent) / 100.0;
JonFreeman 16:d1e4b9ad3b8b 128 stick_attack = ((double) attack) / 100.0;
JonFreeman 16:d1e4b9ad3b8b 129 range_offset = (double) offs;
JonFreeman 16:d1e4b9ad3b8b 130 range_offset /= 1000.0; // This is where to set range_offset sensitivity
JonFreeman 16:d1e4b9ad3b8b 131 // pc.printf ("In RControl_In::set_offset, input signed char = %d, out f %.3f\r\n", offs, range_offset);
JonFreeman 16:d1e4b9ad3b8b 132 }
JonFreeman 16:d1e4b9ad3b8b 133
JonFreeman 16:d1e4b9ad3b8b 134 uint32_t RControl_In::get_chanmode () {
JonFreeman 16:d1e4b9ad3b8b 135 return chan_mode;
JonFreeman 16:d1e4b9ad3b8b 136 }
JonFreeman 16:d1e4b9ad3b8b 137
JonFreeman 16:d1e4b9ad3b8b 138 void RControl_In::set_chanmode (char c, char polarity) {
JonFreeman 16:d1e4b9ad3b8b 139 chan_mode = ((uint32_t) c);
JonFreeman 16:d1e4b9ad3b8b 140 stick_sense = (uint32_t) polarity;
JonFreeman 11:bfb73f083009 141 }
JonFreeman 11:bfb73f083009 142
JonFreeman 14:acaa1add097b 143 void RControl_In::RadC_fall () // December 2018 - Could not make Servo port bidirectional, fix by using PC_14 and 15 as inputs
JonFreeman 14:acaa1add097b 144 { // 30th November 2019 - Swapped _rise and _fall as now using Schmitt inverters on input
JonFreeman 11:bfb73f083009 145 period_us = t.read_us ();
JonFreeman 11:bfb73f083009 146 t.reset ();
JonFreeman 11:bfb73f083009 147 t.start ();
JonFreeman 11:bfb73f083009 148 }
JonFreeman 11:bfb73f083009 149
JonFreeman 14:acaa1add097b 150 void RControl_In::RadC_rise ()
JonFreeman 11:bfb73f083009 151 {
JonFreeman 11:bfb73f083009 152 pulse_width_us = t.read_us ();
JonFreeman 11:bfb73f083009 153 pulse_count++;
JonFreeman 11:bfb73f083009 154 }
JonFreeman 11:bfb73f083009 155 // end of RControl_In class