SAE Teensy ECU
IIT SAE Microcontroller programming
Loading...
Searching...
No Matches
ECUStates.cpp
1#include "ECUStates.hpp"
2#include "AeroServo.h"
3#include "ECUGlobalConfig.h"
4#include "Echo.h"
5#include "Faults.h"
6#include "Heartbeat.h"
7#include "Log.h"
8#include "Mirror.h"
9#include "MotorControl.def"
10#include "MotorControl.h"
11#include "Util.h"
12
13static bool FaultCheck() { // NOTE: Will only return true if hardfault occurs
14 if (Fault::softFault())
16 if (Fault::hardFault())
17 return true;
18 return false;
19}
20
21static void updateFaultLights() {
22 static int bms, imd, bms_l, imd_l = 0;
23 if ((bms = Pins::getPinValue(PINS_BACK_BMS_FAULT)) != bms_l || (imd = Pins::getPinValue(PINS_BACK_IMD_FAULT)) != imd_l) {
24#ifdef CONF_ECU_DEBUG
25 Log.d("FaultLights", "Updating Lights");
26#endif
27 Pins::setInternalValue(PINS_INTERNAL_BMS_FAULT, bms);
28 Pins::setInternalValue(PINS_INTERNAL_IMD_FAULT, imd);
29 bms_l = bms;
30 imd_l = imd;
31 }
32 int breakVal = Pins::getCanPinValue(PINS_FRONT_BRAKE);
33 Pins::setPinValue(PINS_BACK_BRAKE_LIGHT, PINS_ANALOG_HIGH * (breakVal > (CONF_BRAKE_MIN + 32)));
34}
35
36State::State_t *ECUStates::Initialize_State::run(void) {
37 Log.i(ID, "Teensy 3.6 SAE BACK ECU Initalizing");
38 Log.i(ID, "Setup canbus");
39 Canbus::setup(); // allocate and organize addresses
40 Log.i(ID, "Initialize pins");
41 Pins::initialize(); // setup predefined pins
42 Log.i(ID, "Waiting for sync");
43 while (!Pins::getCanPinValue(PINS_INTERNAL_SYNC)) {
44 Canbus::sendData(ADD_MC0_CTRL);
45 Canbus::sendData(ADD_MC1_CTRL);
46 delay(100);
47 }
49 MC::setup();
50#ifdef CONF_ECU_DEBUG
53#endif
54 Heartbeat::addCallback(updateFaultLights); // IMPROVE: don't just periodically check if leds are on
56
57 // Front teensy should know when to blink start light
58 Log.d(ID, "Checking for Inital fault");
59
60 // NOTE: IMD Fault does not matter in initalizing state
61 if (!Pins::getPinValue(PINS_BACK_IMD_FAULT) && FaultCheck()) {
62 Log.e(ID, "Inital fault check tripped");
63 return &ECUStates::FaultState;
64 }
65
66 // TSV
67 Log.i(ID, "Waiting for shutdown signal");
68
69 elapsedMillis shutdownBounce;
70
71 while (true) {
72 if (Pins::getPinValue(PINS_BACK_SHUTDOWN_SIGNAL)) {
73 if (shutdownBounce > 50)
74 break;
75 } else {
76 shutdownBounce = 0;
77 }
78 }
79
80 Log.i(ID, "Shutdown signal received");
81
82 Log.d(ID, "Finshed Setup");
83 return &ECUStates::PreCharge_State;
84};
85
86State::State_t *ECUStates::PreCharge_State::PreCharFault(void) {
87 Log.w(ID, "Opening Air1, Air2 and Precharge Relay");
88 Pins::setPinValue(PINS_BACK_AIR1, LOW);
89 Pins::setPinValue(PINS_BACK_AIR2, LOW);
90 Pins::setPinValue(PINS_BACK_PRECHARGE_RELAY, LOW);
91 Log.e(ID, "Precharge Faulted during precharge");
92 return &ECUStates::FaultState;
93}
94
95bool ECUStates::PreCharge_State::voltageCheck() {
96 int16_t BMSVolt = BMS_DATA_Buffer.getShort(2) / 10; // Byte 2-3: Pack Instant Voltage
97 int16_t MC0Volt = MC0_VOLT_Buffer.getShort(0) / 10; // Bytes 0-1: DC BUS MC Voltage
98 int16_t MC1Volt = MC1_VOLT_Buffer.getShort(0) / 10; // Bytes 0-1: DC BUS MC Voltage
99
100 return 0.65 * BMSVolt <= (MC0Volt + MC1Volt) / 2;
101}
102
103State::State_t *ECUStates::PreCharge_State::run(void) { // NOTE: Low = Closed, High = Open
104 Log.i(ID, "Precharge running");
105
106 if (FaultCheck()) {
107 Log.e(ID, "Precharge Faulted before precharge");
108 return &ECUStates::FaultState;
109 }
110
111 // NOTE: Assuming Air2 is correct
112 Log.w(ID, "Closing Air2 and Precharge Relay and opening Air1");
113 Pins::setPinValue(PINS_BACK_AIR2, PINS_ANALOG_HIGH);
114 Pins::setPinValue(PINS_BACK_PRECHARGE_RELAY, PINS_ANALOG_HIGH);
115 Pins::setPinValue(PINS_BACK_AIR1, LOW);
116
117 if (FaultCheck()) {
118 return PreCharFault();
119 }
120
121 elapsedMillis timeElapsed;
122
123 Log.d(ID, "Running precharge loop");
124
125 while (timeElapsed <= 10000) {
126 if (timeElapsed >= 1000 && voltageCheck()) { // NOTE: will always pass if submodules are disconnected from CAN net
127 Log.w(ID, "Opening precharge relay");
128 Pins::setPinValue(PINS_BACK_PRECHARGE_RELAY, LOW);
129 Log.i(ID, "Precharge Finished, closing Air1");
130 Pins::setPinValue(PINS_BACK_AIR1, PINS_ANALOG_HIGH);
131 return &ECUStates::Idle_State;
132 }
133 }
134
135 Log.e(ID, "Precharge timed out");
136 return PreCharFault();
137};
138
139State::State_t *ECUStates::Idle_State::run(void) {
140 Log.i(ID, "Waiting for Button not to be pressed");
141 while (!Pins::getCanPinValue(PINS_FRONT_BUTTON_INPUT_OFF)) {
142 }
143
144 Log.i(ID, "Waiting for Button or Charging Press");
145
146 // Front teensy should already be blinking start light
147
148 elapsedMillis waiting;
149
150 while (true) {
151 if (!Pins::getCanPinValue(PINS_FRONT_BUTTON_INPUT_OFF)) {
152 Log.i(ID, "Button Pressed");
153 // Front teensy will stop blinking start light
154 return &ECUStates::Button_State;
155 } else if (Pins::getCanPinValue(PINS_INTERNAL_CHARGE_SIGNAL)) {
156 Log.i(ID, "Charging Pressed");
157 // Front teensy will continue blinking start light in charge state
158 return &ECUStates::Charging_State;
159 } else if (FaultCheck()) { // TODO: rate limit logging
160 Log.w(ID, "Fault in idle state");
161 break;
162 }
163 }
164 return &ECUStates::FaultState;
165}
166
167State::State_t *ECUStates::Charging_State::run(void) {
168 Pins::setPinValue(PINS_BACK_CHARGING_RELAY, HIGH);
169 Log.i(ID, "Charging on");
170
171 elapsedMillis voltLogNotify;
172
173 while (Pins::getCanPinValue(PINS_INTERNAL_CHARGE_SIGNAL)) {
174 // IMPROVE: Don't use fault to stop charging
175 if (FaultCheck()) {
176 Pins::setPinValue(PINS_BACK_CHARGING_RELAY, LOW);
177 Log.e(ID, "Charging faulted, turning off");
178 return &ECUStates::FaultState;
179 }
180 }
181
182 Pins::setPinValue(PINS_BACK_CHARGING_RELAY, LOW);
183 Log.i(ID, "Charging turning off");
184
185 return &ECUStates::Idle_State;
186}
187
188State::State_t *ECUStates::Button_State::run(void) {
189 Log.i(ID, "Waiting for Button not to be pressed");
190 while (!Pins::getCanPinValue(PINS_FRONT_BUTTON_INPUT_OFF)) {
191 }
192 Log.i(ID, "Playing sound");
193
194 Pins::setPinValue(PINS_BACK_SOUND_DRIVER, PINS_ANALOG_HIGH);
195
196 elapsedMillis soundTimer;
197
198 while (soundTimer < 3000) {
199 if (FaultCheck()) {
200 Log.e(ID, "Failed to play sound");
201 Pins::setPinValue(PINS_BACK_SOUND_DRIVER, LOW);
202 return &ECUStates::FaultState;
203 }
204 }
205
206 Pins::setPinValue(PINS_BACK_SOUND_DRIVER, LOW);
207 Log.i(ID, "Playing sound finished");
208
209 return &ECUStates::Driving_Mode_State;
210}
211
212void ECUStates::Driving_Mode_State::carCooling(bool enable) { // NOTE: Cooling values are all static
213 int setOn = enable * PINS_ANALOG_MAX;
214 Pins::setPinValue(PINS_BACK_PUMP_DAC, setOn);
215 Pins::setPinValue(PINS_BACK_FAN1_PWM, setOn);
216 Pins::setPinValue(PINS_BACK_FAN2_PWM, setOn);
217 Pins::setPinValue(PINS_BACK_FAN3_PWM, setOn);
218 Pins::setPinValue(PINS_BACK_FAN4_PWM, setOn);
219 Pins::setPinValue(PINS_BACK_FANS_ONOFF, enable);
220}
221
222State::State_t *ECUStates::Driving_Mode_State::DrivingModeFault(void) {
223 Log.i(ID, "Fault happened in driving state");
224 Pins::setInternalValue(PINS_INTERNAL_START, false);
225 carCooling(false);
226 Log.i(ID, "Starting MC heartbeat");
228 return &ECUStates::FaultState;
229}
230
231// NOTE: MCs weak faults also cause a true fault here
232State::State_t *ECUStates::Driving_Mode_State::run(void) {
233 Log.i(ID, "Driving mode on");
234 Log.i(ID, "Cooling on");
235 carCooling(true);
236
237 elapsedMillis controlDelay;
238
239 Log.i(ID, "Stopping MC heartbeat");
241
242 Log.d(ID, "Sending Fault reset to MCs complete");
243 MC::clearFaults(); // Clear fault if any
244
245 Pins::setInternalValue(PINS_INTERNAL_START, true);
246
247 Log.d(ID, "Entering drive loop");
248 while (true) {
249 if (controlDelay > 20) { // NOTE: Each data frame is 89 bits long thus at 250kbps the MC buses can handle a maximum of 2808 messages per second
250 controlDelay = 0;
251
252 static bool runReverse = false;
253
254 if (Pins::getCanPinValue(PINS_INTERNAL_REVERSE) != runReverse) {
255 runReverse = Pins::getCanPinValue(PINS_INTERNAL_REVERSE);
256 MC::setDirection(!runReverse);
257 }
258
259 if (Fault::anyFault()) { // FIXME: are motor controller faults actually being picked up?
260 return DrivingModeFault();
261 }
262
263#if ECU_TESTING != BACK_ECU
264 if (((MC0_VOLT_Buffer.getShort(0) / 10) + (MC1_VOLT_Buffer.getShort(0) / 10)) / 2 < 90) { // 'HVD Fault'
265 Log.e(ID, "'HVD Fault' MC voltage < 90");
266 return DrivingModeFault();
267 }
268#endif
269
270 int breakVal = Pins::getCanPinValue(PINS_FRONT_BRAKE);
271 int steerVal = Pins::getCanPinValue(PINS_FRONT_STEER);
272
273 int pedal0 = Pins::getCanPinValue(PINS_FRONT_PEDAL0);
274 int pedal1 = Pins::getCanPinValue(PINS_FRONT_PEDAL1);
275
276 int pAVG = (pedal0 + pedal1) / 2;
277
278 // NOTE: pedal has a threshold value
279 if (pAVG >= 100 && (float)abs(pedal1 - pedal0) / PINS_ANALOG_HIGH > 0.5f) {
280 Log.e(ID, "Pedal value offset > 10%");
281 return DrivingModeFault();
282 }
283
284 // TODO: passive regen braking, only for hawkrod
285
286 // if (pAVG >= 200) {
287 MC::setTorque(pAVG, breakVal, steerVal);
288 // } else if (MC::motorSpeed() > 25) { // FIXME: Seems to just disable motors?
289 // MC::sendTorque(ADD_MC0_CTRL, -160, 1, 1);
290 // MC::sendTorque(ADD_MC1_CTRL, -160, 1, 1);
291 // }
292
293 if (Fault::softFault()) {
295 }
296
297 Log.i(ID, "Aero servo position:", Aero::getServoValue(), true);
298 Log.i(ID, "Last MC0 Torque Value:", MC::getLastTorqueValue(true), true);
299 Log.i(ID, "Last MC1 Torque Value:", MC::getLastTorqueValue(false), true);
300
301 Aero::run(breakVal, steerVal);
302 }
303
304 if (!Pins::getCanPinValue(PINS_FRONT_BUTTON_INPUT_OFF)) {
305 Log.w(ID, "Going back to Idle state");
306 break;
307 }
308 }
309
310 Pins::setInternalValue(PINS_INTERNAL_START, false);
311
312 Log.i(ID, "Starting MC heartbeat");
314
315 carCooling(false);
316
317 Log.i(ID, "Driving mode off");
318 return &ECUStates::Idle_State;
319}
320
321State::State_t *ECUStates::FaultState::run(void) {
322 Pins::setInternalValue(PINS_INTERNAL_GEN_FAULT, 1);
323 Canbus::enableInterrupts(false);
324
325 Log.w(ID, "Opening Air1, Air2 and Precharge Relay");
326 Pins::setPinValue(PINS_BACK_AIR1, LOW);
327 Pins::setPinValue(PINS_BACK_AIR2, LOW);
328 Pins::setPinValue(PINS_BACK_PRECHARGE_RELAY, LOW);
329
330 Log.w(ID, "Resetting pins");
332
333 if (getLastState() == &ECUStates::PreCharge_State) {
334 while (true) {
335 Log.e(ID, "Precharge fault");
336 Pins::setInternalValue(PINS_INTERNAL_GEN_FAULT, 1);
339 delay(1000);
340 }
341 } else {
342 Log.e(ID, "FAULT STATE");
344 delay(500);
345 }
346 Pins::setInternalValue(PINS_INTERNAL_GEN_FAULT, 0);
347 return &ECUStates::Initialize_State;
348}
349
350State::State_t *ECUStates::Logger_t::run(void) {
351
352 static elapsedMillis timeElapsed;
353
354 if (timeElapsed >= 2000) {
355 timeElapsed = timeElapsed - 2000;
356 Log(ID, "A7 Pin Value:", Pins::getPinValue(0));
357 Log("FAKE ID", "A7 Pin Value:");
358 Log(ID, "whaAAAT?");
359 Log(ID, "", 0xDEADBEEF);
360 Log(ID, "Notify code: ", getNotify());
361 }
362
363 return &ECUStates::Bounce;
364};
365
366State::State_t *ECUStates::Bounce_t::run(void) {
367 delay(250);
368 Log.i(ID, "Bounce!");
369 notify(random(100));
370 delay(250);
371 return getLastState();
372}
Interpretation of Aero subteam code.
Configure global build properties.
Echo can messages with a delay.
Checks for defined faults from canbus addresses or pins.
Make one ECU tell the other it is alive.
Special logging functionality.
Logging::Log_t Log
The global logging object.
This module allows for the monitoring and modification of each GPIO pin on an ECU.
#define CONF_BRAKE_MIN
The brake input range to map as doubles.
Motor Controller module.
#define PINS_ANALOG_HIGH
The high analog value, will not force an analog output to lock onto a high state, as PINS_ANALOG_MAX ...
Definition Pins.h:67
#define PINS_ANALOG_MAX
The maximum analog value, given the current PINS_ANALOG_RES.
Definition Pins.h:63
Various utility functions.
void run(int breakPressure, int steeringAngle)
Run Aero servo logic, given raw values.
void setup(void)
Attach analog pins to their servo objects.
int getServoValue()
Get the current servo position value.
void setup()
Initialize on receiving ECU to echo messages.
bool softFault(void)
Check if any non-serious fault has occurred.
bool hardFault(void)
Checks if any serious fault has occurred.
bool anyFault(void)
Checks both hardFault and softFault.
void logFault(void)
Interprets and logs the last fault that was checked using the Log library.
void addCallback(beatFunc func)
Add a callback to be run at each haertbeat.
void beginBeating()
begin sending a beat signal at a set interval
void setTorque(int pedal, int brake, int steer)
Calculate and set the torque of both MCs.
void setDirection(bool runForward)
Set the direction of the motors.
int getLastTorqueValue(bool mc0)
Get the last torque percent value sent to a MC.
void enableMotorBeating(bool enable)
Start MC heartbeat function, establishing a connection with the MCs.
void clearFaults(void)
Clear MC faults by sending a clear fault command.
void setup(void)
Initialize MC heartbeat function and clear faults, if any.
void setup(void)
Setup a listener for going into mirror mode, dependent on which ECU is compiled.
void setPinValue(uint8_t GPIO_Pin, int value)
Set the pin value of a predefined pin.
int getCanPinValue(uint8_t CAN_GPIO_Pin)
Get the pin value of a predefined canbus pin.
void initialize(void)
Initialize all predefined pins.
int getPinValue(uint8_t GPIO_Pin)
Get the pin value of a predefined pin.
void setInternalValue(uint8_t Internal_Pin, int value)
Set the value of an internal pin.
void resetPhysicalPins()
Resets physical pins to their inital state.
void d(LOG_TAG TAG, LOG_MSG message)
Log a string using a debug tag.
void w(LOG_TAG TAG, LOG_MSG message)
Log a string using a warning tag.
void e(LOG_TAG TAG, LOG_MSG message)
Log a string using an error tag.
void i(LOG_TAG TAG, LOG_MSG message)
Log a string using an info tag.
The parent state structure to extend from to create more states.
Definition State.h:70