Code for 'Smart Regulator' featured in 'Model Engineer', November 2020 on. Contains all work to August 2020 including all code described. Top level algorithm development is quite spares, leaving some work for you! Any questions - jon@jons-workshop.com
Dependencies: mbed BufferedSerial Servo2 PCT2075 I2CEeprom FastPWM
main.cpp@1:450090bdb6f4, 2020-04-25 (annotated)
- Committer:
- JonFreeman
- Date:
- Sat Apr 25 15:35:58 2020 +0000
- Revision:
- 1:450090bdb6f4
- Parent:
- 0:77803b3ee157
- Child:
- 2:8e7b51353f32
About to pick this up again late Apr 2020.;
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
JonFreeman | 0:77803b3ee157 | 1 | #include "mbed.h" |
JonFreeman | 0:77803b3ee157 | 2 | #include "Alternator.h" |
JonFreeman | 0:77803b3ee157 | 3 | |
JonFreeman | 0:77803b3ee157 | 4 | |
JonFreeman | 0:77803b3ee157 | 5 | /* |
JonFreeman | 0:77803b3ee157 | 6 | Alternator Regulator |
JonFreeman | 0:77803b3ee157 | 7 | Jon Freeman |
JonFreeman | 1:450090bdb6f4 | 8 | June 2019 - Feb 2020 |
JonFreeman | 1:450090bdb6f4 | 9 | |
JonFreeman | 1:450090bdb6f4 | 10 | ** Prototype built using Nucleo L432KC. Final design likely to use F401RE. Code should compile for either. ** |
JonFreeman | 0:77803b3ee157 | 11 | |
JonFreeman | 1:450090bdb6f4 | 12 | ** main loop frequency upped from 32Hz to 100Hz ** |
JonFreeman | 1:450090bdb6f4 | 13 | |
JonFreeman | 1:450090bdb6f4 | 14 | WHAT THIS PROGRAMME DOES - Controls 4 stroke petrol engine driving vehicle alternator with new custom regulator |
JonFreeman | 1:450090bdb6f4 | 15 | |
JonFreeman | 1:450090bdb6f4 | 16 | Electronics powered by higher voltage of small 12v backup battery, or alternator field output supply |
JonFreeman | 1:450090bdb6f4 | 17 | Note only Field+ and MAX5035 supplied thus, all else powered from MAX outputs. |
JonFreeman | 1:450090bdb6f4 | 18 | Starting engine provides rectified tickle from magneto to enable MAX5035 creating +5 and +3v3 supplies. |
JonFreeman | 1:450090bdb6f4 | 19 | Alternative, selected by jumper pposition, is external switch - battery+ to MAX enable circuit. |
JonFreeman | 1:450090bdb6f4 | 20 | Anytime engine revs measured < 2000 (or some such) RPM, field current OFF (by pwm 0) |
JonFreeman | 0:77803b3ee157 | 21 | |
JonFreeman | 0:77803b3ee157 | 22 | BEGIN |
JonFreeman | 1:450090bdb6f4 | 23 | Loop forever at 100 Hz { |
JonFreeman | 1:450090bdb6f4 | 24 | Read engine RPM by monitoring engine tacho signal present on engine On/Off switch line |
JonFreeman | 1:450090bdb6f4 | 25 | Read alternator output load current |
JonFreeman | 1:450090bdb6f4 | 26 | Using RPM and current readings, regulate engine rpm via model control servo |
JonFreeman | 1:450090bdb6f4 | 27 | Adjust Alternator field current max limit according to RPM (analogue regulator limits output voltage) |
JonFreeman | 0:77803b3ee157 | 28 | Measure system voltage (just in case this is ever useful) |
JonFreeman | 0:77803b3ee157 | 29 | Respond to any commands arriving at serial port (setup and test link to laptop) |
JonFreeman | 0:77803b3ee157 | 30 | Flash LED at 8 Hz as proof of life |
JonFreeman | 0:77803b3ee157 | 31 | } |
JonFreeman | 0:77803b3ee157 | 32 | END |
JonFreeman | 0:77803b3ee157 | 33 | |
JonFreeman | 0:77803b3ee157 | 34 | INPUTS AnalogIn x 2 - Ammeter chip - current and offset AnalogIns |
JonFreeman | 0:77803b3ee157 | 35 | INPUT AnalogIn - System voltage for info only. |
JonFreeman | 0:77803b3ee157 | 36 | INPUT AnalogIn - ExtRevDemand |
JonFreeman | 0:77803b3ee157 | 37 | INPUT AnalogIn - DriverPot |
JonFreeman | 0:77803b3ee157 | 38 | INPUT Pulse engine speed indicator, speed checked against EEPROM data to select max pwm duty ratio for this speed |
JonFreeman | 0:77803b3ee157 | 39 | INPUT Final pwm gate drive wired back to InterruptIn ** MAYBE USEFUL OR NOT ** Could read this back via serial to laptop |
JonFreeman | 0:77803b3ee157 | 40 | OUTPUT pwm to MCP1630. This is clock to pwm chip. Also limits max duty ratio |
JonFreeman | 0:77803b3ee157 | 41 | RS232 serial via USB to setup eeprom data |
JonFreeman | 0:77803b3ee157 | 42 | */ |
JonFreeman | 1:450090bdb6f4 | 43 | // Uses software bit banged I2C - DONE (because no attempt to get I2C working on these small boards has ever worked) |
JonFreeman | 0:77803b3ee157 | 44 | |
JonFreeman | 0:77803b3ee157 | 45 | /** |
JonFreeman | 0:77803b3ee157 | 46 | * Jumpers fitted to small mbed Nucleo boards - D5 - A5 and D4 - A4 CHECK - yes |
JonFreeman | 0:77803b3ee157 | 47 | */ |
JonFreeman | 1:450090bdb6f4 | 48 | //#ifdef TARGET_NUCLEO_F303K8 // Code too large to fit |
JonFreeman | 1:450090bdb6f4 | 49 | #ifdef TARGET_NUCLEO_L432KC // |
JonFreeman | 0:77803b3ee157 | 50 | /* |
JonFreeman | 0:77803b3ee157 | 51 | declared in file i2c_bit_banged.cpp |
JonFreeman | 0:77803b3ee157 | 52 | DigitalInOut SDA (D4); // Horrible bodge to get i2c working using bit banging. |
JonFreeman | 0:77803b3ee157 | 53 | DigitalInOut SCL (D5); // DigitalInOut do not work as you might expect. Fine if used only as OpenDrain opuputs though! |
JonFreeman | 0:77803b3ee157 | 54 | DigitalIn SDA_IN (A4); // That means paralleling up with two other pins as inputs |
JonFreeman | 0:77803b3ee157 | 55 | DigitalIn SCL_IN (A5); // This works but is a pain. Inbuilt I2C should have worked but never does on small boards with 32 pin cpu. |
JonFreeman | 0:77803b3ee157 | 56 | */ |
JonFreeman | 0:77803b3ee157 | 57 | Serial pc (USBTX, USBRX); // Comms port to pc or terminal using USB lead |
JonFreeman | 0:77803b3ee157 | 58 | BufferedSerial LocalCom (PA_9, PA_10); // New March 2019 |
JonFreeman | 0:77803b3ee157 | 59 | // Above combo of Serial and BufferedSerial is the only one to work ! |
JonFreeman | 0:77803b3ee157 | 60 | |
JonFreeman | 0:77803b3ee157 | 61 | // INPUTS : |
JonFreeman | 1:450090bdb6f4 | 62 | AnalogIn Ain_SystemVolts (A6); // Sniff of alternator output, not used in control loop as done using analogue MCP1630 |
JonFreeman | 1:450090bdb6f4 | 63 | AnalogIn Ammeter_In (A1); // Output of ASC709LLFTR ammeter chip (pin 20), used to increase engine revs if need be |
JonFreeman | 1:450090bdb6f4 | 64 | AnalogIn Ammeter_Ref (A0); // Ref output from ASC709LLFTR used to set ammeter zero (pin 25) |
JonFreeman | 1:450090bdb6f4 | 65 | |
JonFreeman | 1:450090bdb6f4 | 66 | // Nov 2019. Not convinced Ext_Rev_Demand is useful |
JonFreeman | 0:77803b3ee157 | 67 | AnalogIn Ext_Rev_Demand (D3); // Servo determines engine revs, servo out to be higher of Ext_Rev_Demand and internal calc |
JonFreeman | 1:450090bdb6f4 | 68 | |
JonFreeman | 0:77803b3ee157 | 69 | AnalogIn Driver_Pot (A3); // If whole control system can be made to fit |
JonFreeman | 0:77803b3ee157 | 70 | |
JonFreeman | 0:77803b3ee157 | 71 | /* |
JonFreeman | 0:77803b3ee157 | 72 | MODULE PIN USAGE |
JonFreeman | 0:77803b3ee157 | 73 | 1 PA_9 D1 LocalCom Tx |
JonFreeman | 0:77803b3ee157 | 74 | 2 PA_10 D0 LocalCom Rx |
JonFreeman | 0:77803b3ee157 | 75 | 3 NRST |
JonFreeman | 0:77803b3ee157 | 76 | 4 GND |
JonFreeman | 1:450090bdb6f4 | 77 | 5 PA12_D2 NEW June 2019 - Output engine tacho cleaned-up, brought out to testpoint 4 |
JonFreeman | 0:77803b3ee157 | 78 | 6 PB_0 D3 AnalogIn Ext_Rev_Demand |
JonFreeman | 0:77803b3ee157 | 79 | 7 PB_7 D4 SDA i2c to 24LC memory |
JonFreeman | 0:77803b3ee157 | 80 | 8 PB_6 D5 SCL i2c to 24LC memory |
JonFreeman | 0:77803b3ee157 | 81 | 9 PB_12 D6 PwmOut PWM_OSC_IN Timebase for pwm, also determines max duty ratio |
JonFreeman | 0:77803b3ee157 | 82 | 10 N.C. |
JonFreeman | 0:77803b3ee157 | 83 | 11 N.C. |
JonFreeman | 1:450090bdb6f4 | 84 | 12 PA_8 D9 InterruptIn pulse_tacho from engine magneto, used to measure rpm |
JonFreeman | 1:450090bdb6f4 | 85 | 13 PA_11 D10 Throttle servo |
JonFreeman | 1:450090bdb6f4 | 86 | 14 PB_5 D11 // InterruptIn VEXT PWM controller output folded back for cpu to monitor, useful on test to read what pwm required to do what |
JonFreeman | 1:450090bdb6f4 | 87 | 15 PB_4 D12 Scope_probe |
JonFreeman | 0:77803b3ee157 | 88 | 16 PB_3 D13 LED Onboard LED |
JonFreeman | 0:77803b3ee157 | 89 | 17 3V3 |
JonFreeman | 0:77803b3ee157 | 90 | 18 AREF |
JonFreeman | 1:450090bdb6f4 | 91 | 19 PA_0 A0 AnalogIn Ammeter_Ref |
JonFreeman | 0:77803b3ee157 | 92 | 20 PA_1 A1 AnalogIn Ammeter_In |
JonFreeman | 0:77803b3ee157 | 93 | 21 PA_3 A2 PWM analogue out |
JonFreeman | 1:450090bdb6f4 | 94 | 22 PA_4 A3 AnalogIn Driver_Pot |
JonFreeman | 0:77803b3ee157 | 95 | 23 PA_5 A4 n.c. SDA_IN paralleled to i2c pin, necessary because i2c has to be bit banged |
JonFreeman | 0:77803b3ee157 | 96 | 24 PA_6 A5 n.c. SCL_IN paralleled to i2c pin, necessary because i2c has to be bit banged |
JonFreeman | 1:450090bdb6f4 | 97 | 25 PA_7 A6 AnalogIn V_Sample system link voltage |
JonFreeman | 1:450090bdb6f4 | 98 | 26 PA_2 A7 Not used |
JonFreeman | 0:77803b3ee157 | 99 | 27 5V |
JonFreeman | 0:77803b3ee157 | 100 | 28 NRST |
JonFreeman | 0:77803b3ee157 | 101 | 29 GND |
JonFreeman | 0:77803b3ee157 | 102 | 30 VIN |
JonFreeman | 0:77803b3ee157 | 103 | */ |
JonFreeman | 0:77803b3ee157 | 104 | |
JonFreeman | 1:450090bdb6f4 | 105 | InterruptIn pulse_tacho (D9); // Signal from engine magneto (clipped by I limit resistor and 3v3 zener) |
JonFreeman | 1:450090bdb6f4 | 106 | InterruptIn VEXT (D2); // PWM controller output folded back for cpu to monitor, useful on test to read what pwm required to do what |
JonFreeman | 0:77803b3ee157 | 107 | // OUTPUTS : |
JonFreeman | 0:77803b3ee157 | 108 | |
JonFreeman | 1:450090bdb6f4 | 109 | DigitalOut Scope_probe (D12); // Handy pin to hang scope probe onto while developing code |
JonFreeman | 1:450090bdb6f4 | 110 | DigitalOut myled (LED1); // Green LED on board is PB_3 D13 |
JonFreeman | 1:450090bdb6f4 | 111 | PwmOut PWM_OSC_IN (A2); // Can alter prescaler can not use A5 |
JonFreeman | 1:450090bdb6f4 | 112 | //PwmOut A_OUT (A2); // Can alter prescaler can not use A5 PIN STOLEN BY PWM_OSC_IN |
JonFreeman | 1:450090bdb6f4 | 113 | Servo Throttle (D10); // Changed from A2, June 2019 |
JonFreeman | 1:450090bdb6f4 | 114 | DigitalOut EngineTachoOut (D11); // New June 2019 |
JonFreeman | 0:77803b3ee157 | 115 | #endif |
JonFreeman | 1:450090bdb6f4 | 116 | |
JonFreeman | 1:450090bdb6f4 | 117 | #ifdef TARGET_NUCLEO_F401RE // |
JonFreeman | 1:450090bdb6f4 | 118 | //Serial pc (USBTX, USBRX); // Comms port to pc or terminal using USB lead |
JonFreeman | 1:450090bdb6f4 | 119 | BufferedSerial pc (PA_2, PA_3, 2048, 4, NULL); // Pins 16, 17 tx, rx to pc via usb lead |
JonFreeman | 1:450090bdb6f4 | 120 | //BufferedSerial pc (USBTX, USBRX); // Pins 16, 17 tx, rx to pc via usb lead |
JonFreeman | 1:450090bdb6f4 | 121 | BufferedSerial LocalCom (PC_6, PC_7); // Pins 37, 38 tx, rx to Touch Screen Controller |
JonFreeman | 1:450090bdb6f4 | 122 | |
JonFreeman | 1:450090bdb6f4 | 123 | // INPUTS : |
JonFreeman | 1:450090bdb6f4 | 124 | AnalogIn Ain_SystemVolts (PB_1); // Sniff of alternator output, not used in control loop as done using analogue MCP1630 |
JonFreeman | 1:450090bdb6f4 | 125 | AnalogIn Ammeter_In (PC_5); // Output of ASC709LLFTR ammeter chip (pin 20), used to increase engine revs if need be |
JonFreeman | 1:450090bdb6f4 | 126 | AnalogIn Ammeter_Ref (PB_0); // Ref output from ASC709LLFTR used to set ammeter zero (pin 25) |
JonFreeman | 1:450090bdb6f4 | 127 | AnalogIn Ext_Rev_Demand (PC_1); // Servo determines engine revs, servo out to be higher of Ext_Rev_Demand and internal calc |
JonFreeman | 1:450090bdb6f4 | 128 | AnalogIn Driver_Pot (PC_2); // If whole control system can be made to fit |
JonFreeman | 1:450090bdb6f4 | 129 | |
JonFreeman | 1:450090bdb6f4 | 130 | /* |
JonFreeman | 1:450090bdb6f4 | 131 | MODULE PIN USAGE |
JonFreeman | 1:450090bdb6f4 | 132 | */ |
JonFreeman | 1:450090bdb6f4 | 133 | |
JonFreeman | 1:450090bdb6f4 | 134 | InterruptIn pulse_tacho (PB_15); // Signal from engine magneto (clipped by I limit resistor and 3v3 zener) |
JonFreeman | 1:450090bdb6f4 | 135 | InterruptIn VEXT (PC_12); // PWM controller output folded back for cpu to monitor, useful on test to read what pwm required to do what |
JonFreeman | 1:450090bdb6f4 | 136 | // OUTPUTS : |
JonFreeman | 1:450090bdb6f4 | 137 | |
JonFreeman | 1:450090bdb6f4 | 138 | DigitalOut Scope_probe (PB_3); // Handy pin to hang scope probe onto while developing code |
JonFreeman | 1:450090bdb6f4 | 139 | DigitalOut myled (PA_5); // Green LED on board is PA_5 |
JonFreeman | 1:450090bdb6f4 | 140 | //PwmOut PWM_OSC_IN (PA_10); // PA_10 is pwm1/3 Can alter prescaler can not use A5 |
JonFreeman | 1:450090bdb6f4 | 141 | PwmOut PWM_OSC_IN (PB_9); // PA_10 is pwm4/4 Can alter prescaler can not use A5 |
JonFreeman | 1:450090bdb6f4 | 142 | PwmOut A_OUT (PB_5); // PB_5 is pwm3/2 Can alter prescaler can not use A5 PIN STOLEN BY PWM_OSC_IN |
JonFreeman | 1:450090bdb6f4 | 143 | Servo Throttle (PA_0); // PA_8 is pwm1/1 Changed from A2, June 2019 |
JonFreeman | 1:450090bdb6f4 | 144 | DigitalOut EngineTachoOut (PA_7); // New June 2019 |
JonFreeman | 1:450090bdb6f4 | 145 | |
JonFreeman | 1:450090bdb6f4 | 146 | I2C i2c (PB_7, PB_6); // Pins 58, 59 For 24LC64 eeprom |
JonFreeman | 1:450090bdb6f4 | 147 | //#define SDA_PIN PB_7 |
JonFreeman | 1:450090bdb6f4 | 148 | //#define SCL_PIN PB_6 |
JonFreeman | 1:450090bdb6f4 | 149 | |
JonFreeman | 1:450090bdb6f4 | 150 | #endif |
JonFreeman | 1:450090bdb6f4 | 151 | |
JonFreeman | 0:77803b3ee157 | 152 | Timer microsecs; |
JonFreeman | 0:77803b3ee157 | 153 | Ticker loop_timer; // Device to cause periodic interrupts, used to sync iterations of main programme loop - slow |
JonFreeman | 0:77803b3ee157 | 154 | |
JonFreeman | 1:450090bdb6f4 | 155 | const double AMPS_CAL = 90.0; |
JonFreeman | 1:450090bdb6f4 | 156 | extern eeprom_settings user_settings ; |
JonFreeman | 0:77803b3ee157 | 157 | // SYSTEM CONSTANTS |
JonFreeman | 0:77803b3ee157 | 158 | /* Please Do Not Alter these */ |
JonFreeman | 1:450090bdb6f4 | 159 | const int MAIN_LOOP_REPEAT_TIME_US = 10000; // 10000 us, with TACHO_TAB_SIZE = 100 means tacho_ticks_per_time is tacho_ticks_per_second |
JonFreeman | 0:77803b3ee157 | 160 | /* End of Please Do Not Alter these */ |
JonFreeman | 0:77803b3ee157 | 161 | /* Global variable declarations */ |
JonFreeman | 0:77803b3ee157 | 162 | uint32_t //semaphore = 0, |
JonFreeman | 1:450090bdb6f4 | 163 | speed_control_factor= 75000, // fiddled from cli to tweak engine speed controller response |
JonFreeman | 0:77803b3ee157 | 164 | volt_reading = 0, // Global updated by interrupt driven read of Battery Volts |
JonFreeman | 0:77803b3ee157 | 165 | ext_rev_req = 0, |
JonFreeman | 0:77803b3ee157 | 166 | driver_reading = 0, |
JonFreeman | 0:77803b3ee157 | 167 | tacho_count = 0, // Global incremented on each transition of InterruptIn pulse_tacho |
JonFreeman | 1:450090bdb6f4 | 168 | sys_timer100Hz = 0; // gets incremented by our Ticker ISR every MAIN_LOOP_REPEAT_TIME_US |
JonFreeman | 1:450090bdb6f4 | 169 | double amp_reading = 0.0, |
JonFreeman | 1:450090bdb6f4 | 170 | amp_offset = 0.0, |
JonFreeman | 1:450090bdb6f4 | 171 | raw_amp_reading = 0.0, |
JonFreeman | 1:450090bdb6f4 | 172 | raw_amp_offset = 0.0; |
JonFreeman | 1:450090bdb6f4 | 173 | double servo_position = 0.2; // set in speed control loop |
JonFreeman | 1:450090bdb6f4 | 174 | // const double throttle_limit = 0.4; |
JonFreeman | 1:450090bdb6f4 | 175 | double throttle_limit = SERVO_MAX; |
JonFreeman | 0:77803b3ee157 | 176 | bool loop_flag = false; // made true in ISR_loop_timer, picked up and made false again in main programme loop |
JonFreeman | 1:450090bdb6f4 | 177 | bool flag_25Hz = false; // As loop_flag but repeats 25 times per sec |
JonFreeman | 1:450090bdb6f4 | 178 | bool flag_12Hz5 = false; // As loop_flag but repeats 12.5 times per sec |
JonFreeman | 1:450090bdb6f4 | 179 | bool flag_1Hz = false; // As loop_flag but repeats 1 times per sec |
JonFreeman | 1:450090bdb6f4 | 180 | bool query_toggle = false; |
JonFreeman | 0:77803b3ee157 | 181 | |
JonFreeman | 1:450090bdb6f4 | 182 | const int AMP_FILTER_FACTOR = 6; |
JonFreeman | 0:77803b3ee157 | 183 | |
JonFreeman | 0:77803b3ee157 | 184 | /* End of Global variable declarations */ |
JonFreeman | 0:77803b3ee157 | 185 | |
JonFreeman | 1:450090bdb6f4 | 186 | //void ISR_fast_interrupt () { // here at 10 times main loop repeat rate (i.e. 1000Hz, 1.0ms) |
JonFreeman | 1:450090bdb6f4 | 187 | void ISR_fast_interrupt () { |
JonFreeman | 1:450090bdb6f4 | 188 | static uint32_t t = 0; |
JonFreeman | 1:450090bdb6f4 | 189 | Scope_probe = 1; // To show how much time spent in interrupt handler |
JonFreeman | 0:77803b3ee157 | 190 | switch (t) { |
JonFreeman | 0:77803b3ee157 | 191 | case 0: |
JonFreeman | 0:77803b3ee157 | 192 | volt_reading >>= 1; // Result = Result / 2 |
JonFreeman | 0:77803b3ee157 | 193 | volt_reading += Ain_SystemVolts.read_u16 (); // Result = Result + New Reading |
JonFreeman | 0:77803b3ee157 | 194 | break; |
JonFreeman | 0:77803b3ee157 | 195 | case 1: |
JonFreeman | 1:450090bdb6f4 | 196 | raw_amp_reading = (double) Ammeter_In.read(); |
JonFreeman | 0:77803b3ee157 | 197 | break; |
JonFreeman | 0:77803b3ee157 | 198 | case 2: |
JonFreeman | 1:450090bdb6f4 | 199 | raw_amp_offset = Ammeter_Ref.read(); // Feb 2020 Not convinced this is useful |
JonFreeman | 0:77803b3ee157 | 200 | break; |
JonFreeman | 0:77803b3ee157 | 201 | case 3: |
JonFreeman | 0:77803b3ee157 | 202 | ext_rev_req >>= 1; // Result = Result / 2 |
JonFreeman | 1:450090bdb6f4 | 203 | ext_rev_req += Ext_Rev_Demand.read_u16(); |
JonFreeman | 0:77803b3ee157 | 204 | break; |
JonFreeman | 0:77803b3ee157 | 205 | case 4: |
JonFreeman | 0:77803b3ee157 | 206 | driver_reading >>= 1; // Result = Result / 2 |
JonFreeman | 1:450090bdb6f4 | 207 | driver_reading += Driver_Pot.read_u16(); |
JonFreeman | 1:450090bdb6f4 | 208 | // break; |
JonFreeman | 1:450090bdb6f4 | 209 | // case 5: |
JonFreeman | 0:77803b3ee157 | 210 | loop_flag = true; // set flag to allow main programme loop to proceed |
JonFreeman | 1:450090bdb6f4 | 211 | sys_timer100Hz++; // Just a handy measure of elapsed time for anything to use |
JonFreeman | 1:450090bdb6f4 | 212 | if ((sys_timer100Hz & 0x03) == 0) // is now 12.5Hz, not 8 |
JonFreeman | 1:450090bdb6f4 | 213 | flag_25Hz = true; // flag gets set 25 times per sec. Other code may clear flag and make use of this |
JonFreeman | 1:450090bdb6f4 | 214 | default: |
JonFreeman | 0:77803b3ee157 | 215 | break; |
JonFreeman | 0:77803b3ee157 | 216 | } |
JonFreeman | 0:77803b3ee157 | 217 | t++; |
JonFreeman | 0:77803b3ee157 | 218 | if (t > 9) |
JonFreeman | 0:77803b3ee157 | 219 | t = 0; |
JonFreeman | 1:450090bdb6f4 | 220 | Scope_probe = 0; // To show how much time spent in interrupt handler |
JonFreeman | 0:77803b3ee157 | 221 | } |
JonFreeman | 0:77803b3ee157 | 222 | |
JonFreeman | 0:77803b3ee157 | 223 | |
JonFreeman | 0:77803b3ee157 | 224 | |
JonFreeman | 1:450090bdb6f4 | 225 | // New stuff November 2019 |
JonFreeman | 1:450090bdb6f4 | 226 | /** |
JonFreeman | 1:450090bdb6f4 | 227 | * Obtaining Amps_Deliverable from RPM. |
JonFreeman | 1:450090bdb6f4 | 228 | * Lucas workshop sheet shows exponential relationship between RPM over threshold, and Amps_Deliverable, |
JonFreeman | 1:450090bdb6f4 | 229 | * That is Amps_Deliverable rises steeply with RPM, flattening off towards 6000 RPM |
JonFreeman | 1:450090bdb6f4 | 230 | * Curve modeled by eqn |
JonFreeman | 1:450090bdb6f4 | 231 | * I_del = I_max (1 - exp(-(RPM-3000)/Const1)) where Const1 = 1000 is a starting point |
JonFreeman | 1:450090bdb6f4 | 232 | * This is probably fine when driving alternator with BIG engine. |
JonFreeman | 1:450090bdb6f4 | 233 | * When using small engine, rising load current sags engine RPM. |
JonFreeman | 1:450090bdb6f4 | 234 | * Using a linear relationship builds in a good safety margin, possible eqn |
JonFreeman | 1:450090bdb6f4 | 235 | * I_del = I_max (RPM - 3000) / 3000 for use over RPM range 3000-6000 |
JonFreeman | 0:77803b3ee157 | 236 | */ |
JonFreeman | 1:450090bdb6f4 | 237 | const double RPM_Threshold = 3000.0; |
JonFreeman | 1:450090bdb6f4 | 238 | const double RPM_Max = 6000.0; |
JonFreeman | 1:450090bdb6f4 | 239 | //const double I_max = 30.0; |
JonFreeman | 1:450090bdb6f4 | 240 | const double RPM_Range = RPM_Max - RPM_Threshold; |
JonFreeman | 1:450090bdb6f4 | 241 | //#define BIG_ENGINE |
JonFreeman | 1:450090bdb6f4 | 242 | |
JonFreeman | 1:450090bdb6f4 | 243 | double Calculate_Amps_Deliverable (uint32_t RPM) { |
JonFreeman | 1:450090bdb6f4 | 244 | double r = (double)RPM - RPM_Threshold; |
JonFreeman | 1:450090bdb6f4 | 245 | r /= RPM_Range; |
JonFreeman | 1:450090bdb6f4 | 246 | if (r < 0.0) |
JonFreeman | 1:450090bdb6f4 | 247 | r = 0.0; |
JonFreeman | 1:450090bdb6f4 | 248 | if (r > 1.0) |
JonFreeman | 1:450090bdb6f4 | 249 | r = 1.0; |
JonFreeman | 1:450090bdb6f4 | 250 | #ifdef BIG_ENGINE |
JonFreeman | 1:450090bdb6f4 | 251 | const double Const1 = -3.2; // Tweak this to adjust shape of exponential function |
JonFreeman | 1:450090bdb6f4 | 252 | return (1.0 - exp(r*Const1)); |
JonFreeman | 0:77803b3ee157 | 253 | #else |
JonFreeman | 1:450090bdb6f4 | 254 | return r; |
JonFreeman | 0:77803b3ee157 | 255 | #endif |
JonFreeman | 1:450090bdb6f4 | 256 | } |
JonFreeman | 1:450090bdb6f4 | 257 | |
JonFreeman | 1:450090bdb6f4 | 258 | class one_over_s_integrator { // Need this to drive servo Jan 2020 why? |
JonFreeman | 1:450090bdb6f4 | 259 | double internal_integral, max, min, Hz, gain; |
JonFreeman | 1:450090bdb6f4 | 260 | public: |
JonFreeman | 1:450090bdb6f4 | 261 | one_over_s_integrator () { internal_integral = 0.0; max = 1.0; min = -1.0; Hz = 100.0; gain = 1.0;} |
JonFreeman | 1:450090bdb6f4 | 262 | double integral (double input) ; |
JonFreeman | 1:450090bdb6f4 | 263 | void set_max (double) ; |
JonFreeman | 1:450090bdb6f4 | 264 | void set_min (double) ; |
JonFreeman | 1:450090bdb6f4 | 265 | void set_gain (double) ; |
JonFreeman | 1:450090bdb6f4 | 266 | void set_sample_time (double) ; |
JonFreeman | 1:450090bdb6f4 | 267 | } ; |
JonFreeman | 1:450090bdb6f4 | 268 | |
JonFreeman | 1:450090bdb6f4 | 269 | void one_over_s_integrator::set_max (double in) { |
JonFreeman | 1:450090bdb6f4 | 270 | max = in; |
JonFreeman | 0:77803b3ee157 | 271 | } |
JonFreeman | 1:450090bdb6f4 | 272 | void one_over_s_integrator::set_min (double in) { |
JonFreeman | 1:450090bdb6f4 | 273 | min = in; |
JonFreeman | 1:450090bdb6f4 | 274 | } |
JonFreeman | 1:450090bdb6f4 | 275 | void one_over_s_integrator::set_gain (double in) { |
JonFreeman | 1:450090bdb6f4 | 276 | gain = in; |
JonFreeman | 1:450090bdb6f4 | 277 | } |
JonFreeman | 1:450090bdb6f4 | 278 | void one_over_s_integrator::set_sample_time (double in) { |
JonFreeman | 1:450090bdb6f4 | 279 | Hz = 1.0 / in; |
JonFreeman | 1:450090bdb6f4 | 280 | } |
JonFreeman | 1:450090bdb6f4 | 281 | double one_over_s_integrator::integral (double input) { |
JonFreeman | 1:450090bdb6f4 | 282 | internal_integral += gain * input / Hz; // 100 for 100Hz update rate |
JonFreeman | 1:450090bdb6f4 | 283 | if (internal_integral > max) |
JonFreeman | 1:450090bdb6f4 | 284 | internal_integral = max; |
JonFreeman | 1:450090bdb6f4 | 285 | if (internal_integral < min) |
JonFreeman | 1:450090bdb6f4 | 286 | internal_integral = min; |
JonFreeman | 1:450090bdb6f4 | 287 | return internal_integral; |
JonFreeman | 1:450090bdb6f4 | 288 | } |
JonFreeman | 1:450090bdb6f4 | 289 | |
JonFreeman | 1:450090bdb6f4 | 290 | //double one_over_s () |
JonFreeman | 1:450090bdb6f4 | 291 | // End of New stuff November 2019 |
JonFreeman | 0:77803b3ee157 | 292 | |
JonFreeman | 0:77803b3ee157 | 293 | |
JonFreeman | 0:77803b3ee157 | 294 | // New stuff June 2019 |
JonFreeman | 0:77803b3ee157 | 295 | bool magneto_stretch = false; |
JonFreeman | 0:77803b3ee157 | 296 | Timeout magneto_timo; |
JonFreeman | 1:450090bdb6f4 | 297 | uint32_t magneto_times[8] = {0,0,0,0,0,0,0,0}; // June 2019, only 2 of these used |
JonFreeman | 1:450090bdb6f4 | 298 | |
JonFreeman | 0:77803b3ee157 | 299 | |
JonFreeman | 1:450090bdb6f4 | 300 | /** |
JonFreeman | 1:450090bdb6f4 | 301 | void magneto_timeout () |
JonFreeman | 1:450090bdb6f4 | 302 | Here 5ms after magneto pulse detected |
JonFreeman | 1:450090bdb6f4 | 303 | This is sufficient time for ringing to cease, not long enough to lose next pulse even at max engine revs. |
JonFreeman | 1:450090bdb6f4 | 304 | Reset 'magneto_stretch' flag set and used in 'ISR_magneto_tacho' |
JonFreeman | 1:450090bdb6f4 | 305 | */ |
JonFreeman | 0:77803b3ee157 | 306 | void magneto_timeout () |
JonFreeman | 0:77803b3ee157 | 307 | { |
JonFreeman | 0:77803b3ee157 | 308 | magneto_stretch = false; // Magneto ringing finished by now, re-enable magneto pulse count |
JonFreeman | 1:450090bdb6f4 | 309 | EngineTachoOut = 0; // Cleaned tacho output brought out to pin to look at with scope |
JonFreeman | 0:77803b3ee157 | 310 | } |
JonFreeman | 0:77803b3ee157 | 311 | |
JonFreeman | 1:450090bdb6f4 | 312 | /** |
JonFreeman | 1:450090bdb6f4 | 313 | void ISR_magneto_tacho () ; // New June 2019 |
JonFreeman | 1:450090bdb6f4 | 314 | // Engine On/Off switch turns engine off by shorting ignition volts magneto to ground. |
JonFreeman | 1:450090bdb6f4 | 315 | // Therefore when engine running, have pulse signal one pulse per rev (even though 4 stroke, spark delivered at 2 stroke rate) |
JonFreeman | 1:450090bdb6f4 | 316 | // Pulse spacing 20ms @ 3000 RPM, 60ms @ 1000 RPM, 6ms @ 10000 RPM |
JonFreeman | 1:450090bdb6f4 | 317 | |
JonFreeman | 1:450090bdb6f4 | 318 | Magneto signal rings, is quite unclean, therefore a cleanup strategy is needed. |
JonFreeman | 1:450090bdb6f4 | 319 | Solution - On arrival at this interrupt handler, |
JonFreeman | 1:450090bdb6f4 | 320 | If flag 'magneto_stretch' true, do nothing and return (to avoid multiple pulse count) |
JonFreeman | 1:450090bdb6f4 | 321 | Set flag 'magneto_stretch' true; |
JonFreeman | 1:450090bdb6f4 | 322 | Start timer 'magneto_timo' to cause 'magneto_timeout' interrupt in a time longer than ringing bt shorter than shortest time to next spark |
JonFreeman | 1:450090bdb6f4 | 323 | Record time between most recent two sparks and set output bit for scope monitoring |
JonFreeman | 1:450090bdb6f4 | 324 | */ |
JonFreeman | 1:450090bdb6f4 | 325 | void ISR_magneto_tacho () // This interrupt initiated by rising (or falling) edge of magneto output, (not both) |
JonFreeman | 0:77803b3ee157 | 326 | { |
JonFreeman | 0:77803b3ee157 | 327 | if (!magneto_stretch) |
JonFreeman | 0:77803b3ee157 | 328 | { |
JonFreeman | 0:77803b3ee157 | 329 | uint32_t new_time = microsecs.read_us(); |
JonFreeman | 0:77803b3ee157 | 330 | if (new_time < magneto_times[1]) // rollover detection |
JonFreeman | 0:77803b3ee157 | 331 | magneto_times[1] = 0; |
JonFreeman | 0:77803b3ee157 | 332 | magneto_times[0] = new_time - magneto_times[1]; // microsecs between most recent two sparks |
JonFreeman | 0:77803b3ee157 | 333 | magneto_times[1] = new_time; // actual time microsecs of most recent spark |
JonFreeman | 0:77803b3ee157 | 334 | magneto_stretch = true; |
JonFreeman | 0:77803b3ee157 | 335 | magneto_timo.attach_us (&magneto_timeout, 5000); // To ignore ringing and multiple counts on magneto output, all settled within about 5ms |
JonFreeman | 0:77803b3ee157 | 336 | tacho_count++; |
JonFreeman | 1:450090bdb6f4 | 337 | EngineTachoOut = 1; // Cleaned tacho output brought out to pin to look at with scope |
JonFreeman | 0:77803b3ee157 | 338 | } |
JonFreeman | 0:77803b3ee157 | 339 | } |
JonFreeman | 0:77803b3ee157 | 340 | |
JonFreeman | 0:77803b3ee157 | 341 | // Endof New stuff June 2019 |
JonFreeman | 0:77803b3ee157 | 342 | |
JonFreeman | 1:450090bdb6f4 | 343 | const double shrink = 0.2; |
JonFreeman | 1:450090bdb6f4 | 344 | /*double lpf_4th_order_asym (double input) { |
JonFreeman | 0:77803b3ee157 | 345 | * |
JonFreeman | 1:450090bdb6f4 | 346 | * input is driver's control pot. |
JonFreeman | 1:450090bdb6f4 | 347 | * This needs regular calling, maybe 8Hz - 32Hz |
JonFreeman | 1:450090bdb6f4 | 348 | * |
JonFreeman | 1:450090bdb6f4 | 349 | * Output from 4th stage of cascaded Butterworth lpf section |
JonFreeman | 1:450090bdb6f4 | 350 | * Used to delay rising input to motor controller allowing time for engine revs to rise |
JonFreeman | 0:77803b3ee157 | 351 | */ |
JonFreeman | 1:450090bdb6f4 | 352 | double lpf_4th_order_asym (double input) { |
JonFreeman | 1:450090bdb6f4 | 353 | static double lpfs[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; |
JonFreeman | 1:450090bdb6f4 | 354 | if (input < 0.0) input = 0.0; |
JonFreeman | 1:450090bdb6f4 | 355 | if (input > 1.0) input = 1.0; |
JonFreeman | 1:450090bdb6f4 | 356 | lpfs[0] = input; // zeroth order filter |
JonFreeman | 1:450090bdb6f4 | 357 | double tmp; |
JonFreeman | 1:450090bdb6f4 | 358 | for (int order = 1; order < 5; order++) { |
JonFreeman | 1:450090bdb6f4 | 359 | tmp = (lpfs[order] * (1.0 - shrink)) |
JonFreeman | 1:450090bdb6f4 | 360 | + (lpfs[order - 1] * shrink); |
JonFreeman | 1:450090bdb6f4 | 361 | if (tmp > input) |
JonFreeman | 1:450090bdb6f4 | 362 | tmp = input; |
JonFreeman | 1:450090bdb6f4 | 363 | lpfs[order] = tmp; |
JonFreeman | 1:450090bdb6f4 | 364 | } |
JonFreeman | 1:450090bdb6f4 | 365 | return tmp; |
JonFreeman | 0:77803b3ee157 | 366 | } |
JonFreeman | 0:77803b3ee157 | 367 | |
JonFreeman | 1:450090bdb6f4 | 368 | |
JonFreeman | 1:450090bdb6f4 | 369 | VEXT_Data Field; |
JonFreeman | 1:450090bdb6f4 | 370 | |
JonFreeman | 1:450090bdb6f4 | 371 | |
JonFreeman | 0:77803b3ee157 | 372 | void ISR_VEXT_rise () // InterruptIn interrupt service |
JonFreeman | 1:450090bdb6f4 | 373 | { // Here is possible to read back how regulator has controlled pwm - may or may not be useful |
JonFreeman | 1:450090bdb6f4 | 374 | uint32_t tmp = microsecs.read_us(); |
JonFreeman | 1:450090bdb6f4 | 375 | Field.measured_period = tmp - Field.t_on; |
JonFreeman | 1:450090bdb6f4 | 376 | Field.t_on = tmp; |
JonFreeman | 1:450090bdb6f4 | 377 | Field.rise_count++; |
JonFreeman | 0:77803b3ee157 | 378 | } |
JonFreeman | 0:77803b3ee157 | 379 | void ISR_VEXT_fall () // InterruptIn interrupt service |
JonFreeman | 0:77803b3ee157 | 380 | { |
JonFreeman | 1:450090bdb6f4 | 381 | Field.fall_count++; |
JonFreeman | 1:450090bdb6f4 | 382 | Field.t_off = microsecs.read_us(); |
JonFreeman | 1:450090bdb6f4 | 383 | Field.measured_pw_us = Field.t_off - Field.t_on; |
JonFreeman | 0:77803b3ee157 | 384 | } |
JonFreeman | 0:77803b3ee157 | 385 | // **** End of Interrupt Service Routines **** |
JonFreeman | 0:77803b3ee157 | 386 | |
JonFreeman | 0:77803b3ee157 | 387 | |
JonFreeman | 0:77803b3ee157 | 388 | /** uint32_t ReadEngineRPM () |
JonFreeman | 1:450090bdb6f4 | 389 | * |
JonFreeman | 1:450090bdb6f4 | 390 | * June 2019 - Replaced count of alternator frequency by count of engine magneto pulses. |
JonFreeman | 1:450090bdb6f4 | 391 | * |
JonFreeman | 0:77803b3ee157 | 392 | */ |
JonFreeman | 0:77803b3ee157 | 393 | uint32_t ReadEngineRPM () |
JonFreeman | 0:77803b3ee157 | 394 | { |
JonFreeman | 0:77803b3ee157 | 395 | uint32_t time_since_last_spark = microsecs.read_us() - magneto_times[1]; |
JonFreeman | 1:450090bdb6f4 | 396 | if (time_since_last_spark > 250000) // if engine probably stopped, return old method RPM |
JonFreeman | 1:450090bdb6f4 | 397 | return 0; |
JonFreeman | 1:450090bdb6f4 | 398 | return (60000000 / magneto_times[0]); // 60 million / microsecs between two most recent sparks, eg 10,000us between sparks @ 6000 RPM |
JonFreeman | 0:77803b3ee157 | 399 | } |
JonFreeman | 0:77803b3ee157 | 400 | |
JonFreeman | 1:450090bdb6f4 | 401 | /*double Read_Ext_Rev_Req () |
JonFreeman | 1:450090bdb6f4 | 402 | { |
JonFreeman | 1:450090bdb6f4 | 403 | double rv = (double) ext_rev_req; |
JonFreeman | 1:450090bdb6f4 | 404 | return rv / 4096.0; |
JonFreeman | 1:450090bdb6f4 | 405 | }*/ |
JonFreeman | 1:450090bdb6f4 | 406 | |
JonFreeman | 1:450090bdb6f4 | 407 | double Read_Driver_Pot () |
JonFreeman | 1:450090bdb6f4 | 408 | { |
JonFreeman | 1:450090bdb6f4 | 409 | double rv = (double) driver_reading; |
JonFreeman | 1:450090bdb6f4 | 410 | return rv / 4096.0; |
JonFreeman | 1:450090bdb6f4 | 411 | } |
JonFreeman | 1:450090bdb6f4 | 412 | |
JonFreeman | 1:450090bdb6f4 | 413 | double Read_AlternatorAmps () |
JonFreeman | 1:450090bdb6f4 | 414 | { |
JonFreeman | 1:450090bdb6f4 | 415 | int32_t diff = amp_reading - amp_offset; |
JonFreeman | 1:450090bdb6f4 | 416 | double amps = ((double) diff) / (1 << AMP_FILTER_FACTOR); |
JonFreeman | 1:450090bdb6f4 | 417 | amps -= 365.0; // offset probably specific to particular board. |
JonFreeman | 1:450090bdb6f4 | 418 | amps /= 1433.0; // fiddle factor |
JonFreeman | 1:450090bdb6f4 | 419 | return amps; |
JonFreeman | 1:450090bdb6f4 | 420 | } |
JonFreeman | 0:77803b3ee157 | 421 | |
JonFreeman | 0:77803b3ee157 | 422 | double Read_BatteryVolts () |
JonFreeman | 0:77803b3ee157 | 423 | { |
JonFreeman | 1:450090bdb6f4 | 424 | return ((double) volt_reading) / 3282.5; // divisor fiddled to make voltage reading correct ! |
JonFreeman | 0:77803b3ee157 | 425 | } |
JonFreeman | 0:77803b3ee157 | 426 | |
JonFreeman | 1:450090bdb6f4 | 427 | void Read_Ammeter (double * p) |
JonFreeman | 1:450090bdb6f4 | 428 | { |
JonFreeman | 1:450090bdb6f4 | 429 | p[0] = amp_reading; |
JonFreeman | 1:450090bdb6f4 | 430 | p[1] = amp_offset; |
JonFreeman | 1:450090bdb6f4 | 431 | } |
JonFreeman | 1:450090bdb6f4 | 432 | |
JonFreeman | 1:450090bdb6f4 | 433 | /** |
JonFreeman | 1:450090bdb6f4 | 434 | void set_servo (double p) { // Only for test, called from cli |
JonFreeman | 1:450090bdb6f4 | 435 | */ |
JonFreeman | 0:77803b3ee157 | 436 | void set_servo (double p) { // Only for test, called from cli |
JonFreeman | 0:77803b3ee157 | 437 | Throttle = p; |
JonFreeman | 0:77803b3ee157 | 438 | } |
JonFreeman | 0:77803b3ee157 | 439 | |
JonFreeman | 0:77803b3ee157 | 440 | double normalise (double * p) { |
JonFreeman | 0:77803b3ee157 | 441 | if (*p > 0.999) |
JonFreeman | 0:77803b3ee157 | 442 | *p = 0.999; |
JonFreeman | 0:77803b3ee157 | 443 | if (*p < 0.001) |
JonFreeman | 0:77803b3ee157 | 444 | *p = 0.001; |
JonFreeman | 0:77803b3ee157 | 445 | return * p; |
JonFreeman | 0:77803b3ee157 | 446 | } |
JonFreeman | 0:77803b3ee157 | 447 | |
JonFreeman | 1:450090bdb6f4 | 448 | uint32_t RPM_demand = 0; // For test, set from cli 'sv' |
JonFreeman | 1:450090bdb6f4 | 449 | /** |
JonFreeman | 1:450090bdb6f4 | 450 | */ |
JonFreeman | 0:77803b3ee157 | 451 | void set_RPM_demand (uint32_t d) { |
JonFreeman | 0:77803b3ee157 | 452 | if (d < 10) |
JonFreeman | 0:77803b3ee157 | 453 | d = 10; |
JonFreeman | 1:450090bdb6f4 | 454 | if (d > MAX_RPM_LIMIT) |
JonFreeman | 1:450090bdb6f4 | 455 | d = MAX_RPM_LIMIT; |
JonFreeman | 0:77803b3ee157 | 456 | RPM_demand = d; |
JonFreeman | 0:77803b3ee157 | 457 | } |
JonFreeman | 0:77803b3ee157 | 458 | |
JonFreeman | 1:450090bdb6f4 | 459 | /**void set_pwm (double d) { Range 0.0 to 1.0 |
JonFreeman | 1:450090bdb6f4 | 460 | This PWM used to limit max duty ratio of alternator field energisation. |
JonFreeman | 1:450090bdb6f4 | 461 | With R25=33k and C4=100n controlling ramp input to CS pin of MCP1630 (not MCP1630V), |
JonFreeman | 1:450090bdb6f4 | 462 | ramp terminates fet 'on' pulse after a max of approx 980 us. |
JonFreeman | 1:450090bdb6f4 | 463 | With const int PWM_PERIOD_US = 2000 , duty ratio is thus limited to approx 50% max. |
JonFreeman | 1:450090bdb6f4 | 464 | This is about right when using 12V alternator on 24V systems |
JonFreeman | 1:450090bdb6f4 | 465 | A 1.225V reference (U7) is fed to the MCP1630 error amp which compares this to fed-back proportion of system voltage. |
JonFreeman | 1:450090bdb6f4 | 466 | This adjusts final PWM down to zero % as needed to maintain alternator output voltage. |
JonFreeman | 1:450090bdb6f4 | 467 | */ |
JonFreeman | 1:450090bdb6f4 | 468 | void set_pwm (double d) { |
JonFreeman | 1:450090bdb6f4 | 469 | uint32_t i; |
JonFreeman | 1:450090bdb6f4 | 470 | if (d < 0.0) |
JonFreeman | 1:450090bdb6f4 | 471 | d = 0.0; |
JonFreeman | 1:450090bdb6f4 | 472 | if (d > 1.0) |
JonFreeman | 1:450090bdb6f4 | 473 | d = 1.0; |
JonFreeman | 1:450090bdb6f4 | 474 | i = (uint32_t)(d * (PWM_PERIOD_US / 2)); // div 2 when using 12v alternator in 24v system |
JonFreeman | 1:450090bdb6f4 | 475 | // pc.printf ("Setting PWM to %d\r\n", i); |
JonFreeman | 1:450090bdb6f4 | 476 | PWM_OSC_IN.pulsewidth_us (PWM_PERIOD_US - i); // Note PWM is inverted as MCP1630 uses inverted OSC_IN signal |
JonFreeman | 1:450090bdb6f4 | 477 | } |
JonFreeman | 1:450090bdb6f4 | 478 | |
JonFreeman | 1:450090bdb6f4 | 479 | void speed_control_factor_set (struct parameters & a) { |
JonFreeman | 1:450090bdb6f4 | 480 | uint32_t v = (uint32_t)a.dbl[0]; |
JonFreeman | 1:450090bdb6f4 | 481 | if (v > 10) |
JonFreeman | 1:450090bdb6f4 | 482 | speed_control_factor = v; |
JonFreeman | 1:450090bdb6f4 | 483 | pc.printf ("speed_control_factor %d\r\n", speed_control_factor); |
JonFreeman | 1:450090bdb6f4 | 484 | } |
JonFreeman | 1:450090bdb6f4 | 485 | |
JonFreeman | 1:450090bdb6f4 | 486 | void set_throttle_limit (struct parameters & a) { |
JonFreeman | 1:450090bdb6f4 | 487 | if (a.dbl[0] > 0.01 && a.dbl[0] < 1.001) |
JonFreeman | 1:450090bdb6f4 | 488 | throttle_limit = a.dbl[0]; |
JonFreeman | 1:450090bdb6f4 | 489 | pc.printf ("throttle_limit %.2f\r\n", throttle_limit); |
JonFreeman | 1:450090bdb6f4 | 490 | } |
JonFreeman | 1:450090bdb6f4 | 491 | |
JonFreeman | 1:450090bdb6f4 | 492 | void query_system (struct parameters & a) { |
JonFreeman | 1:450090bdb6f4 | 493 | query_toggle = !query_toggle; |
JonFreeman | 1:450090bdb6f4 | 494 | // pc.printf ("Stuff about current state of system\r\n"); |
JonFreeman | 1:450090bdb6f4 | 495 | // pc.printf ("RPM=%d, servo%.2f\r\n", ReadEngineRPM (), servo_position); |
JonFreeman | 1:450090bdb6f4 | 496 | } |
JonFreeman | 1:450090bdb6f4 | 497 | |
JonFreeman | 0:77803b3ee157 | 498 | extern void command_line_interpreter () ; // Comms with optional pc or device using serial port through board USB socket |
JonFreeman | 0:77803b3ee157 | 499 | extern bool i2c_init () ; |
JonFreeman | 0:77803b3ee157 | 500 | extern int check_24LC64 () ; |
JonFreeman | 0:77803b3ee157 | 501 | |
JonFreeman | 0:77803b3ee157 | 502 | // Programme Entry Point |
JonFreeman | 0:77803b3ee157 | 503 | int main() |
JonFreeman | 0:77803b3ee157 | 504 | { |
JonFreeman | 0:77803b3ee157 | 505 | // local variable declarations |
JonFreeman | 1:450090bdb6f4 | 506 | // double servo_position = 0.2; // set in speed control loop |
JonFreeman | 0:77803b3ee157 | 507 | double revs_error; |
JonFreeman | 1:450090bdb6f4 | 508 | double Amps_Deliverable = 0.0; // New Nov 2019 |
JonFreeman | 1:450090bdb6f4 | 509 | // double tempfilt = 0.0, servo_fucker = 0.01; |
JonFreeman | 0:77803b3ee157 | 510 | |
JonFreeman | 1:450090bdb6f4 | 511 | int32_t RPM_ave = 0, RPM_filt = 0, RPM_tmp; |
JonFreeman | 1:450090bdb6f4 | 512 | int32_t irevs_error; |
JonFreeman | 1:450090bdb6f4 | 513 | uint32_t ticks = 0; |
JonFreeman | 1:450090bdb6f4 | 514 | |
JonFreeman | 0:77803b3ee157 | 515 | pulse_tacho.fall (&ISR_magneto_tacho); // 1 pulse per engine rev |
JonFreeman | 0:77803b3ee157 | 516 | VEXT.rise (&ISR_VEXT_rise); // Handles - MCP1630 has just turned mosfet on |
JonFreeman | 0:77803b3ee157 | 517 | VEXT.fall (&ISR_VEXT_fall); // Handles - MCP1630 has just turned mosfet off |
JonFreeman | 0:77803b3ee157 | 518 | microsecs.reset() ; // timer = 0 |
JonFreeman | 0:77803b3ee157 | 519 | microsecs.start () ; // 64 bit, counts micro seconds and times out in half million years |
JonFreeman | 1:450090bdb6f4 | 520 | |
JonFreeman | 1:450090bdb6f4 | 521 | PWM_OSC_IN.period_us (PWM_PERIOD_US); // about 313Hz * 2 |
JonFreeman | 1:450090bdb6f4 | 522 | // PROBLEM using same pwm, common prescaler, can't update servo that fast, can't pwm field that slow. |
JonFreeman | 1:450090bdb6f4 | 523 | |
JonFreeman | 1:450090bdb6f4 | 524 | PWM_OSC_IN.pulsewidth_us (PWM_PERIOD_US / 2); // value is int |
JonFreeman | 1:450090bdb6f4 | 525 | // PWM_OSC_IN.pulsewidth_us (PWM_PERIOD_US); // value is int |
JonFreeman | 1:450090bdb6f4 | 526 | #ifdef TARGET_NUCLEO_F401RE // |
JonFreeman | 1:450090bdb6f4 | 527 | A_OUT.period_us (100); // pwm as analogue out |
JonFreeman | 0:77803b3ee157 | 528 | A_OUT.pulsewidth_us (19); |
JonFreeman | 1:450090bdb6f4 | 529 | #endif |
JonFreeman | 0:77803b3ee157 | 530 | Throttle = servo_position; |
JonFreeman | 1:450090bdb6f4 | 531 | pc.printf ("\r\n\n\n\n\nAlternator Regulator 2020, Jon Freeman, SystemCoreClock=%d\r\n", SystemCoreClock); |
JonFreeman | 0:77803b3ee157 | 532 | if (!i2c_init()) |
JonFreeman | 0:77803b3ee157 | 533 | pc.printf ("i2c bus failed init\r\n"); |
JonFreeman | 0:77803b3ee157 | 534 | pc.printf ("check_24LC64 returned 0x%x\r\n", check_24LC64()); |
JonFreeman | 1:450090bdb6f4 | 535 | user_settings.load () ; // Fetch values from eeprom, also builds table of speed -> pwm lookups |
JonFreeman | 1:450090bdb6f4 | 536 | // pc.printf ("Loaded\r\n"); |
JonFreeman | 0:77803b3ee157 | 537 | // Setup Complete ! Can now start main control forever loop. |
JonFreeman | 1:450090bdb6f4 | 538 | loop_timer.attach_us (&ISR_fast_interrupt, MAIN_LOOP_REPEAT_TIME_US / 10); // Start periodic interrupt generator 1000us at Feb 2020 |
JonFreeman | 0:77803b3ee157 | 539 | |
JonFreeman | 0:77803b3ee157 | 540 | //***** START OF MAIN LOOP |
JonFreeman | 0:77803b3ee157 | 541 | while (1) { // Loop forever, repeats synchroised by waiting for ticker Interrupt Service Routine to set 'loop_flag' true |
JonFreeman | 0:77803b3ee157 | 542 | while (!loop_flag) { // Most of the time is spent in this loop, repeatedly re-checking for commands from pc port |
JonFreeman | 0:77803b3ee157 | 543 | command_line_interpreter () ; // Proceed beyond here once loop_timer ticker ISR has set loop_flag true |
JonFreeman | 1:450090bdb6f4 | 544 | } // Jun 2019 pass here 100 times per sec |
JonFreeman | 1:450090bdb6f4 | 545 | // BEGIN 100Hz stuff |
JonFreeman | 0:77803b3ee157 | 546 | loop_flag = false; // Clear flag set by ticker interrupt handler |
JonFreeman | 1:450090bdb6f4 | 547 | |
JonFreeman | 1:450090bdb6f4 | 548 | // Three variations on engine rpm. |
JonFreeman | 1:450090bdb6f4 | 549 | RPM_tmp = ReadEngineRPM (); |
JonFreeman | 1:450090bdb6f4 | 550 | RPM_ave += RPM_tmp; // Rising sum needs dividing and resetting to 0 when used |
JonFreeman | 1:450090bdb6f4 | 551 | RPM_filt += RPM_tmp; |
JonFreeman | 1:450090bdb6f4 | 552 | RPM_filt >>= 1; |
JonFreeman | 1:450090bdb6f4 | 553 | |
JonFreeman | 1:450090bdb6f4 | 554 | amp_reading += (raw_amp_reading - 0.5) * AMPS_CAL; |
JonFreeman | 1:450090bdb6f4 | 555 | amp_reading /= 2.0; |
JonFreeman | 1:450090bdb6f4 | 556 | amp_offset += (raw_amp_offset - 0.5) * AMPS_CAL; // This reading probably not useful |
JonFreeman | 1:450090bdb6f4 | 557 | amp_offset /= 2.0; |
JonFreeman | 1:450090bdb6f4 | 558 | |
JonFreeman | 1:450090bdb6f4 | 559 | Amps_Deliverable = Calculate_Amps_Deliverable (ReadEngineRPM ()); // Added Nov 2019, not yet used. Returns normalised 0.0 to 1.0 |
JonFreeman | 1:450090bdb6f4 | 560 | |
JonFreeman | 1:450090bdb6f4 | 561 | // PWM_OSC_IN.pulsewidth_us (user_settings.get_pwm(ReadEngineRPM())); // Update field current limit according to latest measured RPM |
JonFreeman | 0:77803b3ee157 | 562 | |
JonFreeman | 0:77803b3ee157 | 563 | // while (LocalCom.readable()) { |
JonFreeman | 0:77803b3ee157 | 564 | // int q = LocalCom.getc(); |
JonFreeman | 0:77803b3ee157 | 565 | // //q++; |
JonFreeman | 0:77803b3ee157 | 566 | // pc.putc (q); |
JonFreeman | 0:77803b3ee157 | 567 | // } |
JonFreeman | 1:450090bdb6f4 | 568 | // END 100Hz stuff |
JonFreeman | 1:450090bdb6f4 | 569 | if (flag_25Hz) { |
JonFreeman | 1:450090bdb6f4 | 570 | flag_25Hz = false; |
JonFreeman | 1:450090bdb6f4 | 571 | // BEGIN 25Hz stuff |
JonFreeman | 0:77803b3ee157 | 572 | |
JonFreeman | 1:450090bdb6f4 | 573 | // END 25Hz stuff |
JonFreeman | 1:450090bdb6f4 | 574 | // BEGIN 12.5Hz stuff |
JonFreeman | 1:450090bdb6f4 | 575 | flag_12Hz5 = !flag_12Hz5; |
JonFreeman | 1:450090bdb6f4 | 576 | if (flag_12Hz5) { // Do any even stuff to be done 12.5 times per second |
JonFreeman | 1:450090bdb6f4 | 577 | #ifdef SPEED_CONTROL_ENABLE |
JonFreeman | 1:450090bdb6f4 | 578 | if (RPM_demand < TICKOVER_RPM) |
JonFreeman | 1:450090bdb6f4 | 579 | servo_position = Throttle = 0.0; |
JonFreeman | 1:450090bdb6f4 | 580 | else { |
JonFreeman | 1:450090bdb6f4 | 581 | RPM_ave /= 8; |
JonFreeman | 1:450090bdb6f4 | 582 | // irevs_error = RPM_demand - ReadEngineRPM (); |
JonFreeman | 1:450090bdb6f4 | 583 | irevs_error = RPM_demand - RPM_filt; |
JonFreeman | 1:450090bdb6f4 | 584 | revs_error = (double) irevs_error; |
JonFreeman | 1:450090bdb6f4 | 585 | if (abs(revs_error) > 3.0) { // if speed error > 3rpm, tweak, otherwise deadband |
JonFreeman | 1:450090bdb6f4 | 586 | //servo_position += (revs_error / 7500.0); |
JonFreeman | 1:450090bdb6f4 | 587 | servo_position += (revs_error / speed_control_factor); |
JonFreeman | 1:450090bdb6f4 | 588 | servo_position = normalise(&servo_position); |
JonFreeman | 1:450090bdb6f4 | 589 | if (servo_position < 0.0 || servo_position > 1.0) |
JonFreeman | 1:450090bdb6f4 | 590 | pc.printf ("servo_position error %f\r\n", servo_position); |
JonFreeman | 1:450090bdb6f4 | 591 | if (servo_position > throttle_limit) |
JonFreeman | 1:450090bdb6f4 | 592 | servo_position = throttle_limit; |
JonFreeman | 1:450090bdb6f4 | 593 | Throttle = servo_position; |
JonFreeman | 1:450090bdb6f4 | 594 | } |
JonFreeman | 1:450090bdb6f4 | 595 | } |
JonFreeman | 1:450090bdb6f4 | 596 | RPM_ave = 0; // Reset needed |
JonFreeman | 1:450090bdb6f4 | 597 | #endif |
JonFreeman | 1:450090bdb6f4 | 598 | } |
JonFreeman | 1:450090bdb6f4 | 599 | else { // Do odd 12.5 times per sec stuff |
JonFreeman | 1:450090bdb6f4 | 600 | flag_12Hz5 = false; |
JonFreeman | 1:450090bdb6f4 | 601 | myled = !myled; |
JonFreeman | 1:450090bdb6f4 | 602 | LocalCom.printf ("%d\r\n", volt_reading); |
JonFreeman | 1:450090bdb6f4 | 603 | //void set_pwm (double d) { |
JonFreeman | 1:450090bdb6f4 | 604 | |
JonFreeman | 1:450090bdb6f4 | 605 | // set_pwm (user_settings.get_pwm(ReadEngineRPM())); |
JonFreeman | 1:450090bdb6f4 | 606 | |
JonFreeman | 1:450090bdb6f4 | 607 | /* servo_position += servo_fucker; |
JonFreeman | 1:450090bdb6f4 | 608 | if (servo_position > 1.0 || servo_position < 0.0) |
JonFreeman | 1:450090bdb6f4 | 609 | servo_fucker *= -1.0; |
JonFreeman | 1:450090bdb6f4 | 610 | Throttle = servo_position; |
JonFreeman | 1:450090bdb6f4 | 611 | */ |
JonFreeman | 1:450090bdb6f4 | 612 | } // End of if(flag_12Hz5) |
JonFreeman | 1:450090bdb6f4 | 613 | // END 12.5Hz stuff |
JonFreeman | 1:450090bdb6f4 | 614 | ticks++; // advances @ 25Hz |
JonFreeman | 1:450090bdb6f4 | 615 | if (ticks > 24) { // once per sec stuff |
JonFreeman | 1:450090bdb6f4 | 616 | // BEGIN 1Hz stuff |
JonFreeman | 0:77803b3ee157 | 617 | ticks = 0; |
JonFreeman | 1:450090bdb6f4 | 618 | if (query_toggle) { |
JonFreeman | 1:450090bdb6f4 | 619 | pc.printf ("V=%.1f\tI=%.1f\tRPM=%d\tservo%.2f\r\n", Read_BatteryVolts(), amp_reading, ReadEngineRPM (), servo_position); |
JonFreeman | 1:450090bdb6f4 | 620 | } |
JonFreeman | 1:450090bdb6f4 | 621 | // pc.printf ("Tick %d\r\n", flag_12Hz5); |
JonFreeman | 1:450090bdb6f4 | 622 | // tempfilt *= 0.99; |
JonFreeman | 1:450090bdb6f4 | 623 | // tempfilt += Read_AlternatorAmps() * 0.01; |
JonFreeman | 1:450090bdb6f4 | 624 | // pc.printf ("RPM %d, err %.1f, s_p %.2f, lut %.3f\r\n", ReadEngineRPM (), revs_error, servo_position, user_settings.get_pwm(ReadEngineRPM())); |
JonFreeman | 1:450090bdb6f4 | 625 | // pc.printf ("Vbat %.2f, servo %.3f, amps %.3f, filt %.3f\r\n", Read_BatteryVolts(), servo_position, Read_AlternatorAmps(), tempfilt); |
JonFreeman | 1:450090bdb6f4 | 626 | // END 1Hz stuff |
JonFreeman | 0:77803b3ee157 | 627 | } // eo once per second stuff |
JonFreeman | 1:450090bdb6f4 | 628 | } // End of 100Hz stuff |
JonFreeman | 0:77803b3ee157 | 629 | } // End of main programme loop |
JonFreeman | 0:77803b3ee157 | 630 | } // End of main function - end of programme |
JonFreeman | 0:77803b3ee157 | 631 | //***** END OF MAIN LOOP |