-
Notifications
You must be signed in to change notification settings - Fork 0
Analog LEDs
ProffieOS v6.7x007
xAnalogLED is the firmware module that:
- Controls the brightness of each analog LED emitter
- Renders colors on multi-emitter LEDs
An analog LED consists of one or more LED channels that respond in a coordinate way to the input color. Each analog LED channel controls one or more emitters through PWM pins. Each emitter is assigned to a Driver, responsible for calculating the value needed in the PWM control register (the Register Value) in order to obtain the desired Brightness at the current battery voltage. Each analog LED channel is controlled by a 24-bit sRGB Control Value, which is processed by each channel via a Color Renderer into brightness values for each emitter.
One or more analog LEDs can be assigned to a blade. Common configurations for analog LEDs are:
- monochromatic LED: one single-emitter channel
- monochromatic LEDs with flash: two single-emitter channels
- RGB LEDs: one 3-emitter channel
- RGB LEDs with flash: one 3-emitter channel + one single-emitter channel. This AnalogLED configuration is exemplified below:
The power delivered to the LEDs is controlled by pulse width modulation (PWM). The duty cycle of the PWM signal generated by the board is controlled by the numeric value written in a timer register, the Register Value. The LED response to the register value is the Luminance of the emitted light, which we measure in lux. If, for any particular LED emitter, we normalize the luminance to the maximum value it can provide and rescale it to a 16-bit unsigned integer, we get the Brightness, which is a dimensionless number in the range 0 to 65535.
We'll call the relationship between brightness and register value a Control Curve. The purpose of a driver is to implement a control curve: given the desired brightness, the driver must provide the register value that will get a proportional luminance from the LED. If the relationship between the requested brightness and the obtained luminance is linear... we got ourselves a linear driver! In most cases, this is what we want from a driver.
OSx supports multiple types of drivers, interchangeable through the common interface xAnalogLED_DriverInterface.
The direct driver implements a simple proportionality relation:
The direct driver is only suitable for calibration measurements or if the LED is controlled by a PWM-driven current regulator. Do not use the direct driver with resistor-limited LEDs!!!
The legacy driver is based on the electrical model of a resistor-limited LED driven by a pulse-width modulated voltage source.
Based on this model, the legacy driver aims to control the current through the LED in a proportional relation with the driver's input (brightness):
The linearity of the legacy driver relies on two assumptions:
- Linearity of the (voltage)-(current) characteristic of the LED, as it's electrical model is limited to two voltage-current points, which can only define a line. This assumption is never true, as the datasheet of any LED will clarify.
- Linearity of the (duty cycle)-(current) characteristic. This is never entirely true because of (1), but it is almost true if the LED never gets into overdrive, meaning the instantaneos value of
$I_{LED}$ never exceeds$MaxAmps$ . Not getting the LED into overdrive even at fully charged battery requires a large current-limiting resistor, which is impractical, so most LEDs will be occasionally overdriven.
Due to those factors, the linearity of the legacy driver is highly dependant on the LED emitter and resistor value.
"p" stands for "photometric" because the pPWL driver aims to control a photometric quantity (luminance) rather than an electrical one (current). For that it needs measurements of luminance vs. register value at constant voltage and constant temperature. After normalization and inversion we get the control curve the driver needs to implement:
The pPWL driver aproximates the control curve by piecewise linear (PWL) interpolation between voltage-dependent reference points. The reference points are recalculated when battery voltage changes more than a certain threshold, by another piecewise linear interpolation. For details about the OSx interpolators see the paragraph on xInterpolators.
By characterizing directly the brightness vs. register value relationship, the pPWL driver compensates in a single curve most of the non-linearities of the electrical circuit, so it provides enough linearity to allow accurate color rendering. This comes at the cost of the requirement of specific photometric data for each LED model and board model.
The color renderer is the software module that calculates the Brightness [0, 65535] needed by each emitter in order to reproduce a color described by R, G and B [0, 255]. Every Analog LED requires a color renderer, even monochromatic and bichromatic LEDs.
OSx supports multiple types of renderers, interchangeable through the common interface xColorRenderer_Interface.
This is a 1, 2 or 3-channel renderer that simply scales each input color to a proportional brightness:
The default value of
The HSL Enhancer is a single-output renderer that responds to the hue, saturation or luminance of the RGB control value. If used as a multi-channel renderer, it will provide the same brightness on all channels.
Depending on how it is configured, a HSL enhancer will provide an output proportional to one of the following parameters of the RGB control value:
-
Lightness (type = 'l'): brightness will be proportional with the average of
$Cmax = max(R,G,B)$ and$Cmin = min(R,G,B)$ . This renderer is suitable for monochromatic LEDs.
Just like for the direct renderer, the integer
- Saturation (type = 's'): brightness will be proportional with the "whiteness" of the RGB control value, which is the inverse of the saturation (saturation as in HSV, since the "S" in HSL has little to do with actual color saturation...). This renderer is suitable for white LEDs used to enhance flash effects.
- Hue (type = 'h'): this renderer responds only to a target color, providing an output proportional to the content of that color in the RGB control value. The brightness provided by this renderer is proportional to average of the control R, G, B components weighted by the target R, G, B components, scaled with the inverse of saturation so it won't alter the white ballance:
The hue enhancer is suitable for controlling chromatic enhancement LEDs, for instance the amber emitter of an RGBA LED. It kinda can be used to render full spectrum on RGB LEDs, with gains or transfer functions controlling the white ballance, but the color rendering matrix is more computationally efficient.
All three types of HSL enhancers can be controlled by
Transfer functions allow the specification of an enhancement curve, thus providing an additional level of control to the HSL enhancement renderer.
The CRM renderer implements a linear transformation between the device-independent color space of the RGB control value and the color space of the 1, 2 or 3 output channels controlled by the renderer:
The color rendering matrix is obtained from colorimetric measurements of:
- The representation of the three emitters in the RGB space
- The coordinates of the RGB's white point in the emitter's color space
The CRM renderer can also apply gamma correction to each output brightness, allowing implementations of perceptual color spaces with linear R,G,B coordinates. Gamma correction must be defined as a transfer function and assigned to each output channel.
The CRM renderer is suitable for RGB control of full-spectrum LEDs.
An analog LED is specified as a collection of analog LED channels. A complete definition of an analog LED channel must include:
- number of emitters
- a driver for each emitter
- a color renderer
The pin assignments are not included in the LED definition, as those are install parameters. For details about install configuration, see Install Configuration.
Analog LEDs are normally specified in a structure stored in a COD file, which references by ID the data required by drivers and color renderers.
The COD entry defining an analog LED is defined by an XML entry buit on the template below. For details about COD files, see COD-and-XML .
<AnalogLED BinOrder="?"> <!--! set BinOrder !-->
<EntryType BinOrder="1" Format="char" value="s"/> <!-- struct -->
<StructID BinOrder="2" Format="uint16" value="5" /> <!-- ID -->
<StructHandler BinOrder="3" Format="uint16" value="113" /> <!-- xAnalogLED -->
<StructSize BinOrder="4" Format="uint16" value="9"/> <!-- 9 BYTES in subsequent fields -->
<nEm BinOrder="5" Format="uint8" value="3"/> <!-- 1...4 emitters, across all channels -->
<channelIDs BinOrder="6" Format="uint16" val0="3001" val02="0" val03="0" val04="0"/> <!-- specify 4 channels, 0 for unused -->
</AnalogLED>The data structure that holds in firmware the properties of an analog LED should use the following fields:
struct {
uint8_t nEm;
uint16_t channelIDs[4];
}__attribute__((packed)) analogLED;<AnalogLED_Channel BinOrder="?"> <!--! set BinOrder !-->
<EntryType BinOrder="1" Format="char" value="s"/> <!-- struct -->
<StructID BinOrder="2" Format="uint16" value="3001" /> <!-- ID -->
<StructHandler BinOrder="3" Format="uint16" value="112" /> <!-- xAnalogLED_Channel -->
<StructSize BinOrder="4" Format="uint16" value="15"/> <!-- 15 BYTES in subsequent fields -->
<nEm BinOrder="5" Format="uint8" value="3"/> <!-- 1...3 emitters -->
<renderer BinOrder="5" Format="uint16" value="1000"/>
<drivers BinOrder="6" Format="uint16" val0="12" val02="19" val03="21" />
<gammaTFs BinOrder="7" Format="uint16" val0="0" val02="0" val03="0" />
</AnalogLED_Channel>The data structure that holds in firmware the properties of an analog LED channel should use the following fields:
struct {
uint8_t nEm; // Number of emitters
uint16_t renderer; // Renderer ID, 0 for Direct
uint16_t drivers[3]; // Driver ID for each emitter, 0 for unused
uint16_t gammaTFs[3]; // IDs of transfer functions implementing gamma correction, for each emitter; 0 for unused
};The AnalogLED structure references by ID drivers and renderers, which will be searched in:
- Same file as AnalogLED
- install.cod
- ledlib.cod
Below are the XML templates used to generate the COD data for each of an analog LED components.
- LED specifications for the legacy driver:
<Legacy_aLEDdriver BinOrder="?"> <!--! set BinOrder !-->
<EntryType BinOrder="1" Format="char" value="s"/> <!-- struct -->
<StructID BinOrder="2" Format="uint16" value="21" /> <!-- ID -->
<StructHandler BinOrder="3" Format="uint16" value="100" /> <!-- xAnalogLED_Driver_Legacy -->
<StructSize BinOrder="4" Format="uint16" value="23"/> <!-- 23 BYTES in subsequent fields -->
<MaxAmps BinOrder="5" Format="float" value="1"/>
<MaxVolts BinOrder="6" Format="float" value="2.65"/>
<P2Amps BinOrder="7" Format="float" value="0.35"/>
<P2Volts BinOrder="8" Format="float" value="2.2"/>
<R BinOrder="9" Format="float" value="0.68"/>
<Red BinOrder="10" Format="uint8" value="255"/>
<Green BinOrder="11" Format="uint8" value="0"/>
<Blue BinOrder="12" Format="uint8" value="0"/>
</Legacy_aLEDdriver> struct {
float MaxAmps;
float MaxVolts;
float P2Amps;
float P2Volts;
float R;
uint8_t Red;
uint8_t Green;
uint8_t Blue;
};- LED specifications for the pPWL driver:
<pPWL_aLEDdriver BinOrder="?"> <!--! set BinOrder !-->
<EntryType BinOrder="1" Format="char" value="t"/> <!-- table -->
<TableID BinOrder="2" Format="uint16" value="21" /> <!-- ID -->
<TableHandler BinOrder="3" Format="uint16" value="101" /> <!-- xAnalogLED_Driver_pPWL -->
<TableSize BinOrder="4" Format="uint16" sizeH="7" sizeV="3" /> <!-- sizeV = number of rows in table below -->
<TableDataType BinOrder="5" Format="string" len="7" value="uint16"/>
<TableData BinOrder="6" Format="uint16">
<Row0 BinOrder="1" Format="uint16" val0="16585" val1="11056" val2="8542" val3="6857" val4="5796" val5="5025" val6="4463" />
<Row1 BinOrder="2" Format="uint16" val0="32019" val1="21353" val2="16529" val3="13301" val4="11294" val5="9847" val6="8785" />
<Row2 BinOrder="3" Format="uint16" val0="46133" val1="31193" val2="24240" val3="19587" val4="16714" val5="14633" val6="13110" />
</TableData>
</pPWL_aLEDdriver>- HSL enhancer color renderer:
<HSLe_renderer BinOrder="?"> <!--! set BinOrder !-->
<EntryType BinOrder="1" Format="char" value="s"/> <!-- struct -->
<StructID BinOrder="2" Format="uint16" value="21" /> <!-- ID -->
<StructHandler BinOrder="3" Format="uint16" value="110" /> <!-- _xColorRenderer_HSLe -->
<StructSize BinOrder="4" Format="uint16" value="10"/> <!-- 10 BYTES in subsequent fields -->
<type BinOrder="5" Format="char" value="s"/>
<gain BinOrder="6" Format="float" value="0"/>
<targetR BinOrder="7" Format="uint8" value="255"/>
<targetG BinOrder="8" Format="uint8" value="190"/>
<targetB BinOrder="9" Format="uint8" value="0"/>
<tfID BinOrder="10" Format="uint16" value="999"/>
</HSLe_renderer> struct {
char type; // 'h', 's' or 'l'
float gain; // [0.003891, 10]
uint8_t tR; // target Red (for H enhancer only)
uint8_t tG; // target Green (for H enhancer only)
uint8_t tB; // target Blue (for H enhancer only)
uint16_t tfID; // transfer function ID (0 for proportional)
};- Transfer function for HSL enhancer:
<enhancerTF BinOrder="?"> <!--! set BinOrder !-->
<EntryType BinOrder="1" Format="char" value="t"/> <!-- table -->
<TableID BinOrder="2" Format="uint16" value="999" /> <!-- ID -->
<TableHandler BinOrder="3" Format="uint16" value="99" /> <!-- xInterpolator -->
<TableSize BinOrder="4" Format="uint16" sizeH="4" sizeV="2" /> <!-- sizeH = number of reference points -->
<TableDataType BinOrder="5" Format="string" len="7" value="uint8"/>
<TableData BinOrder="6" Format="uint8"> <!-- Transfer function should start at (0,0) and end at (255,255) -->
<Row0 BinOrder="1" Format="uint8" val0="0" val1="100" val2="200" val3="255" /> <!-- X values -->
<Row1 BinOrder="2" Format="uint8" val0="0" val1="10" val2="210" val3="255" /> <!-- Y values -->
</TableData>
</enhancerTF>- CRM color renderer:
<CRM_renderer BinOrder="?"> <!--! set BinOrder !-->
<EntryType BinOrder="1" Format="char" value="t"/> <!-- table -->
<TableID BinOrder="2" Format="uint16" value="21" /> <!-- ID -->
<TableHandler BinOrder="3" Format="uint16" value="111" /> <!-- xColorRenderer_CRM -->
<TableSize BinOrder="4" Format="uint16" sizeH="3" sizeV="3" /> <!-- sizeV = number of outputs -->
<TableDataType BinOrder="5" Format="string" len="7" value="float"/>
<TableData BinOrder="6" Format="float"> <!-- Color Rendering Matrix -->
<Row0 BinOrder="1" Format="float" val0="2.7689" val1="1.7517" val2="1.1302" />
<Row1 BinOrder="2" Format="float" val0="1.0" val1="4.5907" val2="0.0601" />
<Row2 BinOrder="3" Format="float" val0="0" val1="0.5907" val2="5.0601" />
</TableData>
</CRM_renderer>- Transfer function for CRM's renderer gamma correction:
<gammaTF BinOrder="?"> <!--! set BinOrder !-->
<EntryType BinOrder="1" Format="char" value="t"/> <!-- table -->
<TableID BinOrder="2" Format="uint16" value="999" /> <!-- ID -->
<TableHandler BinOrder="3" Format="uint16" value="99" /> <!-- xInterpolator -->
<TableSize BinOrder="4" Format="uint16" sizeH="4" sizeV="1" /> <!-- sizeH = number of reference points -->
<TableDataType BinOrder="5" Format="string" len="7" value="uint8"/>
<TableData BinOrder="6" Format="uint16"> <!-- Transfer function should start at (0,0) and end at (65535, 65535) -->
<Row0 BinOrder="1" Format="uint8" val0="12000" val1="32767" val2="50000" val3="65535" /> <!-- X values -->
<Row1 BinOrder="2" Format="uint8" val0="32767" val1="42000" val2="63000" val3="65535" /> <!-- Y values -->
</TableData>
</gammaTF>Normally all the data associated with LEDs is stored in the LED library file called ledlib.cod, but COD file being content-oblivious, such data can also be located in the installation file install.cod or any other COD file.
-
Create analog LED object
xAnalogLED myLED(20000); // auto-refresh at 20 ms -
Install
-
From COD file(s):
myLED.Install("myleds.cod", codid_KR3Cree_RGB, bladePowerPin1, bladePowerPin2, bladePowerPin3); -
From code & file(s)
myLED.AssignDriver(bladePowerPin1, "myleds.cod", codid_XP2E_Red); // emitters are auto-numbered myLED.AssignDriver(bladePowerPin2, "myleds.cod", codid_XP2E_Green); myLED.AssignDriver(bladePowerPin3, "myleds.cod", codid_XP2E_Blue); myLED.AssignRenderer("myleds.cod", codid_KR3Cree_crm_sRGB); -
From code:
// see implementation details
-
Activate PWM on assigned pins
myLED.Activate(); -
Set color
myLED.Set(0xFF0000); // red myLED.Set(0x00FF00); // green myLED.Set(0x0000FF); // blue
-
- \blades\xAnalogLED.h
- \common\xInterpolator.h
- \common\xCodReader.h
-
- xAnalogLED - controls a multi-emitter LED
- xAnalogLED_DriverInterface - interface for drivers
- xColorRenderer_Interface - interface for color renderers
-
- SimpleBlade
- SimpleBladePtr
-
- PWMPinInterface
- SimplePWMPin
- PWMPin
- MultiChannelLED
- LEDInterface
- ColorSelector
- DriveLogic
- LEDArrayHelper
(C) RSX Engineering SRL. FileVer. 1.1 / Oct. 2022
- Board Configuration (coming soon)
- Install Configuration
- User Profile
- Twist & Tick Menu (coming soon)
- UltraSaber Prop (coming soon)
- COD Reader
- Interpolators
- CPU Probes
- Developer's Setup(coming soon)