Skip to content

Commit 7ccfdd8

Browse files
xtitorisdron0gus
authored andcommitted
Add heater control parameters to TunerStudio
1 parent 82aa512 commit 7ccfdd8

10 files changed

Lines changed: 599 additions & 30 deletions

File tree

firmware/boards/port.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include "port_shared.h"
77
#include "wideband_config.h"
8+
#include "heater_control.h"
89

910
struct AnalogChannelResult
1011
{
@@ -57,7 +58,7 @@ class Configuration {
5758
private:
5859
// Increment this any time the configuration format changes
5960
// It is stored along with the data to ensure that it has been written before
60-
static constexpr uint32_t ExpectedTag = 0xDEADBE02;
61+
static constexpr uint32_t ExpectedTag = 0xDEADBE03;
6162
uint32_t Tag = ExpectedTag;
6263

6364
public:
@@ -107,6 +108,10 @@ class Configuration {
107108
egt[i].AemNetIdOffset = i;
108109
}
109110

111+
heaterConfig.HeaterSupplyOffVoltage = HEATER_SUPPLY_OFF_VOLTAGE;
112+
heaterConfig.HeaterSupplyOnVoltage = HEATER_SUPPLY_ON_VOLTAGE;
113+
heaterConfig.PreheatTimeSec = HEATER_PREHEAT_TIME;
114+
110115
/* Finaly */
111116
Tag = ExpectedTag;
112117
}
@@ -143,12 +148,15 @@ class Configuration {
143148
uint8_t AemNetIdOffset;
144149
uint8_t pad[5];
145150
} egt[2];
151+
152+
struct HeaterConfig heaterConfig;
146153
} __attribute__((packed));
147154

148155
// pad to 256 bytes including tag
149156
uint8_t pad[256 - sizeof(Tag)];
150157
};
151158
};
159+
static_assert(sizeof(Configuration) == 256, "Configuration size incorrect");
152160

153161
int InitConfiguration();
154162
Configuration* GetConfiguration();
@@ -170,4 +178,4 @@ extern "C" void checkDfuAndJump();
170178
SensorType GetSensorType();
171179
void SetupESRDriver(SensorType sensor);
172180
void ToggleESRDriver(SensorType sensor);
173-
int GetESRSupplyR();
181+
int GetESRSupplyR();

firmware/heater_control.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include "fault.h"
44
#include "sampling.h"
55

6+
#include "port.h"
7+
68
using namespace wbo;
79

810
static const PidConfig heaterPidConfig =
@@ -13,18 +15,17 @@ static const PidConfig heaterPidConfig =
1315
.clamp = 3.0f, // Integrator clamp (volts)
1416
};
1517

16-
HeaterControllerBase::HeaterControllerBase(int ch, int preheatTimeSec, int warmupTimeSec)
18+
HeaterControllerBase::HeaterControllerBase(int ch)
1719
: m_pid(heaterPidConfig, HEATER_CONTROL_PERIOD)
1820
, ch(ch)
19-
, m_preheatTimeSec(preheatTimeSec)
20-
, m_warmupTimeSec(warmupTimeSec)
2121
{
2222
}
2323

24-
void HeaterControllerBase::Configure(float targetTempC, float targetEsr)
24+
void HeaterControllerBase::Configure(float targetTempC, float targetEsr, struct HeaterConfig* configuration)
2525
{
2626
m_targetTempC = targetTempC;
2727
m_targetEsr = targetEsr;
28+
m_configuration = configuration;
2829

2930
m_preheatTimer.reset();
3031
m_warmupTimer.reset();
@@ -60,14 +61,14 @@ HeaterState HeaterControllerBase::GetNextState(HeaterState currentState, HeaterA
6061
if (heaterAllowState == HeaterAllow::Unknown)
6162
{
6263
// measured voltage too low to auto-start heating
63-
if (heaterSupplyVoltage < HEATER_SUPPLY_OFF_VOLTAGE)
64+
if (heaterSupplyVoltage < m_configuration->HeaterSupplyOffVoltage)
6465
{
6566
m_heaterStableTimer.reset();
6667
// set fault
6768
SetFault(ch, Fault::SensorNoHeatSupply);
6869
return HeaterState::NoHeaterSupply;
6970
}
70-
else if (heaterSupplyVoltage > HEATER_SUPPLY_ON_VOLTAGE)
71+
else if (heaterSupplyVoltage > m_configuration->HeaterSupplyOnVoltage)
7172
{
7273
// measured voltage is high enougth to auto-start heating, wait some time to stabilize
7374
heaterAllowed = m_heaterStableTimer.hasElapsedSec(HEATER_BATTERY_STAB_TIME);
@@ -101,7 +102,7 @@ HeaterState HeaterControllerBase::GetNextState(HeaterState currentState, HeaterA
101102
#endif
102103

103104
// If preheat timeout, or sensor is already hot (engine running?)
104-
if (m_preheatTimer.hasElapsedSec(m_preheatTimeSec) || sensorTemp > closedLoopTemp)
105+
if (m_preheatTimer.hasElapsedSec(m_configuration->PreheatTimeSec) || sensorTemp > closedLoopTemp)
105106
{
106107
// If enough time has elapsed, start the ramp
107108
// Start the ramp at 7 volts
@@ -121,7 +122,7 @@ HeaterState HeaterControllerBase::GetNextState(HeaterState currentState, HeaterA
121122
m_closedLoopStableTimer.reset();
122123
return HeaterState::ClosedLoop;
123124
}
124-
else if (m_warmupTimer.hasElapsedSec(m_warmupTimeSec))
125+
else if (m_warmupTimer.hasElapsedSec(HEATER_WARMUP_TIMEOUT))
125126
{
126127
SetFault(ch, Fault::SensorDidntHeat);
127128
// retry after timeout

firmware/heater_control.h

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "can.h"
88
#include "pid.h"
99
#include "timer.h"
10+
#include "fixed_point.h"
1011

1112
enum class HeaterState
1213
{
@@ -17,6 +18,14 @@ enum class HeaterState
1718
NoHeaterSupply,
1819
};
1920

21+
struct HeaterConfig {
22+
FixedPoint<uint8_t, 10> HeaterSupplyOffVoltage; // in 0.1V steps, 25.5V max
23+
FixedPoint<uint8_t, 10> HeaterSupplyOnVoltage; // in 0.1V steps, 25.5V max
24+
ScaledValue<uint8_t, 5> PreheatTimeSec; // In 5 second steps, 1275s max
25+
uint8_t pad[5];
26+
} __attribute__((packed));
27+
static_assert(sizeof(HeaterConfig) == 8, "HeaterConfig size incorrect");
28+
2029
struct ISampler;
2130

2231
struct IHeaterController
@@ -31,8 +40,8 @@ struct IHeaterController
3140
class HeaterControllerBase : public IHeaterController
3241
{
3342
public:
34-
HeaterControllerBase(int ch, int preheatTimeSec, int warmupTimeSec);
35-
void Configure(float targetTempC, float targetEsr);
43+
HeaterControllerBase(int ch);
44+
void Configure(float targetTempC, float targetEsr, struct HeaterConfig* configuration);
3645
void Update(const ISampler& sampler, HeaterAllow heaterAllowState) override;
3746

3847
bool IsRunningClosedLoop() const override;
@@ -62,9 +71,6 @@ class HeaterControllerBase : public IHeaterController
6271

6372
const uint8_t ch;
6473

65-
const int m_preheatTimeSec;
66-
const int m_warmupTimeSec;
67-
6874
int m_retryTime = 0;
6975

7076
Timer m_heaterStableTimer;
@@ -79,7 +85,7 @@ class HeaterControllerBase : public IHeaterController
7985
Timer m_underheatTimer;
8086
Timer m_overheatTimer;
8187

82-
static const int batteryStabTimeCounter = HEATER_BATTERY_STAB_TIME / HEATER_CONTROL_PERIOD;
88+
struct HeaterConfig* m_configuration;
8389
};
8490

8591
const IHeaterController& GetHeaterController(int ch);

firmware/heater_thread.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ static const PWMConfig heaterPwmConfig = {
2828
class HeaterController : public HeaterControllerBase {
2929
public:
3030
HeaterController(int ch, int pwm_ch)
31-
: HeaterControllerBase(ch, HEATER_PREHEAT_TIME, HEATER_WARMUP_TIMEOUT)
31+
: HeaterControllerBase(ch)
3232
, pwm_ch(pwm_ch)
3333
{
3434
}
@@ -74,21 +74,24 @@ static void HeaterThread(void*)
7474
// immediately think we overshot the target temperature
7575
chThdSleepMilliseconds(1000);
7676

77+
struct HeaterConfig* configuration = &GetConfiguration()->heaterConfig;
78+
7779
// Configure heater controllers for sensor type
7880
for (int i = 0; i < AFR_CHANNELS; i++)
7981
{
8082
auto& h = heaterControllers[i];
83+
8184
switch (GetSensorType())
8285
{
8386
case SensorType::LSU42:
84-
h.Configure(730, 80);
87+
h.Configure(730, 80, configuration);
8588
break;
8689
case SensorType::LSUADV:
87-
h.Configure(785, 300);
90+
h.Configure(785, 300, configuration);
8891
break;
8992
case SensorType::LSU49:
9093
default:
91-
h.Configure(780, 300);
94+
h.Configure(780, 300, configuration);
9295
break;
9396
}
9497
}

firmware/ini/wideband_dual.ini

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,11 @@ AemNetEgtTx0 = bits, U08, 152, [2:2], "Disable", "Enable"
8585
AemNetEgtIdx0 = scalar, U08, 154, "", 1, 0, 0, 255, 0
8686

8787
AemNetEgtTx1 = bits, U08, 160, [2:2], "Disable", "Enable"
88-
AemNetEgtIdx1 = scalar, U08, 162, "", 1, 0, 0, 255, 0
88+
AemNetEgtIdx1 = scalar, U08, 162, "", 1, 0, 0, 255, 0
89+
90+
HeaterSupplyOffVoltage = scalar, U08, 168, "V", 0.1, 0, 0, 24.0, 2
91+
HeaterSupplyOnVoltage = scalar, U08, 169, "V", 0.1, 0, 0, 24.0, 2
92+
PreheatTimeSec = scalar, U08, 170, "s", 5, 0, 0, 1275, 0
8993

9094
page = 2 ; this is a RAM only page with no burnable flash
9195
; name = class, type, offset, [shape], units, scale, translate, min, max, digits
@@ -333,6 +337,7 @@ entry = EGT1_commErrors, "EGT 1: comm errors", int, "%d"
333337
menuDialog = main
334338
menu = "&Settings"
335339
subMenu = sensor_settings, "Sensor settings"
340+
subMenu = heater_settings, "Heater settings"
336341
subMenu = can_settings, "CAN AFR settings"
337342
subMenu = can_egt_settings, "CAN EGT settings"
338343

@@ -365,6 +370,11 @@ cmd_openblt = "Z\x00\xbc\x00\x00"
365370
dialog = sensor_settings, "Sensor Settings"
366371
field = "Sensor Type", LsuSensorType
367372

373+
dialog = heater_settings, "Heater Settings"
374+
field = "Heater Supply Off Voltage", HeaterSupplyOffVoltage
375+
field = "Heater Supply On Voltage", HeaterSupplyOnVoltage
376+
field = "Preheat Time Sec", PreheatTimeSec
377+
368378
dialog = afr0_can_settings, "AFR 0 (left) channel CAN Settings"
369379
field = "RusEFI protocol:"
370380
field = "Output AFR", RusEfiTx0

firmware/util/fixed_point.h

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <type_traits>
5+
#include <limits>
6+
7+
/**
8+
* @brief A lightweight class for storing scaled values using a base type with ratio scale factor.
9+
*
10+
* This class is designed to be compatible with packed structures
11+
*
12+
* Examples:
13+
* - ScaledValue<int16_t, 10> multiplies raw values by 10.0 (e.g., 13 raw -> 130.0f)
14+
* - ScaledValue<int16_t, 1, 10> multiplies raw values by 0.1 (e.g., 128 raw -> 12.8f)
15+
*
16+
* @tparam TStorage Base type for storage (e.g., int8_t, uint8_t, int16_t, uint16_t, etc.).
17+
* @tparam TScaleFactorNumerator Numerator of the scale factor (e.g., 10 for scale 10.0).
18+
* @tparam TScaleFactorDenominator Denominator of the scale factor (e.g., 10 for scale 0.1).
19+
*/
20+
template<typename TStorage, uint16_t TScaleFactorNumerator, uint16_t TScaleFactorDenominator = 1>
21+
struct ScaledValue {
22+
static_assert(std::is_integral<TStorage>::value, "TStorage must be an integral type");
23+
static_assert(TScaleFactorDenominator != 0, "TScaleFactorDenominator must not be zero");
24+
25+
static constexpr float scale() {
26+
return static_cast<float>(TScaleFactorNumerator) / static_cast<float>(TScaleFactorDenominator);
27+
}
28+
29+
TStorage value; // Storage using the base type
30+
31+
// Convert to float
32+
constexpr float getValue() const {
33+
return static_cast<float>(value) * scale();
34+
}
35+
36+
// Convert from float
37+
constexpr void setValue(float val) {
38+
float scaled = val / scale();
39+
if (scaled < minRawValue()) {
40+
value = minRawValue();
41+
return;
42+
}
43+
if (scaled > maxRawValue()) {
44+
value = maxRawValue();
45+
return;
46+
}
47+
value = static_cast<TStorage>(scaled + (scaled >= 0 ? 0.5f : -0.5f)); // Round to nearest
48+
}
49+
50+
constexpr TStorage getRaw() const {
51+
return value;
52+
}
53+
54+
constexpr void setRaw(TStorage raw) {
55+
value = raw;
56+
}
57+
58+
// Implicit conversion to float
59+
constexpr operator float() const {
60+
return getValue();
61+
}
62+
63+
// Implicit assignment from float
64+
constexpr ScaledValue& operator=(float val) {
65+
setValue(val);
66+
return *this;
67+
}
68+
69+
private:
70+
// Maximum raw value that can be stored
71+
constexpr TStorage maxRawValue() const {
72+
return std::numeric_limits<TStorage>::max();
73+
}
74+
75+
// Minimum raw value that can be stored
76+
constexpr TStorage minRawValue() const {
77+
return std::numeric_limits<TStorage>::min();
78+
}
79+
};
80+
81+
// Alias for fixed-point
82+
template<typename TStorage, int TScaleFactor>
83+
using FixedPoint = ScaledValue<TStorage, 1, TScaleFactor>;

test/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ CPPSRC += \
3030
test_stubs.cpp \
3131
tests/test_sampler.cpp \
3232
tests/test_heater.cpp \
33+
tests/test_fixed_point.cpp \
34+
tests/test_config.cpp \
3335

3436
INCDIR += \
3537
$(PROJECT_DIR)/googletest/googlemock/ \

0 commit comments

Comments
 (0)