mirror of https://github.com/rusefi/wideband.git
Multichannel (#134)
* Multichannel AFR * heater: fixes for multi channel mode * f1_dual: fix configuration names * pump_dac: fixes for multi-channel mode * pwm: fixes for multichannel mode * pump_control: reference instead of pointer * sampling: reference instead of pointer * heater_control: reference instead of pointer * sampling: comment about heater/battery voltage * f0_module: fixes for multi-channel update * f1_rev2: fix for multichannel AFR * hello rev 3 Co-authored-by: rusefillc <sdfsdfqsf2334234234>
This commit is contained in:
parent
2cc460adba
commit
cddcd0d88c
|
@ -53,7 +53,10 @@ void AuxOutThread(void*)
|
||||||
{
|
{
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
SetAuxDac(0, GetLambda());
|
for (int ch = 0; ch < AFR_CHANNELS; ch++)
|
||||||
|
{
|
||||||
|
SetAuxDac(ch, GetLambda(ch));
|
||||||
|
}
|
||||||
|
|
||||||
chThdSleepMilliseconds(10);
|
chThdSleepMilliseconds(10);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,11 @@
|
||||||
|
|
||||||
// PA7
|
// PA7
|
||||||
#define HEATER_PWM_DEVICE PWMD1
|
#define HEATER_PWM_DEVICE PWMD1
|
||||||
#define HEATER_PWM_CHANNEL 0
|
#define HEATER_PWM_CHANNEL_0 0
|
||||||
|
|
||||||
// PA6
|
// PA6
|
||||||
#define PUMP_DAC_PWM_DEVICE PWMD3
|
#define PUMP_DAC_PWM_DEVICE PWMD3
|
||||||
#define PUMP_DAC_PWM_CHANNEL 0
|
#define PUMP_DAC_PWM_CHANNEL_0 0
|
||||||
|
|
||||||
#define ID_SEL1_PORT GPIOB
|
#define ID_SEL1_PORT GPIOB
|
||||||
#define ID_SEL1_PIN 1
|
#define ID_SEL1_PIN 1
|
||||||
|
|
|
@ -31,13 +31,12 @@
|
||||||
|
|
||||||
// L_heater_pwm - PB7 TIM4_CH2
|
// L_heater_pwm - PB7 TIM4_CH2
|
||||||
#define HEATER_PWM_DEVICE PWMD4
|
#define HEATER_PWM_DEVICE PWMD4
|
||||||
#define HEATER_PWM_CHANNEL 1
|
#define HEATER_PWM_CHANNEL_0 1
|
||||||
#define L_HEATER_PORT GPIOB
|
#define L_HEATER_PORT GPIOB
|
||||||
#define L_HEATER_PIN 7
|
#define L_HEATER_PIN 7
|
||||||
|
|
||||||
// B_heater_pwm - PB6 TIM4_CH1
|
// R_heater_pwm - PB6 TIM4_CH1
|
||||||
#define R_HEATER_PWM_DEVICE PWMD4
|
#define HEATER_PWM_CHANNEL_1 0
|
||||||
#define R_HEATER_PWM_CHANNEL 0
|
|
||||||
#define R_HEATER_PORT GPIOB
|
#define R_HEATER_PORT GPIOB
|
||||||
#define R_HEATER_PIN 6
|
#define R_HEATER_PIN 6
|
||||||
|
|
||||||
|
@ -51,7 +50,8 @@
|
||||||
|
|
||||||
// PA1 TIM2_CH2
|
// PA1 TIM2_CH2
|
||||||
#define PUMP_DAC_PWM_DEVICE PWMD2
|
#define PUMP_DAC_PWM_DEVICE PWMD2
|
||||||
#define PUMP_DAC_PWM_CHANNEL 1
|
#define PUMP_DAC_PWM_CHANNEL_0 2 /* left */
|
||||||
|
#define PUMP_DAC_PWM_CHANNEL_1 1 /* right */
|
||||||
|
|
||||||
// TIM1 - DAC for AUX outputs
|
// TIM1 - DAC for AUX outputs
|
||||||
#define AUXOUT_DAC_PWM_DEVICE PWMD1
|
#define AUXOUT_DAC_PWM_DEVICE PWMD1
|
||||||
|
|
|
@ -31,11 +31,11 @@
|
||||||
|
|
||||||
// PB6 TIM4_CH1
|
// PB6 TIM4_CH1
|
||||||
#define HEATER_PWM_DEVICE PWMD4
|
#define HEATER_PWM_DEVICE PWMD4
|
||||||
#define HEATER_PWM_CHANNEL 0
|
#define HEATER_PWM_CHANNEL_0 0
|
||||||
|
|
||||||
// PA1 TIM2_CH2
|
// PA1 TIM2_CH2
|
||||||
#define PUMP_DAC_PWM_DEVICE PWMD2
|
#define PUMP_DAC_PWM_DEVICE PWMD2
|
||||||
#define PUMP_DAC_PWM_CHANNEL 1
|
#define PUMP_DAC_PWM_CHANNEL_0 1
|
||||||
|
|
||||||
// TIM1 - DAC for AUX outputs
|
// TIM1 - DAC for AUX outputs
|
||||||
#define AUXOUT_DAC_PWM_DEVICE PWMD1
|
#define AUXOUT_DAC_PWM_DEVICE PWMD1
|
||||||
|
|
|
@ -31,11 +31,11 @@
|
||||||
|
|
||||||
// PB6 TIM4_CH1
|
// PB6 TIM4_CH1
|
||||||
#define HEATER_PWM_DEVICE PWMD4
|
#define HEATER_PWM_DEVICE PWMD4
|
||||||
#define HEATER_PWM_CHANNEL 0
|
#define HEATER_PWM_CHANNEL_0 0
|
||||||
|
|
||||||
// PA1 TIM2_CH2
|
// PA1 TIM2_CH2
|
||||||
#define PUMP_DAC_PWM_DEVICE PWMD2
|
#define PUMP_DAC_PWM_DEVICE PWMD2
|
||||||
#define PUMP_DAC_PWM_CHANNEL 1
|
#define PUMP_DAC_PWM_CHANNEL_0 1
|
||||||
|
|
||||||
// DAC for AUX outputs
|
// DAC for AUX outputs
|
||||||
#define AUXOUT_DAC_DEVICE DACD1
|
#define AUXOUT_DAC_DEVICE DACD1
|
||||||
|
|
|
@ -131,7 +131,8 @@ void InitCan()
|
||||||
void SendRusefiFormat(uint8_t idx)
|
void SendRusefiFormat(uint8_t idx)
|
||||||
{
|
{
|
||||||
auto baseAddress = WB_DATA_BASE_ADDR + 2 * idx;
|
auto baseAddress = WB_DATA_BASE_ADDR + 2 * idx;
|
||||||
auto esr = GetSensorInternalResistance();
|
/* TODO: */
|
||||||
|
int ch = 0;
|
||||||
|
|
||||||
{
|
{
|
||||||
CanTxTyped<wbo::StandardData> frame(baseAddress + 0);
|
CanTxTyped<wbo::StandardData> frame(baseAddress + 0);
|
||||||
|
@ -139,19 +140,21 @@ void SendRusefiFormat(uint8_t idx)
|
||||||
// The same header is imported by the ECU and checked against this data in the frame
|
// The same header is imported by the ECU and checked against this data in the frame
|
||||||
frame.get().Version = RUSEFI_WIDEBAND_VERSION;
|
frame.get().Version = RUSEFI_WIDEBAND_VERSION;
|
||||||
|
|
||||||
uint16_t lambda = GetLambda() * 10000;
|
uint16_t lambda = GetLambda(ch) * 10000;
|
||||||
frame.get().Lambda = lambda;
|
frame.get().Lambda = lambda;
|
||||||
frame.get().TemperatureC = GetSensorTemperature();
|
frame.get().TemperatureC = GetSensorTemperature(ch);
|
||||||
frame.get().Valid = IsRunningClosedLoop() ? 0x01 : 0x00;
|
frame.get().Valid = IsRunningClosedLoop(ch) ? 0x01 : 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
auto esr = GetSensorInternalResistance(ch);
|
||||||
|
|
||||||
CanTxTyped<wbo::DiagData> frame(baseAddress + 1);
|
CanTxTyped<wbo::DiagData> frame(baseAddress + 1);
|
||||||
|
|
||||||
frame.get().Esr = esr;
|
frame.get().Esr = esr;
|
||||||
frame.get().NernstDc = GetNernstDc() * 1000;
|
frame.get().NernstDc = GetNernstDc(ch) * 1000;
|
||||||
frame.get().PumpDuty = GetPumpOutputDuty() * 255;
|
frame.get().PumpDuty = GetPumpOutputDuty(ch) * 255;
|
||||||
frame.get().Status = GetCurrentFault();
|
frame.get().Status = GetCurrentFault(ch);
|
||||||
frame.get().HeaterDuty = GetHeaterDuty() * 255;
|
frame.get().HeaterDuty = GetHeaterDuty(ch) * 255;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ using namespace wbo;
|
||||||
|
|
||||||
static Fault currentFault = Fault::None;
|
static Fault currentFault = Fault::None;
|
||||||
|
|
||||||
void SetFault(Fault fault)
|
void SetFault(int ch, Fault fault)
|
||||||
{
|
{
|
||||||
currentFault = fault;
|
currentFault = fault;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ bool HasFault()
|
||||||
return currentFault != Fault::None;
|
return currentFault != Fault::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Fault GetCurrentFault()
|
Fault GetCurrentFault(int ch)
|
||||||
{
|
{
|
||||||
return currentFault;
|
return currentFault;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,6 @@
|
||||||
|
|
||||||
#include "../for_rusefi/wideband_can.h"
|
#include "../for_rusefi/wideband_can.h"
|
||||||
|
|
||||||
void SetFault(wbo::Fault fault);
|
void SetFault(int ch, wbo::Fault fault);
|
||||||
bool HasFault();
|
bool HasFault();
|
||||||
wbo::Fault GetCurrentFault();
|
wbo::Fault GetCurrentFault(int ch);
|
||||||
|
|
|
@ -13,15 +13,74 @@
|
||||||
using namespace wbo;
|
using namespace wbo;
|
||||||
|
|
||||||
// 400khz / 1024 = 390hz PWM
|
// 400khz / 1024 = 390hz PWM
|
||||||
static Pwm heaterPwm(HEATER_PWM_DEVICE, HEATER_PWM_CHANNEL, 400'000, 1024);
|
static Pwm heaterPwm(HEATER_PWM_DEVICE);
|
||||||
|
PWMConfig heaterPwmConfig = {
|
||||||
|
400'000,
|
||||||
|
1024,
|
||||||
|
nullptr,
|
||||||
|
{
|
||||||
|
{PWM_OUTPUT_ACTIVE_HIGH, nullptr},
|
||||||
|
{PWM_OUTPUT_ACTIVE_HIGH, nullptr},
|
||||||
|
{PWM_OUTPUT_ACTIVE_HIGH, nullptr},
|
||||||
|
{PWM_OUTPUT_ACTIVE_HIGH, nullptr}
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
#if STM32_PWM_USE_ADVANCED
|
||||||
|
0
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
static constexpr int preheatTimeCounter = HEATER_PREHEAT_TIME / HEATER_CONTROL_PERIOD;
|
static constexpr int preheatTimeCounter = HEATER_PREHEAT_TIME / HEATER_CONTROL_PERIOD;
|
||||||
static constexpr int batteryStabTimeCounter = HEATER_BATTERY_STAB_TIME / HEATER_CONTROL_PERIOD;
|
static constexpr int batteryStabTimeCounter = HEATER_BATTERY_STAB_TIME / HEATER_CONTROL_PERIOD;
|
||||||
static int timeCounter = preheatTimeCounter;
|
|
||||||
static int batteryStabTime = batteryStabTimeCounter;
|
|
||||||
static float rampVoltage = 0;
|
|
||||||
|
|
||||||
static HeaterState GetNextState(HeaterState state, HeaterAllow heaterAllowState, float batteryVoltage, float sensorEsr)
|
struct heater_state {
|
||||||
|
Pid heaterPid;
|
||||||
|
int timeCounter;
|
||||||
|
int batteryStabTime;
|
||||||
|
float rampVoltage;
|
||||||
|
HeaterState heaterState;
|
||||||
|
uint8_t ch;
|
||||||
|
uint8_t pwm_ch;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct heater_state state[AFR_CHANNELS] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
.heaterPid = Pid(
|
||||||
|
0.3f, // kP
|
||||||
|
0.3f, // kI
|
||||||
|
0.01f, // kD
|
||||||
|
3.0f, // Integrator clamp (volts)
|
||||||
|
HEATER_CONTROL_PERIOD
|
||||||
|
),
|
||||||
|
.timeCounter = preheatTimeCounter,
|
||||||
|
.batteryStabTime = batteryStabTimeCounter,
|
||||||
|
.rampVoltage = 0,
|
||||||
|
.heaterState = HeaterState::Preheat,
|
||||||
|
.ch = 0,
|
||||||
|
.pwm_ch = HEATER_PWM_CHANNEL_0,
|
||||||
|
},
|
||||||
|
#if (AFR_CHANNELS > 1)
|
||||||
|
{
|
||||||
|
.heaterPid = Pid(
|
||||||
|
0.3f, // kP
|
||||||
|
0.3f, // kI
|
||||||
|
0.01f, // kD
|
||||||
|
3.0f, // Integrator clamp (volts)
|
||||||
|
HEATER_CONTROL_PERIOD
|
||||||
|
),
|
||||||
|
.timeCounter = preheatTimeCounter,
|
||||||
|
.batteryStabTime = batteryStabTimeCounter,
|
||||||
|
.rampVoltage = 0,
|
||||||
|
.heaterState = HeaterState::Preheat,
|
||||||
|
.ch = 1,
|
||||||
|
.pwm_ch = HEATER_PWM_CHANNEL_1,
|
||||||
|
},
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static HeaterState GetNextState(struct heater_state &s, HeaterAllow heaterAllowState, float batteryVoltage, float sensorEsr)
|
||||||
{
|
{
|
||||||
bool heaterAllowed = heaterAllowState == HeaterAllow::Allowed;
|
bool heaterAllowed = heaterAllowState == HeaterAllow::Allowed;
|
||||||
|
|
||||||
|
@ -31,37 +90,37 @@ static HeaterState GetNextState(HeaterState state, HeaterAllow heaterAllowState,
|
||||||
// measured voltage too low to auto-start heating
|
// measured voltage too low to auto-start heating
|
||||||
if (batteryVoltage < HEATER_BATTETY_OFF_VOLTAGE)
|
if (batteryVoltage < HEATER_BATTETY_OFF_VOLTAGE)
|
||||||
{
|
{
|
||||||
batteryStabTime = batteryStabTimeCounter;
|
s.batteryStabTime = batteryStabTimeCounter;
|
||||||
}
|
}
|
||||||
// measured voltage is high enougth to auto-start heating, wait some time to stabilize
|
// measured voltage is high enougth to auto-start heating, wait some time to stabilize
|
||||||
if ((batteryVoltage > HEATER_BATTERY_ON_VOLTAGE) && (batteryStabTime > 0))
|
if ((batteryVoltage > HEATER_BATTERY_ON_VOLTAGE) && (s.batteryStabTime > 0))
|
||||||
{
|
{
|
||||||
batteryStabTime--;
|
s.batteryStabTime--;
|
||||||
}
|
}
|
||||||
heaterAllowed = batteryStabTime == 0;
|
heaterAllowed = s.batteryStabTime == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!heaterAllowed)
|
if (!heaterAllowed)
|
||||||
{
|
{
|
||||||
// ECU hasn't allowed preheat yet, reset timer, and force preheat state
|
// ECU hasn't allowed preheat yet, reset timer, and force preheat state
|
||||||
timeCounter = preheatTimeCounter;
|
s.timeCounter = preheatTimeCounter;
|
||||||
return HeaterState::Preheat;
|
return HeaterState::Preheat;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (state)
|
switch (s.heaterState)
|
||||||
{
|
{
|
||||||
case HeaterState::Preheat:
|
case HeaterState::Preheat:
|
||||||
timeCounter--;
|
s.timeCounter--;
|
||||||
|
|
||||||
// If preheat timeout, or sensor is already hot (engine running?)
|
// If preheat timeout, or sensor is already hot (engine running?)
|
||||||
if (timeCounter <= 0 || sensorEsr < HEATER_CLOSED_LOOP_THRESHOLD_ESR)
|
if (s.timeCounter <= 0 || sensorEsr < HEATER_CLOSED_LOOP_THRESHOLD_ESR)
|
||||||
{
|
{
|
||||||
// If enough time has elapsed, start the ramp
|
// If enough time has elapsed, start the ramp
|
||||||
// Start the ramp at 4 volts
|
// Start the ramp at 4 volts
|
||||||
rampVoltage = 4;
|
s.rampVoltage = 4;
|
||||||
|
|
||||||
// Next phase times out at 15 seconds
|
// Next phase times out at 15 seconds
|
||||||
timeCounter = HEATER_WARMUP_TIMEOUT / HEATER_CONTROL_PERIOD;
|
s.timeCounter = HEATER_WARMUP_TIMEOUT / HEATER_CONTROL_PERIOD;
|
||||||
|
|
||||||
return HeaterState::WarmupRamp;
|
return HeaterState::WarmupRamp;
|
||||||
}
|
}
|
||||||
|
@ -73,25 +132,25 @@ static HeaterState GetNextState(HeaterState state, HeaterAllow heaterAllowState,
|
||||||
{
|
{
|
||||||
return HeaterState::ClosedLoop;
|
return HeaterState::ClosedLoop;
|
||||||
}
|
}
|
||||||
else if (timeCounter == 0)
|
else if (s.timeCounter == 0)
|
||||||
{
|
{
|
||||||
SetFault(Fault::SensorDidntHeat);
|
SetFault(s.ch, Fault::SensorDidntHeat);
|
||||||
return HeaterState::Stopped;
|
return HeaterState::Stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
timeCounter--;
|
s.timeCounter--;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case HeaterState::ClosedLoop:
|
case HeaterState::ClosedLoop:
|
||||||
// Check that the sensor's ESR is acceptable for normal operation
|
// Check that the sensor's ESR is acceptable for normal operation
|
||||||
if (sensorEsr < HEATER_OVERHEAT_ESR)
|
if (sensorEsr < HEATER_OVERHEAT_ESR)
|
||||||
{
|
{
|
||||||
SetFault(Fault::SensorOverheat);
|
SetFault(s.ch, Fault::SensorOverheat);
|
||||||
return HeaterState::Stopped;
|
return HeaterState::Stopped;
|
||||||
}
|
}
|
||||||
else if (sensorEsr > HEATER_UNDERHEAT_ESR)
|
else if (sensorEsr > HEATER_UNDERHEAT_ESR)
|
||||||
{
|
{
|
||||||
SetFault(Fault::SensorUnderheat);
|
SetFault(s.ch, Fault::SensorUnderheat);
|
||||||
return HeaterState::Stopped;
|
return HeaterState::Stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,38 +158,30 @@ static HeaterState GetNextState(HeaterState state, HeaterAllow heaterAllowState,
|
||||||
case HeaterState::Stopped: break;
|
case HeaterState::Stopped: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return s.heaterState;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Pid heaterPid(
|
static float GetVoltageForState(struct heater_state &s, float heaterEsr)
|
||||||
0.3f, // kP
|
|
||||||
0.3f, // kI
|
|
||||||
0.01f, // kD
|
|
||||||
3.0f, // Integrator clamp (volts)
|
|
||||||
HEATER_CONTROL_PERIOD
|
|
||||||
);
|
|
||||||
|
|
||||||
static float GetVoltageForState(HeaterState state, float heaterEsr)
|
|
||||||
{
|
{
|
||||||
switch (state)
|
switch (s.heaterState)
|
||||||
{
|
{
|
||||||
case HeaterState::Preheat:
|
case HeaterState::Preheat:
|
||||||
// Max allowed during condensation phase (preheat) is 2v
|
// Max allowed during condensation phase (preheat) is 2v
|
||||||
return 1.5f;
|
return 1.5f;
|
||||||
case HeaterState::WarmupRamp:
|
case HeaterState::WarmupRamp:
|
||||||
if (rampVoltage < 10)
|
if (s.rampVoltage < 10)
|
||||||
{
|
{
|
||||||
// 0.3 volt per second, divided by battery voltage and update rate
|
// 0.3 volt per second, divided by battery voltage and update rate
|
||||||
constexpr float rampRateVoltPerSecond = 0.3f;
|
constexpr float rampRateVoltPerSecond = 0.3f;
|
||||||
constexpr float heaterFrequency = 1000.0f / HEATER_CONTROL_PERIOD;
|
constexpr float heaterFrequency = 1000.0f / HEATER_CONTROL_PERIOD;
|
||||||
rampVoltage += (rampRateVoltPerSecond / heaterFrequency);
|
s.rampVoltage += (rampRateVoltPerSecond / heaterFrequency);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rampVoltage;
|
return s.rampVoltage;
|
||||||
case HeaterState::ClosedLoop:
|
case HeaterState::ClosedLoop:
|
||||||
// "nominal" heater voltage is 7.5v, so apply correction around that point (instead of relying on integrator so much)
|
// "nominal" heater voltage is 7.5v, so apply correction around that point (instead of relying on integrator so much)
|
||||||
// Negated because lower resistance -> hotter
|
// Negated because lower resistance -> hotter
|
||||||
return 7.5f - heaterPid.GetOutput(HEATER_TARGET_ESR, heaterEsr);
|
return 7.5f - s.heaterPid.GetOutput(HEATER_TARGET_ESR, heaterEsr);
|
||||||
case HeaterState::Stopped:
|
case HeaterState::Stopped:
|
||||||
// Something has gone wrong, turn off the heater.
|
// Something has gone wrong, turn off the heater.
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -140,57 +191,60 @@ static float GetVoltageForState(HeaterState state, float heaterEsr)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HeaterState state = HeaterState::Preheat;
|
|
||||||
|
|
||||||
|
|
||||||
static THD_WORKING_AREA(waHeaterThread, 256);
|
static THD_WORKING_AREA(waHeaterThread, 256);
|
||||||
static void HeaterThread(void*)
|
static void HeaterThread(void*)
|
||||||
{
|
{
|
||||||
|
int ch;
|
||||||
|
|
||||||
// Wait for temperature sensing to stabilize so we don't
|
// Wait for temperature sensing to stabilize so we don't
|
||||||
// immediately think we overshot the target temperature
|
// immediately think we overshot the target temperature
|
||||||
chThdSleepMilliseconds(1000);
|
chThdSleepMilliseconds(1000);
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
// Read sensor state
|
|
||||||
float heaterEsr = GetSensorInternalResistance();
|
|
||||||
|
|
||||||
auto heaterAllowState = GetHeaterAllowed();
|
auto heaterAllowState = GetHeaterAllowed();
|
||||||
|
|
||||||
// If we haven't heard from rusEFI, use the internally sensed
|
for (ch = 0; ch < AFR_CHANNELS; ch++) {
|
||||||
// battery voltage instead of voltage over CAN.
|
heater_state &s = state[ch];
|
||||||
float batteryVoltage = heaterAllowState == HeaterAllow::Unknown
|
|
||||||
? GetInternalBatteryVoltage()
|
|
||||||
: GetRemoteBatteryVoltage();
|
|
||||||
|
|
||||||
// Run the state machine
|
// Read sensor state
|
||||||
state = GetNextState(state, heaterAllowState, batteryVoltage, heaterEsr);
|
float heaterEsr = GetSensorInternalResistance(ch);
|
||||||
float heaterVoltage = GetVoltageForState(state, heaterEsr);
|
|
||||||
|
|
||||||
// Limit to 11 volts
|
// If we haven't heard from rusEFI, use the internally sensed
|
||||||
if (heaterVoltage > 11) {
|
// battery voltage instead of voltage over CAN.
|
||||||
heaterVoltage = 11;
|
float batteryVoltage = heaterAllowState == HeaterAllow::Unknown
|
||||||
}
|
? GetInternalBatteryVoltage(ch)
|
||||||
|
: GetRemoteBatteryVoltage();
|
||||||
|
|
||||||
// duty = (V_eff / V_batt) ^ 2
|
// Run the state machine
|
||||||
float voltageRatio = heaterVoltage / batteryVoltage;
|
s.heaterState = GetNextState(s, heaterAllowState, batteryVoltage, heaterEsr);
|
||||||
float duty = voltageRatio * voltageRatio;
|
float heaterVoltage = GetVoltageForState(s, heaterEsr);
|
||||||
|
|
||||||
#ifdef HEATER_MAX_DUTY
|
// Limit to 11 volts
|
||||||
if (duty > HEATER_MAX_DUTY) {
|
if (heaterVoltage > 11) {
|
||||||
duty = HEATER_MAX_DUTY;
|
heaterVoltage = 11;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (batteryVoltage < 23)
|
// duty = (V_eff / V_batt) ^ 2
|
||||||
{
|
float voltageRatio = heaterVoltage / batteryVoltage;
|
||||||
// Pipe the output to the heater driver
|
float duty = voltageRatio * voltageRatio;
|
||||||
heaterPwm.SetDuty(duty);
|
|
||||||
}
|
#ifdef HEATER_MAX_DUTY
|
||||||
else
|
if (duty > HEATER_MAX_DUTY) {
|
||||||
{
|
duty = HEATER_MAX_DUTY;
|
||||||
// Overvoltage protection - sensor not rated for PWM above 24v
|
}
|
||||||
heaterPwm.SetDuty(0);
|
#endif
|
||||||
|
|
||||||
|
if (batteryVoltage < 23)
|
||||||
|
{
|
||||||
|
// Pipe the output to the heater driver
|
||||||
|
heaterPwm.SetDuty(s.pwm_ch, duty);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Overvoltage protection - sensor not rated for PWM above 24v
|
||||||
|
heaterPwm.SetDuty(s.pwm_ch, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop at ~20hz
|
// Loop at ~20hz
|
||||||
|
@ -200,25 +254,28 @@ static void HeaterThread(void*)
|
||||||
|
|
||||||
void StartHeaterControl()
|
void StartHeaterControl()
|
||||||
{
|
{
|
||||||
heaterPwm.Start();
|
heaterPwm.Start(heaterPwmConfig);
|
||||||
heaterPwm.SetDuty(0);
|
heaterPwm.SetDuty(state[0].pwm_ch, 0);
|
||||||
|
#if (AFR_CHANNELS > 1)
|
||||||
|
heaterPwm.SetDuty(state[1].pwm_ch, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
chThdCreateStatic(waHeaterThread, sizeof(waHeaterThread), NORMALPRIO + 1, HeaterThread, nullptr);
|
chThdCreateStatic(waHeaterThread, sizeof(waHeaterThread), NORMALPRIO + 1, HeaterThread, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsRunningClosedLoop()
|
bool IsRunningClosedLoop(int ch)
|
||||||
{
|
{
|
||||||
return state == HeaterState::ClosedLoop;
|
return state[ch].heaterState == HeaterState::ClosedLoop;
|
||||||
}
|
}
|
||||||
|
|
||||||
float GetHeaterDuty()
|
float GetHeaterDuty(int ch)
|
||||||
{
|
{
|
||||||
return heaterPwm.GetLastDuty();
|
return heaterPwm.GetLastDuty(state[ch].pwm_ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaterState GetHeaterState()
|
HeaterState GetHeaterState(int ch)
|
||||||
{
|
{
|
||||||
return state;
|
return state[ch].heaterState;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* describeHeaterState(HeaterState state)
|
const char* describeHeaterState(HeaterState state)
|
||||||
|
|
|
@ -11,7 +11,7 @@ enum class HeaterState
|
||||||
};
|
};
|
||||||
|
|
||||||
void StartHeaterControl();
|
void StartHeaterControl();
|
||||||
bool IsRunningClosedLoop();
|
bool IsRunningClosedLoop(int ch);
|
||||||
float GetHeaterDuty();
|
float GetHeaterDuty(int ch);
|
||||||
HeaterState GetHeaterState();
|
HeaterState GetHeaterState(int ch);
|
||||||
const char* describeHeaterState(HeaterState state);
|
const char* describeHeaterState(HeaterState state);
|
||||||
|
|
|
@ -22,9 +22,9 @@ static float GetPhi(float pumpCurrent)
|
||||||
return gain * pumpCurrent + 0.99559f;
|
return gain * pumpCurrent + 0.99559f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float GetLambda()
|
float GetLambda(int ch)
|
||||||
{
|
{
|
||||||
float pumpCurrent = GetPumpNominalCurrent();
|
float pumpCurrent = GetPumpNominalCurrent(ch);
|
||||||
|
|
||||||
// Lambda is reciprocal of phi
|
// Lambda is reciprocal of phi
|
||||||
return 1 / GetPhi(pumpCurrent);
|
return 1 / GetPhi(pumpCurrent);
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
float GetLambda();
|
float GetLambda(int ch);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include "wideband_config.h"
|
||||||
#include "livedata.h"
|
#include "livedata.h"
|
||||||
|
|
||||||
#include "lambda_conversion.h"
|
#include "lambda_conversion.h"
|
||||||
|
@ -9,18 +10,23 @@
|
||||||
#include <rusefi/fragments.h>
|
#include <rusefi/fragments.h>
|
||||||
|
|
||||||
static livedata_common_s livedata_common;
|
static livedata_common_s livedata_common;
|
||||||
static livedata_afr_s livedata_afr;
|
static livedata_afr_s livedata_afr[AFR_CHANNELS];
|
||||||
|
|
||||||
void SamplingUpdateLiveData()
|
void SamplingUpdateLiveData()
|
||||||
{
|
{
|
||||||
livedata_afr.afr = GetLambda();
|
for (int ch = 0; ch < AFR_CHANNELS; ch++)
|
||||||
livedata_afr.temperature = GetSensorTemperature();
|
{
|
||||||
livedata_afr.nernstVoltage = GetNernstDc();
|
volatile struct livedata_afr_s *data = &livedata_afr[ch];
|
||||||
livedata_afr.pumpCurrentTarget = GetPumpCurrent();
|
|
||||||
livedata_afr.pumpCurrentMeasured = GetPumpNominalCurrent();
|
|
||||||
livedata_afr.heaterDuty = GetHeaterDuty();
|
|
||||||
|
|
||||||
livedata_common.vbatt = GetInternalBatteryVoltage();
|
data->afr = GetLambda(ch);
|
||||||
|
data->temperature = GetSensorTemperature(ch);
|
||||||
|
data->nernstVoltage = GetNernstDc(ch);
|
||||||
|
data->pumpCurrentTarget = GetPumpCurrent(ch);
|
||||||
|
data->pumpCurrentMeasured = GetPumpNominalCurrent(ch);
|
||||||
|
data->heaterDuty = GetHeaterDuty(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
livedata_common.vbatt = GetInternalBatteryVoltage(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const livedata_common_s * getCommonLiveDataStructAddr()
|
const livedata_common_s * getCommonLiveDataStructAddr()
|
||||||
|
@ -28,14 +34,16 @@ const livedata_common_s * getCommonLiveDataStructAddr()
|
||||||
return &livedata_common;
|
return &livedata_common;
|
||||||
}
|
}
|
||||||
|
|
||||||
const livedata_afr_s * getAfrLiveDataStructAddr()
|
const struct livedata_afr_s * getAfrLiveDataStructAddr(const int ch)
|
||||||
{
|
{
|
||||||
return &livedata_afr;
|
if (ch < AFR_CHANNELS)
|
||||||
|
return &livedata_afr[ch];
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const FragmentEntry fragments[] = {
|
static const FragmentEntry fragments[] = {
|
||||||
getCommonLiveDataStructAddr(),
|
getCommonLiveDataStructAddr(),
|
||||||
getAfrLiveDataStructAddr(),
|
getAfrLiveDataStructAddr(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
FragmentList getFragments() {
|
FragmentList getFragments() {
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "wideband_config.h"
|
||||||
|
|
||||||
/* +0 offset */
|
/* +0 offset */
|
||||||
struct livedata_common_s {
|
struct livedata_common_s {
|
||||||
union {
|
union {
|
||||||
|
@ -33,4 +35,4 @@ void SamplingUpdateLiveData();
|
||||||
|
|
||||||
/* access functions */
|
/* access functions */
|
||||||
const struct livedata_common_s * getCommonLiveDataStructAddr();
|
const struct livedata_common_s * getCommonLiveDataStructAddr();
|
||||||
const struct livedata_afr_s * getAfrLiveDataStructAddr();
|
const struct livedata_afr_s * getAfrLiveDataStructAddr(const int ch);
|
||||||
|
|
|
@ -42,7 +42,9 @@ int main() {
|
||||||
|
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
auto fault = GetCurrentFault();
|
/* TODO: show error for all AFR channels */
|
||||||
|
/* TODO: show EGT errors */
|
||||||
|
auto fault = GetCurrentFault(0);
|
||||||
|
|
||||||
if (fault == Fault::None)
|
if (fault == Fault::None)
|
||||||
{
|
{
|
||||||
|
@ -53,7 +55,7 @@ int main() {
|
||||||
palTogglePad(LED_GREEN_PORT, LED_GREEN_PIN);
|
palTogglePad(LED_GREEN_PORT, LED_GREEN_PIN);
|
||||||
|
|
||||||
// Slow blink if closed loop, fast if not
|
// Slow blink if closed loop, fast if not
|
||||||
chThdSleepMilliseconds(IsRunningClosedLoop() ? 700 : 50);
|
chThdSleepMilliseconds(IsRunningClosedLoop(0) ? 700 : 50);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,28 +7,46 @@
|
||||||
|
|
||||||
#include "ch.h"
|
#include "ch.h"
|
||||||
|
|
||||||
// Bosch CJ125 is somewhere VERY ROUGHLY like 200-400A/(v*s) integrator gain
|
struct pump_control_state {
|
||||||
static Pid pumpPid(50.0f, 10000.0f, 0, 10, 2);
|
Pid pumpPid;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct pump_control_state state[AFR_CHANNELS] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Pid(50.0f, 10000.0f, 0.0f, 10.0f, 2),
|
||||||
|
},
|
||||||
|
#if (AFR_CHANNELS > 1)
|
||||||
|
{
|
||||||
|
Pid(50.0f, 10000.0f, 0.0f, 10.0f, 2),
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
static THD_WORKING_AREA(waPumpThread, 256);
|
static THD_WORKING_AREA(waPumpThread, 256);
|
||||||
static void PumpThread(void*)
|
static void PumpThread(void*)
|
||||||
{
|
{
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
// Only actuate pump when running closed loop!
|
for (int ch = 0; ch < AFR_CHANNELS; ch++)
|
||||||
if (IsRunningClosedLoop())
|
|
||||||
{
|
{
|
||||||
float nernstVoltage = GetNernstDc();
|
pump_control_state &s = state[ch];
|
||||||
|
|
||||||
float result = pumpPid.GetOutput(NERNST_TARGET, nernstVoltage);
|
// Only actuate pump when running closed loop!
|
||||||
|
if (IsRunningClosedLoop(ch))
|
||||||
|
{
|
||||||
|
float nernstVoltage = GetNernstDc(ch);
|
||||||
|
|
||||||
// result is in mA
|
float result = s.pumpPid.GetOutput(NERNST_TARGET, nernstVoltage);
|
||||||
SetPumpCurrentTarget(result * 1000);
|
|
||||||
}
|
// result is in mA
|
||||||
else
|
SetPumpCurrentTarget(ch, result * 1000);
|
||||||
{
|
}
|
||||||
// Otherwise set zero pump current to avoid damaging the sensor
|
else
|
||||||
SetPumpCurrentTarget(0);
|
{
|
||||||
|
// Otherwise set zero pump current to avoid damaging the sensor
|
||||||
|
SetPumpCurrentTarget(ch, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run at 500hz
|
// Run at 500hz
|
||||||
|
|
|
@ -7,28 +7,59 @@
|
||||||
#include "hal.h"
|
#include "hal.h"
|
||||||
|
|
||||||
// 48MHz / 1024 = 46.8khz PWM
|
// 48MHz / 1024 = 46.8khz PWM
|
||||||
static Pwm pumpDac(PUMP_DAC_PWM_DEVICE, PUMP_DAC_PWM_CHANNEL, 48'000'000, 1024);
|
PWMConfig pumpDacConfig = {
|
||||||
|
48'000'000,
|
||||||
|
1024,
|
||||||
|
nullptr,
|
||||||
|
{
|
||||||
|
{PWM_OUTPUT_ACTIVE_HIGH, nullptr},
|
||||||
|
{PWM_OUTPUT_ACTIVE_HIGH, nullptr},
|
||||||
|
{PWM_OUTPUT_ACTIVE_HIGH, nullptr},
|
||||||
|
{PWM_OUTPUT_ACTIVE_HIGH, nullptr}
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
#if STM32_PWM_USE_ADVANCED
|
||||||
|
0
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
static int32_t curIpump;
|
static Pwm pumpDac(PUMP_DAC_PWM_DEVICE);
|
||||||
|
|
||||||
|
struct pump_dac_state {
|
||||||
|
int32_t curIpump;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t pumpDacPwmCh[] = {
|
||||||
|
PUMP_DAC_PWM_CHANNEL_0,
|
||||||
|
#if (AFR_CHANNELS > 1)
|
||||||
|
PUMP_DAC_PWM_CHANNEL_1,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct pump_dac_state state[AFR_CHANNELS];
|
||||||
|
|
||||||
void InitPumpDac()
|
void InitPumpDac()
|
||||||
{
|
{
|
||||||
pumpDac.Start();
|
pumpDac.Start(pumpDacConfig);
|
||||||
|
|
||||||
// Set zero current to start - sensor can be damaged if current flowing
|
for (int ch = 0; ch < AFR_CHANNELS; ch++)
|
||||||
// while warming up
|
{
|
||||||
SetPumpCurrentTarget(0);
|
// Set zero current to start - sensor can be damaged if current flowing
|
||||||
|
// while warming up
|
||||||
|
SetPumpCurrentTarget(ch, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetPumpCurrentTarget(int32_t microampere)
|
void SetPumpCurrentTarget(int ch, int32_t microampere)
|
||||||
{
|
{
|
||||||
// Don't allow pump current when the sensor isn't hot
|
// Don't allow pump current when the sensor isn't hot
|
||||||
if (!IsRunningClosedLoop())
|
if (!IsRunningClosedLoop(ch))
|
||||||
{
|
{
|
||||||
microampere = 0;
|
microampere = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
curIpump = microampere;
|
state[ch].curIpump = microampere;
|
||||||
|
|
||||||
// 47 ohm resistor
|
// 47 ohm resistor
|
||||||
// 0.147 gain
|
// 0.147 gain
|
||||||
|
@ -38,15 +69,15 @@ void SetPumpCurrentTarget(int32_t microampere)
|
||||||
// offset by half vcc
|
// offset by half vcc
|
||||||
volts += HALF_VCC;
|
volts += HALF_VCC;
|
||||||
|
|
||||||
pumpDac.SetDuty(volts / VCC_VOLTS);
|
pumpDac.SetDuty(pumpDacPwmCh[ch], volts / VCC_VOLTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
float GetPumpOutputDuty()
|
float GetPumpOutputDuty(int ch)
|
||||||
{
|
{
|
||||||
return pumpDac.GetLastDuty();
|
return pumpDac.GetLastDuty(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
float GetPumpCurrent()
|
float GetPumpCurrent(int ch)
|
||||||
{
|
{
|
||||||
return (float)curIpump / 1000.0;
|
return (float)state[ch].curIpump / 1000.0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
void InitPumpDac();
|
void InitPumpDac();
|
||||||
void SetPumpCurrentTarget(int32_t microamperes);
|
void SetPumpCurrentTarget(int ch, int32_t microamperes);
|
||||||
float GetPumpOutputDuty();
|
float GetPumpOutputDuty(int ch);
|
||||||
float GetPumpCurrent();
|
float GetPumpCurrent(int ch);
|
||||||
|
|
|
@ -2,17 +2,8 @@
|
||||||
|
|
||||||
#include <rusefi/math.h>
|
#include <rusefi/math.h>
|
||||||
|
|
||||||
Pwm::Pwm(PWMDriver& driver, uint8_t channel, uint32_t counterFrequency, uint32_t counterPeriod)
|
|
||||||
: m_driver(&driver)
|
|
||||||
, m_channel(channel)
|
|
||||||
, m_counterFrequency(counterFrequency)
|
|
||||||
, m_counterPeriod(counterPeriod)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Pwm::Pwm(PWMDriver& driver)
|
Pwm::Pwm(PWMDriver& driver)
|
||||||
: m_driver(&driver)
|
: m_driver(&driver)
|
||||||
, m_channel(0)
|
|
||||||
, m_counterFrequency(0)
|
, m_counterFrequency(0)
|
||||||
, m_counterPeriod(0)
|
, m_counterPeriod(0)
|
||||||
{
|
{
|
||||||
|
@ -50,17 +41,13 @@ void Pwm::Start(PWMConfig& config)
|
||||||
|
|
||||||
void Pwm::SetDuty(int channel, float duty) {
|
void Pwm::SetDuty(int channel, float duty) {
|
||||||
auto dutyFloat = clampF(0, duty, 1);
|
auto dutyFloat = clampF(0, duty, 1);
|
||||||
m_dutyFloat = dutyFloat;
|
m_dutyFloat[channel] = dutyFloat;
|
||||||
pwmcnt_t highTime = m_counterPeriod * dutyFloat;
|
pwmcnt_t highTime = m_counterPeriod * dutyFloat;
|
||||||
|
|
||||||
pwm_lld_enable_channel(m_driver, channel, highTime);
|
pwmEnableChannel(m_driver, channel, highTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pwm::SetDuty(float duty) {
|
float Pwm::GetLastDuty(int channel)
|
||||||
SetDuty(m_channel, duty);
|
|
||||||
}
|
|
||||||
|
|
||||||
float Pwm::GetLastDuty() const
|
|
||||||
{
|
{
|
||||||
return m_dutyFloat;
|
return m_dutyFloat[channel];
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,19 +10,17 @@ struct PWMDriver;
|
||||||
class Pwm
|
class Pwm
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Pwm(PWMDriver& driver, uint8_t channel, uint32_t counterFrequency, uint32_t counterPeriod);
|
|
||||||
Pwm(PWMDriver& driver);
|
Pwm(PWMDriver& driver);
|
||||||
|
|
||||||
void Start();
|
void Start();
|
||||||
void Start(PWMConfig& config);
|
void Start(PWMConfig& config);
|
||||||
void SetDuty(float duty);
|
|
||||||
void SetDuty(int channel, float duty);
|
void SetDuty(int channel, float duty);
|
||||||
float GetLastDuty() const;
|
float GetLastDuty(int channel);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PWMDriver* const m_driver;
|
PWMDriver* const m_driver;
|
||||||
const uint8_t m_channel;
|
//const uint8_t m_channel;
|
||||||
/* const */ uint32_t m_counterFrequency;
|
/* const */ uint32_t m_counterFrequency;
|
||||||
/* const */ uint16_t m_counterPeriod;
|
/* const */ uint16_t m_counterPeriod;
|
||||||
float m_dutyFloat;
|
float m_dutyFloat[4];
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,10 +12,14 @@
|
||||||
#include <rusefi/interpolation.h>
|
#include <rusefi/interpolation.h>
|
||||||
|
|
||||||
// Stored results
|
// Stored results
|
||||||
static float nernstAc = 0;
|
struct measure_results {
|
||||||
static float nernstDc = 0;
|
float nernstAc;
|
||||||
static float pumpCurrentSenseVoltage = 0;
|
float nernstDc;
|
||||||
static float internalBatteryVoltage = 0;
|
float pumpCurrentSenseVoltage;
|
||||||
|
float internalBatteryVoltage;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct measure_results results[AFR_CHANNELS];
|
||||||
|
|
||||||
// Last point is approximated by the greatest measurable sensor resistance
|
// Last point is approximated by the greatest measurable sensor resistance
|
||||||
static const float lsu49TempBins[] = { 80, 150, 200, 250, 300, 350, 400, 450, 550, 650, 800, 1000, 1200, 2500, 5000 };
|
static const float lsu49TempBins[] = { 80, 150, 200, 250, 300, 350, 400, 450, 550, 650, 800, 1000, 1200, 2500, 5000 };
|
||||||
|
@ -30,51 +34,51 @@ static THD_WORKING_AREA(waSamplingThread, 256);
|
||||||
|
|
||||||
static void SamplingThread(void*)
|
static void SamplingThread(void*)
|
||||||
{
|
{
|
||||||
float r_2 = 0;
|
float r_2[AFR_CHANNELS] = {0};
|
||||||
float r_3 = 0;
|
float r_3[AFR_CHANNELS] = {0};
|
||||||
|
|
||||||
/* GD32: Insert 20us delay after ADC enable */
|
/* GD32: Insert 20us delay after ADC enable */
|
||||||
chThdSleepMilliseconds(1);
|
chThdSleepMilliseconds(1);
|
||||||
|
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
/* TODO: run for all channels */
|
|
||||||
int ch = 0;
|
|
||||||
|
|
||||||
auto result = AnalogSample();
|
auto result = AnalogSample();
|
||||||
|
|
||||||
// Toggle the pin after sampling so that any switching noise occurs while we're doing our math instead of when sampling
|
// Toggle the pin after sampling so that any switching noise occurs while we're doing our math instead of when sampling
|
||||||
palTogglePad(NERNST_ESR_DRIVER_PORT, NERNST_ESR_DRIVER_PIN);
|
palTogglePad(NERNST_ESR_DRIVER_PORT, NERNST_ESR_DRIVER_PIN);
|
||||||
|
|
||||||
float r_1 = result.ch[ch].NernstVoltage;
|
for (int ch = 0; ch < AFR_CHANNELS; ch++) {
|
||||||
|
measure_results &res = results[ch];
|
||||||
|
float r_1 = result.ch[ch].NernstVoltage;
|
||||||
|
|
||||||
// r2_opposite_phase estimates where the previous sample would be had we not been toggling
|
// r2_opposite_phase estimates where the previous sample would be had we not been toggling
|
||||||
// AKA the absolute value of the difference between r2_opposite_phase and r2 is the amplitude
|
// AKA the absolute value of the difference between r2_opposite_phase and r2 is the amplitude
|
||||||
// of the AC component on the nernst voltage. We have to pull this trick so as to use the past 3
|
// of the AC component on the nernst voltage. We have to pull this trick so as to use the past 3
|
||||||
// samples to cancel out any slope in the DC (aka actual nernst cell output) from the AC measurement
|
// samples to cancel out any slope in the DC (aka actual nernst cell output) from the AC measurement
|
||||||
// See firmware/sampling.png for a drawing of what's going on here
|
// See firmware/sampling.png for a drawing of what's going on here
|
||||||
float r2_opposite_phase = (r_1 + r_3) / 2;
|
float r2_opposite_phase = (r_1 + r_3[ch]) / 2;
|
||||||
|
|
||||||
// Compute AC (difference) and DC (average) components
|
// Compute AC (difference) and DC (average) components
|
||||||
float nernstAcLocal = f_abs(r2_opposite_phase - r_2);
|
float nernstAcLocal = f_abs(r2_opposite_phase - r_2[ch]);
|
||||||
nernstDc = (r2_opposite_phase + r_2) / 2;
|
res.nernstDc = (r2_opposite_phase + r_2[ch]) / 2;
|
||||||
|
|
||||||
nernstAc =
|
res.nernstAc =
|
||||||
(1 - ESR_SENSE_ALPHA) * nernstAc +
|
(1 - ESR_SENSE_ALPHA) * res.nernstAc +
|
||||||
ESR_SENSE_ALPHA * nernstAcLocal;
|
ESR_SENSE_ALPHA * nernstAcLocal;
|
||||||
|
|
||||||
// Exponential moving average (aka first order lpf)
|
// Exponential moving average (aka first order lpf)
|
||||||
pumpCurrentSenseVoltage =
|
res.pumpCurrentSenseVoltage =
|
||||||
(1 - PUMP_FILTER_ALPHA) * pumpCurrentSenseVoltage +
|
(1 - PUMP_FILTER_ALPHA) * res.pumpCurrentSenseVoltage +
|
||||||
PUMP_FILTER_ALPHA * (result.ch[ch].PumpCurrentVoltage - result.VirtualGroundVoltageInt);
|
PUMP_FILTER_ALPHA * (result.ch[ch].PumpCurrentVoltage - result.VirtualGroundVoltageInt);
|
||||||
|
|
||||||
#ifdef BATTERY_INPUT_DIVIDER
|
#ifdef BATTERY_INPUT_DIVIDER
|
||||||
internalBatteryVoltage = result.ch[ch].BatteryVoltage;
|
res.internalBatteryVoltage = result.ch[ch].BatteryVoltage;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Shift history over by one
|
// Shift history over by one
|
||||||
r_3 = r_2;
|
r_3[ch] = r_2[ch];
|
||||||
r_2 = r_1;
|
r_2[ch] = r_1;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(TS_ENABLED)
|
#if defined(TS_ENABLED)
|
||||||
/* tunerstudio */
|
/* tunerstudio */
|
||||||
|
@ -89,24 +93,24 @@ void StartSampling()
|
||||||
chThdCreateStatic(waSamplingThread, sizeof(waSamplingThread), NORMALPRIO + 5, SamplingThread, nullptr);
|
chThdCreateStatic(waSamplingThread, sizeof(waSamplingThread), NORMALPRIO + 5, SamplingThread, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
float GetNernstAc()
|
float GetNernstAc(int ch)
|
||||||
{
|
{
|
||||||
return nernstAc;
|
return results[ch].nernstAc;
|
||||||
}
|
}
|
||||||
|
|
||||||
float GetSensorInternalResistance()
|
float GetSensorInternalResistance(int ch)
|
||||||
{
|
{
|
||||||
// Sensor is the lowside of a divider, top side is 22k, and 3.3v AC pk-pk is injected
|
// Sensor is the lowside of a divider, top side is 22k, and 3.3v AC pk-pk is injected
|
||||||
float totalEsr = ESR_SUPPLY_R / (VCC_VOLTS / GetNernstAc() - 1);
|
float totalEsr = ESR_SUPPLY_R / (VCC_VOLTS / GetNernstAc(ch) - 1);
|
||||||
|
|
||||||
// There is a resistor between the opamp and Vm sensor pin. Remove the effect of that
|
// There is a resistor between the opamp and Vm sensor pin. Remove the effect of that
|
||||||
// resistor so that the remainder is only the ESR of the sensor itself
|
// resistor so that the remainder is only the ESR of the sensor itself
|
||||||
return totalEsr - VM_RESISTOR_VALUE;
|
return totalEsr - VM_RESISTOR_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
float GetSensorTemperature()
|
float GetSensorTemperature(int ch)
|
||||||
{
|
{
|
||||||
float esr = GetSensorInternalResistance();
|
float esr = GetSensorInternalResistance(ch);
|
||||||
|
|
||||||
if (esr > 5000)
|
if (esr > 5000)
|
||||||
{
|
{
|
||||||
|
@ -116,21 +120,24 @@ float GetSensorTemperature()
|
||||||
return interpolate2d(esr, lsu49TempBins, lsu49TempValues);
|
return interpolate2d(esr, lsu49TempBins, lsu49TempValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
float GetNernstDc()
|
float GetNernstDc(int ch)
|
||||||
{
|
{
|
||||||
return nernstDc;
|
return results[ch].nernstDc;
|
||||||
}
|
}
|
||||||
|
|
||||||
float GetPumpNominalCurrent()
|
float GetPumpNominalCurrent(int ch)
|
||||||
{
|
{
|
||||||
// Gain is 10x, then a 61.9 ohm resistor
|
// Gain is 10x, then a 61.9 ohm resistor
|
||||||
// Effective resistance with the gain is 619 ohms
|
// Effective resistance with the gain is 619 ohms
|
||||||
// 1000 is to convert to milliamperes
|
// 1000 is to convert to milliamperes
|
||||||
constexpr float ratio = -1000 / (PUMP_CURRENT_SENSE_GAIN * LSU_SENSE_R);
|
constexpr float ratio = -1000 / (PUMP_CURRENT_SENSE_GAIN * LSU_SENSE_R);
|
||||||
return pumpCurrentSenseVoltage * ratio;
|
return results[ch].pumpCurrentSenseVoltage * ratio;
|
||||||
}
|
}
|
||||||
|
|
||||||
float GetInternalBatteryVoltage()
|
float GetInternalBatteryVoltage(int ch)
|
||||||
{
|
{
|
||||||
return internalBatteryVoltage;
|
// Dual HW can measure heater voltage for each channel
|
||||||
|
// by measuring voltage on Heater- while FET is off
|
||||||
|
// TODO: rename function?
|
||||||
|
return results[ch].internalBatteryVoltage;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,9 @@
|
||||||
|
|
||||||
void StartSampling();
|
void StartSampling();
|
||||||
|
|
||||||
float GetNernstAc();
|
float GetNernstAc(int ch);
|
||||||
float GetSensorInternalResistance();
|
float GetSensorInternalResistance(int ch);
|
||||||
float GetSensorTemperature();
|
float GetSensorTemperature(int ch);
|
||||||
float GetNernstDc();
|
float GetNernstDc(int ch);
|
||||||
|
float GetPumpNominalCurrent(int ch);
|
||||||
float GetPumpNominalCurrent();
|
float GetInternalBatteryVoltage(int ch);
|
||||||
|
|
||||||
float GetInternalBatteryVoltage();
|
|
||||||
|
|
|
@ -33,33 +33,41 @@ static void UartThread(void*)
|
||||||
|
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
float lambda = GetLambda();
|
int ch;
|
||||||
int lambdaIntPart = lambda;
|
|
||||||
int lambdaThousandths = (lambda - lambdaIntPart) * 1000;
|
|
||||||
int batteryVoltageMv = GetInternalBatteryVoltage() * 1000;
|
|
||||||
int duty = GetHeaterDuty() * 100;
|
|
||||||
|
|
||||||
size_t writeCount = chsnprintf(printBuffer, 200,
|
for (ch = 0; ch < AFR_CHANNELS; ch++) {
|
||||||
"%d.%03d\tAC %d mV\tR: %d\tT: %d\tIpump: %d\tVbat: %d\theater: %s (%d)\tfault: %s\r\n",
|
float lambda = GetLambda(ch);
|
||||||
lambdaIntPart, lambdaThousandths,
|
int lambdaIntPart = lambda;
|
||||||
(int)(GetNernstAc() * 1000.0),
|
int lambdaThousandths = (lambda - lambdaIntPart) * 1000;
|
||||||
(int)GetSensorInternalResistance(),
|
int batteryVoltageMv = GetInternalBatteryVoltage(ch) * 1000;
|
||||||
(int)GetSensorTemperature(),
|
int duty = GetHeaterDuty(ch) * 100;
|
||||||
(int)(GetPumpNominalCurrent() * 1000),
|
|
||||||
batteryVoltageMv,
|
size_t writeCount = chsnprintf(printBuffer, 200,
|
||||||
describeHeaterState(GetHeaterState()), duty,
|
"[AFR%d]: %d.%03d DC %4d mV AC %4d mV Rint: %5d T: %4d C Ipump: %6d uA Vheater: %5d heater: %s (%d)\tfault: %s\r\n",
|
||||||
describeFault(GetCurrentFault()));
|
ch,
|
||||||
chnWrite(&SD1, (const uint8_t *)printBuffer, writeCount);
|
lambdaIntPart, lambdaThousandths,
|
||||||
chThdSleepMilliseconds(50);
|
(int)(GetNernstDc(ch) * 1000.0),
|
||||||
|
(int)(GetNernstAc(ch) * 1000.0),
|
||||||
|
(int)GetSensorInternalResistance(ch),
|
||||||
|
(int)GetSensorTemperature(ch),
|
||||||
|
(int)(GetPumpNominalCurrent(ch) * 1000),
|
||||||
|
batteryVoltageMv,
|
||||||
|
describeHeaterState(GetHeaterState(ch)), duty,
|
||||||
|
describeFault(GetCurrentFault(ch)));
|
||||||
|
chnWrite(&SD1, (const uint8_t *)printBuffer, writeCount);
|
||||||
|
}
|
||||||
|
|
||||||
#if HAL_USE_SPI
|
#if HAL_USE_SPI
|
||||||
writeCount = chsnprintf(printBuffer, 200,
|
for (ch = 0; ch < EGT_CHANNELS; ch++) {
|
||||||
"EGT: %d C (int %d C)\r\n",
|
size_t writeCount = chsnprintf(printBuffer, 200,
|
||||||
(int)getEgtDrivers()[0].temperature,
|
"EGT[%d]: %d C (int %d C)\r\n",
|
||||||
(int)getEgtDrivers()[0].cold_joint_temperature);
|
(int)getEgtDrivers()[ch].temperature,
|
||||||
chnWrite(&SD1, (const uint8_t *)printBuffer, writeCount);
|
(int)getEgtDrivers()[ch].cold_joint_temperature);
|
||||||
chThdSleepMilliseconds(50);
|
chnWrite(&SD1, (const uint8_t *)printBuffer, writeCount);
|
||||||
|
}
|
||||||
#endif /* HAL_USE_SPI */
|
#endif /* HAL_USE_SPI */
|
||||||
|
|
||||||
|
chThdSleepMilliseconds(100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue