Rewrite on libavr

Same controller: thermistor on ADC0 averaged over 1000 free-running
conversions, 50 kHz fan PWM on OC0B, 115200 Bd console with the full
command set, EEPROM temperature histogram, watchdog-reset path into the
boot section. The Steinhart-Hart math and the libm log are gone — the
Beta equation and the cubic fan curve are consteval-evaluated into
flash tables; the firmware never does floating point. Byte-identical
.text in both libavr modes. Legacy stays on master.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-18 04:37:26 +02:00
parent 76d6b1583b
commit ab78d94872
35 changed files with 734 additions and 1237 deletions

49
src/curve.hpp Normal file
View File

@@ -0,0 +1,49 @@
#pragma once
#include <array>
#include <cstdint>
#include <avr/pgmspace.h>
// The auto-mode fan curve, tabulated at compile time: the legacy cubic
// (0.002246·x³ 0.09·x² + 0.91·x, zero below 20 °C) becomes a flash
// lookup of duty percent per °C.
namespace app::curve {
namespace detail {
consteval std::uint8_t duty_entry(int celsius)
{
double x = celsius;
if (x < 20)
return 0;
double duty = 0.002246 * x * x * x - 0.09 * x * x + 0.91 * x;
if (duty < 0)
duty = 0;
if (duty > 100)
duty = 100;
return static_cast<std::uint8_t>(duty + 0.5);
}
struct table {
[[gnu::progmem]] static constexpr std::array<std::uint8_t, 100> data = [] {
std::array<std::uint8_t, 100> out{};
for (int t = 0; t < 100; ++t)
out[static_cast<std::size_t>(t)] = duty_entry(t);
return out;
}();
};
} // namespace detail
// Duty percent for a temperature (clamped to the 0..99 °C table window).
inline std::uint8_t duty(std::int8_t celsius)
{
if (celsius < 0)
celsius = 0;
if (celsius > 99)
celsius = 99;
return pgm_read_byte(&detail::table::data[static_cast<std::uint8_t>(celsius)]);
}
} // namespace app::curve