FastLED 3.9.15
Loading...
Searching...
No Matches
power_mgt.cpp.hpp
Go to the documentation of this file.
1
3
5#include "led_sysdefs.h" // Must be included first (required by lib8tion.h)
6#include "pixeltypes.h" // CRGB
7#include "controller.h" // CLEDController
8#include "fastpin.h" // Pin
10#if SKETCH_HAS_LARGE_MEMORY
11#include "fl/math/math.h" // fl::pow, fl::lround — libm-gated wrappers
12#include "fl/stl/array.h"
13#endif
14#include "fl/stl/int.h" // fl::u32, fl::u8
15#include "power_mgt.h" // Function declarations (to avoid redefinition errors)
16#include "fl/stl/singleton.h" // fl::Singleton
17// POWER MANAGEMENT
18
37
38static constexpr float kLinearPowerScalingExponent = 1.0f;
39static constexpr float kPowerScalingExponentEpsilon = 0.0001f;
40
45
46#if SKETCH_HAS_LARGE_MEMORY
47static constexpr fl::size kPowerScalingTableSize = 256;
48
52struct PowerScalingState {
53 fl::array<fl::u8, kPowerScalingTableSize> forward;
54 fl::array<fl::u8, kPowerScalingTableSize> reverse;
55
56 PowerScalingState() {
57 reset_identity();
58 }
59
60 void reset_identity() {
61 for (fl::size i = 0; i < kPowerScalingTableSize; ++i) {
62 forward[i] = static_cast<fl::u8>(i);
63 reverse[i] = static_cast<fl::u8>(i);
64 }
65 }
66};
67
68static PowerScalingState& gPowerScaling() {
70}
71
74static void rebuild_power_scaling_tables(float exponent) {
75 PowerScalingState& state = gPowerScaling();
76 if (!(exponent > 0.0f) ||
78 state.reset_identity();
79 return;
80 }
81
82 // Forward LUT: source brightness -> scaled brightness via pow(x/255, exponent)
83 state.forward[0] = 0;
84 for (fl::size i = 1; i < kPowerScalingTableSize; ++i) {
85 float normalized = static_cast<float>(i) / 255.0f;
86 // Route through fl::powf / fl::lroundf (libm-gated via FL_MATH_USE_LIBM,
87 // see fl/math/math.cpp.hpp). Use the float overloads — fl::pow takes
88 // double, so passing floats would auto-promote and re-anchor the
89 // double-precision soft-FP chain that #3002 is trying to avoid.
90 int mapped = static_cast<int>(
91 fl::lroundf(fl::powf(normalized, exponent) * 255.0f));
92 state.forward[i] = static_cast<fl::u8>(fl::clamp(mapped, 0, 255));
93 }
94 state.forward[255] = 255;
95
96 // Reverse LUT: scaled brightness -> largest source whose forward value is
97 // <= the scaled value. Floor-inverse, so `unmap_power_value()` never
98 // rounds a budgeted scaled brightness *up* past the power budget.
99 state.reverse[0] = 0;
100 int source = 0;
101 for (int scaled = 1; scaled < static_cast<int>(kPowerScalingTableSize); ++scaled) {
102 while (source < 255 && state.forward[source + 1] <= scaled) {
103 ++source;
104 }
105 state.reverse[scaled] = static_cast<fl::u8>(source);
106 }
107}
108#endif
109
111#if SKETCH_HAS_LARGE_MEMORY
112 return gPowerScaling().forward[brightness];
113#else
114 return brightness;
115#endif
116}
117
118static fl::u8 unmap_power_value(fl::u8 scaled_brightness) {
119#if SKETCH_HAS_LARGE_MEMORY
120 return gPowerScaling().reverse[scaled_brightness];
121#else
122 return scaled_brightness;
123#endif
124}
125
126fl::u32 scale_power_for_brightness(fl::u32 total_mW, fl::u8 brightness) {
127 return fl::scale32by8(total_mW, map_power_value(brightness));
128}
129
131
132// Alternate calibration by RAtkins via pre-PSU wattage measurments;
133// these are all probably about 20%-25% too high due to PSU heat losses,
134// but if you're measuring wattage on the PSU input side, this may
135// be a better set of calibrations. (WS2812B)
136// static const uint8_t gRed_mW = 100;
137// static const uint8_t gGreen_mW = 48;
138// static const uint8_t gBlue_mW = 100;
139// static const uint8_t gDark_mW = 12;
140
141
144#define POWER_LED 1
145
148#define POWER_DEBUG_PRINT 0
149
150
151// Power consumed by the MCU
152static const fl::u8 gMCU_mW = 25 * 5; // 25mA @ 5v = 125 mW
153
154static fl::u8 gMaxPowerIndicatorLEDPinNumber = 0; // default = Arduino onboard LED pin. set to zero to skip this.
155
156
157// Span-based version (primary implementation)
159 fl::u32 red32 = 0, green32 = 0, blue32 = 0;
160
161 // Iterate using span's safe indexing
162 for(size_t i = 0; i < leds.size(); i++) {
163 red32 += map_power_value(leds[i].r);
164 green32 += map_power_value(leds[i].g);
165 blue32 += map_power_value(leds[i].b);
166 }
167
168 red32 *= gPowerModel().red_mW;
169 green32 *= gPowerModel().green_mW;
170 blue32 *= gPowerModel().blue_mW;
171
172 red32 >>= 8;
173 green32 >>= 8;
174 blue32 >>= 8;
175
176 fl::u32 total = red32 + green32 + blue32 + (gPowerModel().dark_mW * leds.size());
177
178 return total;
179}
180
181// Pointer-based version (delegates to span version)
182fl::u32 calculate_unscaled_power_mW( const CRGB* ledbuffer, fl::u16 numLeds ) //25354
183{
184 return calculate_unscaled_power_mW(fl::span<const CRGB>(ledbuffer, numLeds));
185}
186
187
188fl::u8 calculate_max_brightness_for_power_vmA(const CRGB* ledbuffer, fl::u16 numLeds, fl::u8 target_brightness, fl::u32 max_power_V, fl::u32 max_power_mA) {
189 return calculate_max_brightness_for_power_mW(ledbuffer, numLeds, target_brightness, max_power_V * max_power_mA);
190}
191
192fl::u8 calculate_max_brightness_for_power_mW(const CRGB* ledbuffer, fl::u16 numLeds, fl::u8 target_brightness, fl::u32 max_power_mW) {
193 fl::u32 total_mW = calculate_unscaled_power_mW( ledbuffer, numLeds);
194
195 fl::u8 target_brightness_scaled = map_power_value(target_brightness);
196 fl::u32 requested_power_mW = scale_power_for_brightness(total_mW, target_brightness);
197
198 fl::u8 recommended_brightness = target_brightness;
199 if(requested_power_mW > max_power_mW) {
200 fl::u8 recommended_scaled = (fl::u32)(target_brightness_scaled * (fl::u32)(max_power_mW)) / requested_power_mW;
201 recommended_brightness = unmap_power_value(recommended_scaled);
202 }
203
204 return recommended_brightness;
205}
206
207// sets brightness to
208// - no more than target_brightness
209// - no more than max_mW milliwatts
210fl::u8 calculate_max_brightness_for_power_mW( fl::u8 target_brightness, fl::u32 max_power_mW)
211{
212 fl::u32 total_mW = gMCU_mW;
213
215 while(pCur) {
216 total_mW += calculate_unscaled_power_mW( pCur->leds(), pCur->size());
217 pCur = pCur->next();
218 }
219
220#if POWER_DEBUG_PRINT == 1
221 Serial.print("power demand at full brightness mW = ");
222 Serial.println( total_mW);
223#endif
224
225 fl::u8 target_brightness_scaled = map_power_value(target_brightness);
226 fl::u32 requested_power_mW = scale_power_for_brightness(total_mW, target_brightness);
227#if POWER_DEBUG_PRINT == 1
228 if( target_brightness != 255 ) {
229 Serial.print("power demand at scaled brightness mW = ");
230 Serial.println( requested_power_mW);
231 }
232 Serial.print("power limit mW = ");
233 Serial.println( max_power_mW);
234#endif
235
236 if( requested_power_mW < max_power_mW) {
237#if POWER_LED > 0
239 Pin(gMaxPowerIndicatorLEDPinNumber).lo(); // turn the LED off
240 }
241#endif
242#if POWER_DEBUG_PRINT == 1
243 Serial.print("demand is under the limit");
244#endif
245 return target_brightness;
246 }
247
248 fl::u8 recommended_scaled = (fl::u32)(target_brightness_scaled * (fl::u32)(max_power_mW)) / ((fl::u32)(requested_power_mW));
249 fl::u8 recommended_brightness = unmap_power_value(recommended_scaled);
250#if POWER_DEBUG_PRINT == 1
251 Serial.print("recommended brightness # = ");
252 Serial.println( recommended_brightness);
253
254 fl::u32 resultant_power_mW = scale_power_for_brightness(total_mW, recommended_brightness);
255 Serial.print("resultant power demand mW = ");
256 Serial.println( resultant_power_mW);
257
258 Serial.println();
259#endif
260
261#if POWER_LED > 0
263 Pin(gMaxPowerIndicatorLEDPinNumber).hi(); // turn the LED on
264 }
265#endif
266
267 return recommended_brightness;
268}
269
271{
273}
274
275void set_power_model(const PowerModelRGB& model) {
276 gPowerModel() = model;
277#if SKETCH_HAS_LARGE_MEMORY
278 rebuild_power_scaling_tables(model.exponent);
279#else
280 // Small-memory targets ignore non-linear exponent and keep linear behavior;
281 // clamp the stored field so get_power_scaling_exponent() reports the truth.
283#endif
284}
285
286void set_power_scaling_exponent(float exponent) {
287 PowerModelRGB model = gPowerModel();
288 model.exponent = exponent;
289 set_power_model(model);
290}
291
293 // Authoritative storage lives in the model; on small-memory builds the
294 // field is clamped to 1.0 by set_power_model.
295 return gPowerModel().exponent;
296}
297
301
302// Note: The following deprecated wrapper functions have been moved to FastLED.cpp:
303// - set_max_power_in_volts_and_milliamps()
304// - set_max_power_in_milliwatts()
305// - show_at_max_brightness_for_power()
306// - delay_at_max_brightness_for_power()
307// These functions depend on the FastLED singleton and don't belong in this core power calculation file.
fl::CRGB leds[NUM_LEDS]
fl::UISlider brightness("Brightness", BRIGHTNESS, 0, 255)
TestState state
CLEDController * next() FL_NOEXCEPT
Get the next controller in the linked list after this one.
CRGB * leds() FL_NOEXCEPT
Pointer to the CRGB array for this controller.
virtual int size() const FL_NOEXCEPT
How many LEDs does this controller manage?
static CLEDController * head() FL_NOEXCEPT
Get the first LED controller in the linked list of controllers.
static T & instance() FL_NOEXCEPT
Definition singleton.h:41
fl::CLEDController CLEDController
Backward compatibility header - use fl/fastpin.h directly.
fl::CRGB CRGB
Definition crgb.h:25
fl::u32 scale_power_for_brightness(fl::u32 total_mW, fl::u8 brightness)
Applies the configured power-scaling response to a total power value.
void set_max_power_indicator_LED(fl::u8 pinNumber)
Select a pin with an LED that will be flashed to indicate that power management is pulling down the b...
fl::u8 calculate_max_brightness_for_power_mW(const CRGB *ledbuffer, fl::u16 numLeds, fl::u8 target_brightness, fl::u32 max_power_mW)
Determines the highest brightness level you can use and still stay under the specified power budget f...
fl::u8 calculate_max_brightness_for_power_vmA(const CRGB *ledbuffer, fl::u16 numLeds, fl::u8 target_brightness, fl::u32 max_power_V, fl::u32 max_power_mA)
Determines the highest brightness level you can use and still stay under the specified power budget f...
fl::u32 calculate_unscaled_power_mW(fl::span< const CRGB > leds)
void set_power_model(const PowerModelRGB &model)
Set custom RGB LED power consumption model.
float get_power_scaling_exponent()
Get the current brightness-to-power response exponent.
void set_power_scaling_exponent(float exponent)
Set a non-linear brightness-to-power response exponent.
PowerModelRGB get_power_model()
Get current RGB power model.
Determines which platform system definitions to include.
constexpr T && forward(typename remove_reference< T >::type &t) FL_NOEXCEPT
Definition s16x16x4.h:234
unsigned char u8
Definition stdint.h:131
float powf(float base, float exponent) FL_NOEXCEPT
Definition math.h:436
constexpr bool almost_equal(T a, T b, U tolerance) FL_NOEXCEPT
Definition math.h:90
long lroundf(float value) FL_NOEXCEPT
Definition math.h:330
void reverse(Iterator first, Iterator last) FL_NOEXCEPT
Definition algorithm.h:11
constexpr enable_if< is_fixed_point< T >::value, T >::type clamp(T x, T lo, T hi) FL_NOEXCEPT
Includes defintions for RGB and HSV pixels.
static constexpr float kLinearPowerScalingExponent
static fl::u8 unmap_power_value(fl::u8 scaled_brightness)
static constexpr float kPowerScalingExponentEpsilon
static fl::u8 gMaxPowerIndicatorLEDPinNumber
static fl::u8 map_power_value(fl::u8 brightness)
static const fl::u8 gMCU_mW
static PowerModelRGB & gPowerModel()
Global RGB power model (initialized to WS2812 @ 5V defaults, linear response)
Functions to limit the power used by FastLED.
fl::u8 blue_mW
Blue channel power at full brightness (255), in milliwatts.
Definition power_mgt.h:30
fl::u8 dark_mW
Dark LED baseline power consumption, in milliwatts.
Definition power_mgt.h:31
fl::u8 red_mW
Red channel power at full brightness (255), in milliwatts.
Definition power_mgt.h:28
float exponent
Brightness-to-power response exponent (1.0 = linear)
Definition power_mgt.h:32
fl::u8 green_mW
Green channel power at full brightness (255), in milliwatts.
Definition power_mgt.h:29
RGB LED power consumption model Used for standard 3-channel LEDs (WS2812, WS2812B,...
Definition power_mgt.h:27
#define Serial
Definition serial.h:304