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-10 18:32:41 -08:00
|
|
|
if (timeCounter <= 0)
|
2020-10-30 02:40:18 -07:00
|
|
|
{
|
|
|
|
// If enough time has elapsed, start the ramp
|
|
|
|
// Start the ramp at 50% duty - ~6-7 volts
|
|
|
|
rampDuty = 0.5f;
|
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:
|
|
|
|
// TODO: handle departure from closed loop
|
2020-12-10 22:08:00 -08:00
|
|
|
if (sensorEsr < HEATER_FAULT_ESR)
|
|
|
|
{
|
|
|
|
setFault(Fault::SensorOverheat);
|
2020-12-11 15:10:04 -08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-12-11 15:24:56 -08:00
|
|
|
static Pid heaterPid(0.01f, 0.05f, 1, 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)
|
|
|
|
{
|
|
|
|
case HeaterState::Preheat: return 0.125f;
|
|
|
|
case HeaterState::WarmupRamp:
|
|
|
|
if (rampDuty < 0.9f)
|
|
|
|
{
|
|
|
|
// 0.4 volt per second, divided by battery voltage and update rate
|
|
|
|
rampDuty += (0.4f / (14 * 20));
|
|
|
|
}
|
|
|
|
|
|
|
|
return rampDuty;
|
|
|
|
case HeaterState::ClosedLoop:
|
2020-10-31 17:34:36 -07:00
|
|
|
// Negated because lower resistance -> hotter
|
|
|
|
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*)
|
|
|
|
{
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
// Read sensor state
|
|
|
|
float heaterEsr = GetSensorInternalResistance();
|
|
|
|
|
|
|
|
// Run the state machine
|
|
|
|
state = GetNextState(state, heaterEsr);
|
|
|
|
float duty = GetDutyForState(state, heaterEsr);
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|