• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# lws_led gpio and pwm class drivers
2
3Lws provides an abstract led controller class that can bind an array of LEDs
4to gpio and pwm controllers, and automatically handled pwm sequencers.
5
6Lumience intensity is corrected for IEC curves to match perceptual intensity,
7and the correction can be overridden per led for curve adaptation matching.
8
9Intensity is normalized to a 16-bit scale, when controlled by a GPIO b15 is
10significant and the rest ignored.  When controlled by PWM, as many bits from
11b15 down are significant as the PWM arrangements can represent.
12
13The PWM sequencers use arbitrary function generation callbacks on a normalized
1416-bit phase space, they can choose how much to interpolate and how much to put
15in a table, a 64-sample, 16-bit sine function is provided along with 16-bit
16linear sawtooth.
17
18Changing the sequencer is subject to a third transition function sequencer, this
19can for example mix the transition linearly over, eg, 500ms so the leds look
20very smooth.
21
22## Defining an led controller
23
24An array of inidividual LED information is provided first, and referenced by
25the LED controller definintion.  Leds are named so code does not introduce
26dependencies on specific implementations.
27
28```
29static const lws_led_gpio_map_t lgm[] = {
30	{
31		.name			= "alert",
32		.gpio			= GPIO_NUM_25,
33		.pwm_ops		= &pwm_ops,
34		.active_level		= 1,
35	},
36};
37
38static const lws_led_gpio_controller_t lgc = {
39	.led_ops			= lws_led_gpio_ops,
40	.gpio_ops			= &lws_gpio_plat,
41	.led_map			= &lgm[0],
42	.count_leds			= LWS_ARRAY_SIZE(lgm)
43};
44
45	struct lws_led_state *lls;
46
47	lls = lgc.led_ops.create(&lgc.led_ops);
48	if (!lls) {
49		lwsl_err("%s: could not create led\n", __func__);
50		goto spin;
51	}
52
53```
54
55For GPIO control, the active level of the GPIO to light the LED may be set.
56
57Each LED may bind to a pwm controller, in which case setting the intensity
58programs the pwm controller corresponding to the GPIO.
59
60## Setting the intensity directly
61
62```
63	lgc.led_ops.intensity(&lgc.led_ops, "alert", 0);
64```
65
66## Defining Sequencer
67
68Some common sequencers are provided out of the box, you can also define your
69own arbitrary ones.
70
71The main point is sequencers have a function that returns an intensity for each
72of 65536 phase steps in its cycle.  For example, this is the linear function
73that is included
74
75```
76lws_led_intensity_t
77lws_led_func_linear(lws_led_seq_phase_t n)
78{
79	return (lws_led_intensity_t)n;
80}
81```
82
83It simply returns an intensity between 0 - 65535 matching the phase angle of
840 - 65535 that it was given, so it's a sawtooth ramp.
85
86An interpolated sine function is also provided that returns an intensity
87between 0 - 65535 reflecting one cycle of sine wave for the phase angle of 0 -
8865535.
89
90These functions are packaged into sequencer structures like this
91
92```
93const lws_led_sequence_def_t lws_pwmseq_sine_endless_fast = {
94	.func			= lws_led_func_sine,
95	.ledphase_offset	= 0, /* already at 0 amp at 0 phase */
96	.ledphase_total		= LWS_SEQ_LEDPHASE_TOTAL_ENDLESS,
97	.ms			= 750
98};
99```
100
101This "endless" sequencer cycles through the sine function at 750ms per cycle.
102Non-endless sequencers have a specific start and end in the phase space, eg
103
104```
105const lws_led_sequence_def_t lws_pwmseq_sine_up = {
106	.func			= lws_led_func_sine,
107	.ledphase_offset	= 0, /* already at 0 amp at 0 phase */
108	.ledphase_total		= LWS_LED_FUNC_PHASE / 2, /* 180 degree ./^ */
109	.ms			= 300
110};
111```
112
113... this one traverses 180 degrees of the sine wave starting from 0 and ending
114at full intensity, over 300ms.
115
116A commonly-used, provided one is like this, as used in the next section
117
118```
119const lws_led_sequence_def_t lws_pwmseq_linear_wipe = {
120	.func			= lws_led_func_linear,
121	.ledphase_offset	= 0,
122	.ledphase_total		= LWS_LED_FUNC_PHASE - 1,
123	.ms			= 300
124};
125```
126
127## Setting the intensity using sequencer transitions
128
129The main api for high level sequenced control is
130
131```
132int
133lws_led_transition(struct lws_led_state *lcs, const char *name,
134		   const lws_led_sequence_def_t *next,
135		   const lws_led_sequence_def_t *trans);
136```
137
138This fades from the current sequence to a new sequence, using `trans` sequencer
139intensity as the mix factor.  `trans` is typically `lws_pwmseq_linear_wipe`,
140fading between the current and new linearly over 300ms.  At the end of the
141`trans` sequence, the new sequence simply replaces the current one and the
142transition is completed.
143
144Sequencers use a single 30Hz OS timer while any sequence is active.
145
146exported sequencer symbol|description
147---|---
148lws_pwmseq_sine_endless_slow|continuous 100% sine, 1.5s cycle
149lws_pwmseq_sine_endless_fast|continuous 100% sine, 0.75s cycle
150lws_pwmseq_linear_wipe|single 0 - 100% ramp over 0.3s
151lws_pwmseq_sine_up|single 0 - 100% using sine curve over 0.3s
152lws_pwmseq_sine_down|single 100% - 0 using sine curve over 0.3s
153lws_pwmseq_static_on|100% static
154lws_pwmseq_static_half|50% static
155lws_pwmseq_static_off|0% static
156