Add lws_display and minimal example support for esp32-wrover to match wsp32-heltec-wb32 Since no usable buttons that don't affect something else on wrover kit, assumes a button to 0V on GPIO14.
4.7 KiB
lws_led gpio and pwm class drivers
Lws provides an abstract led controller class that can bind an array of LEDs to gpio and pwm controllers, and automatically handled pwm sequencers.
Lumience intensity is corrected for IEC curves to match perceptual intensity, and the correction can be overridden per led for curve adaptation matching.
Intensity is normalized to a 16-bit scale, when controlled by a GPIO b15 is significant and the rest ignored. When controlled by PWM, as many bits from b15 down are significant as the PWM arrangements can represent.
The PWM sequencers use arbitrary function generation callbacks on a normalized 16-bit phase space, they can choose how much to interpolate and how much to put in a table, a 64-sample, 16-bit sine function is provided along with 16-bit linear sawtooth.
Changing the sequencer is subject to a third transition function sequencer, this can for example mix the transition linearly over, eg, 500ms so the leds look very smooth.
Defining an led controller
An array of inidividual LED information is provided first, and referenced by the LED controller definintion. Leds are named so code does not introduce dependencies on specific implementations.
static const lws_led_gpio_map_t lgm[] = {
{
.name = "alert",
.gpio = GPIO_NUM_25,
.pwm_ops = &pwm_ops,
.active_level = 1,
},
};
static const lws_led_gpio_controller_t lgc = {
.led_ops = lws_led_gpio_ops,
.gpio_ops = &lws_gpio_plat,
.led_map = &lgm[0],
.count_leds = LWS_ARRAY_SIZE(lgm)
};
struct lws_led_state *lls;
lls = lgc.led_ops.create(&lgc.led_ops);
if (!lls) {
lwsl_err("%s: could not create led\n", __func__);
goto spin;
}
For GPIO control, the active level of the GPIO to light the LED may be set.
Each LED may bind to a pwm controller, in which case setting the intensity programs the pwm controller corresponding to the GPIO.
Setting the intensity directly
lgc.led_ops.intensity(&lgc.led_ops, "alert", 0);
Defining Sequencer
Some common sequencers are provided out of the box, you can also define your own arbitrary ones.
The main point is sequencers have a function that returns an intensity for each of 65536 phase steps in its cycle. For example, this is the linear function that is included
lws_led_intensity_t
lws_led_func_linear(lws_led_seq_phase_t n)
{
return (lws_led_intensity_t)n;
}
It simply returns an intensity between 0 - 65535 matching the phase angle of 0 - 65535 that it was given, so it's a sawtooth ramp.
An interpolated sine function is also provided that returns an intensity between 0 - 65535 reflecting one cycle of sine wave for the phase angle of 0 - 65535.
These functions are packaged into sequencer structures like this
const lws_led_sequence_def_t lws_pwmseq_sine_endless_fast = {
.func = lws_led_func_sine,
.ledphase_offset = 0, /* already at 0 amp at 0 phase */
.ledphase_total = LWS_SEQ_LEDPHASE_TOTAL_ENDLESS,
.ms = 750
};
This "endless" sequencer cycles through the sine function at 750ms per cycle. Non-endless sequencers have a specific start and end in the phase space, eg
const lws_led_sequence_def_t lws_pwmseq_sine_up = {
.func = lws_led_func_sine,
.ledphase_offset = 0, /* already at 0 amp at 0 phase */
.ledphase_total = LWS_LED_FUNC_PHASE / 2, /* 180 degree ./^ */
.ms = 300
};
... this one traverses 180 degrees of the sine wave starting from 0 and ending at full intensity, over 300ms.
A commonly-used, provided one is like this, as used in the next section
const lws_led_sequence_def_t lws_pwmseq_linear_wipe = {
.func = lws_led_func_linear,
.ledphase_offset = 0,
.ledphase_total = LWS_LED_FUNC_PHASE - 1,
.ms = 300
};
Setting the intensity using sequencer transitions
The main api for high level sequenced control is
int
lws_led_transition(struct lws_led_state *lcs, const char *name,
const lws_led_sequence_def_t *next,
const lws_led_sequence_def_t *trans);
This fades from the current sequence to a new sequence, using trans
sequencer
intensity as the mix factor. trans
is typically lws_pwmseq_linear_wipe
,
fading between the current and new linearly over 300ms. At the end of the
trans
sequence, the new sequence simply replaces the current one and the
transition is completed.
Sequencers use a single 30Hz OS timer while any sequence is active.
exported sequencer symbol | description |
---|---|
lws_pwmseq_sine_endless_slow | continuous 100% sine, 1.5s cycle |
lws_pwmseq_sine_endless_fast | continuous 100% sine, 0.75s cycle |
lws_pwmseq_linear_wipe | single 0 - 100% ramp over 0.3s |
lws_pwmseq_sine_up | single 0 - 100% using sine curve over 0.3s |
lws_pwmseq_sine_down | single 100% - 0 using sine curve over 0.3s |
lws_pwmseq_static_on | 100% static |
lws_pwmseq_static_half | 50% static |
lws_pwmseq_static_off | 0% static |