From f17ebd17e5a4d705db436b7277aa18c87a9a37d4 Mon Sep 17 00:00:00 2001 From: BlackMark Date: Sat, 18 Jul 2026 08:03:42 +0200 Subject: [PATCH] Use libavr flash_table and __builtin_log The thermistor Beta curve and the fan cubic move onto avr::flash_table instead of hand-rolled [[gnu::progmem]] arrays with raw pgm_read, and the compile-time logarithm uses __builtin_log (which constant-folds on the AVR backend) instead of a hand-rolled series. Same 11284 B, still byte-identical across libavr modes. Co-Authored-By: Claude --- src/curve.hpp | 23 ++++++++--------- src/thermistor.hpp | 64 +++++++++++++--------------------------------- 2 files changed, 29 insertions(+), 58 deletions(-) diff --git a/src/curve.hpp b/src/curve.hpp index 09050ef..44acbba 100644 --- a/src/curve.hpp +++ b/src/curve.hpp @@ -3,11 +3,11 @@ #include #include -#include +#include // 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. +// (0.002246·x³ − 0.09·x² + 0.91·x, zero below 20 °C) becomes a libavr +// flash_table of duty percent per °C. namespace app::curve { namespace detail { @@ -25,14 +25,13 @@ consteval std::uint8_t duty_entry(int celsius) return static_cast(duty + 0.5); } -struct table { - [[gnu::progmem]] static constexpr std::array data = [] { - std::array out{}; - for (int t = 0; t < 100; ++t) - out[static_cast(t)] = duty_entry(t); - return out; - }(); -}; +inline constexpr avr::flash_table<[] { + std::array out{}; + for (int t = 0; t < 100; ++t) + out[static_cast(t)] = duty_entry(t); + return out; +}()> + table; } // namespace detail @@ -43,7 +42,7 @@ inline std::uint8_t duty(std::int8_t celsius) celsius = 0; if (celsius > 99) celsius = 99; - return pgm_read_byte(&detail::table::data[static_cast(celsius)]); + return detail::table[static_cast(celsius)]; } } // namespace app::curve diff --git a/src/thermistor.hpp b/src/thermistor.hpp index 67bb9cd..f24da85 100644 --- a/src/thermistor.hpp +++ b/src/thermistor.hpp @@ -3,12 +3,12 @@ #include #include -#include +#include // NTC thermistor on a series divider, solved entirely at compile time: -// the Beta equation (with its logarithm) runs consteval into a flash -// table — the firmware never does floating point. Raw 10-bit ADC counts -// map to quarter-°C with linear interpolation between table steps. +// the Beta equation (logarithm and all) runs consteval into a libavr +// flash_table — the firmware never does floating point. Raw 10-bit ADC +// counts map to quarter-°C with linear interpolation between table steps. namespace app::thermistor { inline constexpr double series_resistor = 9951; @@ -18,40 +18,18 @@ inline constexpr double nominal_temperature = 25; namespace detail { -// Natural log for the consteval evaluator (no freestanding ): -// range-reduce by powers of two, then the atanh series around 1. -consteval double ln(double x) -{ - constexpr double ln2 = 0.6931471805599453; - int k = 0; - while (x > 1.5) { - x /= 2; - ++k; - } - while (x < 0.75) { - x *= 2; - --k; - } - double z = (x - 1) / (x + 1); - double z2 = z * z; - double term = z; - double sum = 0; - for (int n = 1; n < 30; n += 2) { - sum += term / n; - term *= z2; - } - return 2 * sum + k * ln2; -} - consteval double temperature_of(double adc) { double resistance = series_resistor * adc / (1023.0 - adc); - double steinhart = ln(resistance / nominal_resistance) / beta + 1.0 / (nominal_temperature + 273.15); + // __builtin_log constant-folds on the AVR backend, so no runtime libm + // and no hand-rolled series is needed for the compile-time table. + double steinhart = __builtin_log(resistance / nominal_resistance) / beta + 1.0 / (nominal_temperature + 273.15); return 1.0 / steinhart - 273.15; } // 256 entries over the 10-bit range (steps of 4 counts), quarter-°C, -// clamped to a sane sensor window. +// clamped to a sane sensor window; entry 256 mirrors 255 so interpolation +// at full scale has a right neighbour. consteval std::int16_t quarters_entry(int index) { double adc = index * 4.0; @@ -67,19 +45,13 @@ consteval std::int16_t quarters_entry(int index) return static_cast(t < 0 ? t - 0.5 : t + 0.5); } -struct table { - [[gnu::progmem]] static constexpr std::array data = [] { - std::array out{}; - for (int i = 0; i < 257; ++i) - out[static_cast(i)] = quarters_entry(i < 256 ? i : 255); - return out; - }(); -}; - -inline std::int16_t read_entry(std::uint16_t index) -{ - return static_cast(pgm_read_word(&table::data[index])); -} +inline constexpr avr::flash_table<[] { + std::array out{}; + for (int i = 0; i < 257; ++i) + out[static_cast(i)] = quarters_entry(i < 256 ? i : 255); + return out; +}()> + table; } // namespace detail @@ -88,8 +60,8 @@ inline std::int16_t quarters(std::uint16_t adc) { std::uint16_t index = adc >> 2; // the 257th entry backs index+1 at full scale std::uint8_t frac = adc & 3; - auto a = detail::read_entry(index); - auto b = detail::read_entry(static_cast(index + 1)); + auto a = detail::table[index]; + auto b = detail::table[static_cast(index + 1)]; return static_cast(a + ((b - a) * frac) / 4); }