wideband/firmware/heater_control.cpp

167 lines
4.4 KiB
C++
Raw Normal View History

2020-10-30 01:53:54 -07:00
#include "heater_control.h"
2020-10-31 14:58:34 -07:00
#include "wideband_config.h"
2020-10-30 01:53:54 -07:00
2020-10-30 02:40:18 -07:00
#include "ch.h"
2020-10-30 01:53:54 -07:00
#include "hal.h"
2020-12-10 18:32:41 -08:00
#include "fault.h"
2020-10-30 01:53:54 -07:00
#include "pwm.h"
#include "sampling.h"
2020-10-31 14:58:34 -07:00
#include "pid.h"
2020-10-30 01:53:54 -07:00
// 400khz / 1024 = 390hz PWM
Pwm heaterPwm(PWMD1, 0, 400'000, 1024);
2020-10-30 02:40:18 -07:00
enum class HeaterState
{
Preheat,
WarmupRamp,
ClosedLoop,
2020-12-10 18:32:41 -08:00
Stopped,
2020-10-30 02:40:18 -07:00
};
2020-12-10 22:08:00 -08:00
int timeCounter = HEATER_PREHEAT_TIME / HEATER_CONTROL_PERIOD;
2020-10-30 02:40:18 -07:00
float rampDuty = 0.5f;
static HeaterState GetNextState(HeaterState state, float sensorEsr)
{
switch (state)
{
case HeaterState::Preheat:
2020-12-10 18:32:41 -08:00
timeCounter--;
2020-10-30 02:40:18 -07:00
2020-12-13 17:24:33 -08:00
// If preheat timeout, or sensor is already hot (engine running?)
if (timeCounter <= 0 || sensorEsr < HEATER_CLOSED_LOOP_THRESHOLD_ESR)
2020-10-30 02:40:18 -07:00
{
// If enough time has elapsed, start the ramp
2021-07-09 23:15:24 -07:00
// Start the ramp at 30% duty - ~3-4 volts
rampDuty = 0.3f;
2020-12-10 18:32:41 -08:00
// Next phase times out at 15 seconds
2020-12-10 22:08:00 -08:00
timeCounter = HEATER_WARMUP_TIMEOUT / HEATER_CONTROL_PERIOD;
2020-12-10 18:32:41 -08:00
2020-10-30 02:40:18 -07:00
return HeaterState::WarmupRamp;
}
2020-10-31 14:54:50 -07:00
// Stay in preheat - wait for time to elapse
break;
2020-10-30 02:40:18 -07:00
case HeaterState::WarmupRamp:
2020-12-10 22:08:00 -08:00
if (sensorEsr < HEATER_CLOSED_LOOP_THRESHOLD_ESR)
2020-10-30 02:40:18 -07:00
{
return HeaterState::ClosedLoop;
}
2020-12-10 18:32:41 -08:00
else if (timeCounter == 0)
{
setFault(Fault::SensorDidntHeat);
return HeaterState::Stopped;
}
timeCounter--;
2020-10-31 14:54:50 -07:00
break;
2020-12-10 18:32:41 -08:00
case HeaterState::ClosedLoop:
2020-12-11 15:46:03 -08:00
if (sensorEsr < HEATER_OVERHEAT_ESR)
2020-12-10 22:08:00 -08:00
{
setFault(Fault::SensorOverheat);
2020-12-11 15:10:04 -08:00
return HeaterState::Stopped;
2020-12-10 22:08:00 -08:00
}
2020-12-11 15:46:03 -08:00
else if (sensorEsr > HEATER_UNDERHEAT_ESR)
{
setFault(Fault::SensorUnderheat);
return HeaterState::Stopped;
}
2020-12-10 22:08:00 -08:00
2020-12-10 18:32:41 -08:00
break;
case HeaterState::Stopped: break;
2020-10-30 02:40:18 -07:00
}
2020-10-31 14:54:50 -07:00
return state;
2020-10-30 02:40:18 -07:00
}
2021-07-09 23:15:24 -07:00
// oscillates at 0.04 kP
//static Pid heaterPid(0.04f,0, 0, 0.6f, HEATER_CONTROL_PERIOD);
// "no overshoot" PID
//static Pid heaterPid(0.008f, 0.0288f, 0.0015f, 0.6f, HEATER_CONTROL_PERIOD);
// PI
//static Pid heaterPid(0.02f, 0.0576f, 0.0015f, 0.6f, HEATER_CONTROL_PERIOD);
// rusefi ETB autotune
//static Pid heaterPid(0.014f, 0.018f, 0.0018f, 0.6f, HEATER_CONTROL_PERIOD);
// rusefi ETB autotune with more PD
static Pid heaterPid(0.02f, 0.018f, 0.003f, 0.6f, HEATER_CONTROL_PERIOD);
2020-10-31 14:58:34 -07:00
2020-10-30 02:40:18 -07:00
static float GetDutyForState(HeaterState state, float heaterEsr)
{
switch (state)
{
2020-12-25 22:46:07 -08:00
case HeaterState::Preheat: return 0.04f;
2020-10-30 02:40:18 -07:00
case HeaterState::WarmupRamp:
2020-12-25 22:46:07 -08:00
if (rampDuty < 0.75f)
2020-10-30 02:40:18 -07:00
{
2021-07-09 23:15:24 -07:00
// 0.3 volt per second, divided by battery voltage and update rate
constexpr float rampRateVoltPerSecond = 0.3f;
2021-04-26 17:40:06 -07:00
constexpr float heaterFrequency = 1000.0f / HEATER_CONTROL_PERIOD;
rampDuty += ((rampRateVoltPerSecond / 14) / heaterFrequency);
2020-10-30 02:40:18 -07:00
}
return rampDuty;
case HeaterState::ClosedLoop:
2020-10-31 17:34:36 -07:00
// Negated because lower resistance -> hotter
2020-12-11 16:50:06 -08:00
return -heaterPid.GetOutput(HEATER_TARGET_ESR, heaterEsr);
2020-12-10 18:32:41 -08:00
case HeaterState::Stopped:
// Something has gone wrong, return 0.
return 0;
2020-10-30 02:40:18 -07:00
}
2020-12-10 21:05:02 -08:00
// should be unreachable
return 0;
2020-10-30 02:40:18 -07:00
}
2020-10-31 14:54:50 -07:00
static HeaterState state = HeaterState::Preheat;
2020-10-30 02:40:18 -07:00
static THD_WORKING_AREA(waHeaterThread, 256);
static void HeaterThread(void*)
{
2021-06-03 21:03:41 -07:00
// Wait for temperature sensing to stabilize so we don't
// immediately think we overshot the target temperature
chThdSleepMilliseconds(1000);
2020-10-30 02:40:18 -07:00
while (true)
{
// Read sensor state
float heaterEsr = GetSensorInternalResistance();
// Run the state machine
state = GetNextState(state, heaterEsr);
float duty = GetDutyForState(state, heaterEsr);
2021-07-09 23:15:24 -07:00
// Limit to 80% duty
if (duty > 0.8) {
duty = 0.8;
}
2020-10-30 02:40:18 -07:00
// Pipe the output to the heater driver
heaterPwm.SetDuty(duty);
// Loop at ~20hz
2020-10-31 14:58:34 -07:00
chThdSleepMilliseconds(HEATER_CONTROL_PERIOD);
2020-10-30 02:40:18 -07:00
}
}
2020-10-30 01:53:54 -07:00
void StartHeaterControl()
{
heaterPwm.Start();
heaterPwm.SetDuty(0);
2020-10-30 02:40:18 -07:00
chThdCreateStatic(waHeaterThread, sizeof(waHeaterThread), NORMALPRIO + 1, HeaterThread, nullptr);
2020-10-30 01:53:54 -07:00
}
2020-10-31 14:54:50 -07:00
bool IsRunningClosedLoop()
{
return state == HeaterState::ClosedLoop;
}