• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Generic GPIO led
3  *
4  * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22  * IN THE SOFTWARE.
23  */
24 #include "private-lib-core.h"
25 
26 static const lws_led_intensity_t sineq16[] = {
27 
28 	/*
29 	 * Quadrant at sin(270) in 16 samples, normalized so
30 	 * -1 == 0 and 0 == 32767
31 	 */
32 
33 	    0,   158,   630,  1411,  2494,  3869,  5522,  7437,
34 	 9597, 11980, 14562, 17321, 20228, 23225, 26374, 29555,
35 	32767 /* to interpolate against */
36 };
37 
38 /*
39  * Elaborate the 90 degree phase table to 360 degrees and offset to +32768,
40  * notice for the last sample we have to interpolate against a 17th sample
41  * reflecting full scale to avoid clipping due to interpolation against the
42  * 16th sample again
43  */
44 
45 static lws_led_intensity_t
sine_lu(int n,int next)46 sine_lu(int n, int next)
47 {
48         switch ((n >> 4) & 3) {
49         case 1:
50         	/* forwards */
51                 return 32768 + sineq16[(n & 15) + next];
52         case 2:
53         	/* scan it backwards */
54                 return 32768 + sineq16[15 - (n & 15) + (!next)];
55         case 3:
56         	/* forwards */
57                 return 32768 - sineq16[(n & 15) + next];
58         default:
59         	/* scan it backwards */
60                 return 32768 - sineq16[15 - (n & 15) + (!next)];
61         }
62 }
63 
64 /*
65  * The normalized phase resolution is 16-bit, however much table you decide to
66  * have needs interpolating or indexing in a reduced number of significant
67  * phase bits if it doesn't have the same phase resolution.
68  *
69  * In this sine table we have a 16 x 15-bit sample quadrant reflected 4 times
70  * to make 360 degrees, so 64 accurate sample points, with the rest of the
71  * intermediate phases generated by linear interpolation.  That probably would
72  * sound a bit funky, but for modulating light dynamically it's more than
73  * enough.
74  */
75 
76 lws_led_intensity_t
lws_led_func_sine(lws_led_seq_phase_t n)77 lws_led_func_sine(lws_led_seq_phase_t n)
78 {
79         /*
80          * 2: quadrant
81          * 4: table entry in quadrant
82          * 10: interp (LSB)
83          */
84 
85         return (sine_lu(n >> 10, 0) * (0x3ff - (n & 0x3ff)) +
86         	sine_lu(n >> 10, 1) * (n & 0x3ff)) / 0x3ff;
87 }
88 
89 lws_led_intensity_t
lws_led_func_linear(lws_led_seq_phase_t n)90 lws_led_func_linear(lws_led_seq_phase_t n)
91 {
92 	return (lws_led_intensity_t)n;
93 }
94 
95 
96 static lws_led_intensity_t
lws_led_func_static(lws_led_seq_phase_t n)97 lws_led_func_static(lws_led_seq_phase_t n)
98 {
99 	return ((int)n * LWS_LED_MAX_INTENSITY) / 2;
100 }
101 
102 const lws_led_sequence_def_t lws_pwmseq_static_off = {
103 	.func			= lws_led_func_static,
104 	.ledphase_offset	= 0,
105 	.ledphase_total		= 0,
106 	.ms			= 0
107 };
108 
109 const lws_led_sequence_def_t lws_pwmseq_static_half = {
110 	.func			= lws_led_func_static,
111 	.ledphase_offset	= 1,
112 	.ledphase_total		= 0,
113 	.ms			= 0
114 };
115 
116 const lws_led_sequence_def_t lws_pwmseq_static_on = {
117 	.func			= lws_led_func_static,
118 	.ledphase_offset	= 2,
119 	.ledphase_total		= 0,
120 	.ms			= 0
121 };
122 
123 const lws_led_sequence_def_t lws_pwmseq_sine_up = {
124 	.func			= lws_led_func_sine,
125 	.ledphase_offset	= 0, /* already at 0 amp at 0 phase */
126 	.ledphase_total		= LWS_LED_FUNC_PHASE / 2, /* 180 degree ./^ */
127 	.ms			= 300
128 };
129 
130 const lws_led_sequence_def_t lws_pwmseq_sine_down = {
131 	.func			= lws_led_func_sine,
132 	.ledphase_offset	= LWS_LED_FUNC_PHASE / 2, /* start at peak */
133 	.ledphase_total		= LWS_LED_FUNC_PHASE / 2, /* 180 degree ./^ */
134 	.ms			= 300
135 };
136 
137 const lws_led_sequence_def_t lws_pwmseq_linear_wipe = {
138 	.func			= lws_led_func_linear,
139 	.ledphase_offset	= 0,
140 	.ledphase_total		= LWS_LED_FUNC_PHASE - 1,
141 	.ms			= 300
142 };
143 
144 const lws_led_sequence_def_t lws_pwmseq_sine_endless_slow = {
145 	.func			= lws_led_func_sine,
146 	.ledphase_offset	= 0, /* already at 0 amp at 0 phase */
147 	.ledphase_total		= LWS_SEQ_LEDPHASE_TOTAL_ENDLESS,
148 	.ms			= 1500
149 };
150 
151 const lws_led_sequence_def_t lws_pwmseq_sine_endless_fast = {
152 	.func			= lws_led_func_sine,
153 	.ledphase_offset	= 0, /* already at 0 amp at 0 phase */
154 	.ledphase_total		= LWS_SEQ_LEDPHASE_TOTAL_ENDLESS,
155 	.ms			= 750
156 };
157