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