1 /*
2 * Copyright (c) 2016-2019, Texas Instruments Incorporated
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of Texas Instruments Incorporated nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 /*
34 * ======== LED.c ========
35 */
36
37 /* Drivers */
38 #include <ti/drivers/GPIO.h>
39 #include <ti/drivers/PWM.h>
40 #include <ti/drivers/dpl/ClockP.h>
41
42 /* Module Header */
43 #include <ti/drivers/apps/LED.h>
44
45 #define LED_PWMPERIOD_1MS 1000U /* Default PWM period is 1 ms*/
46
47 /* Default LED parameters structure */
48 const LED_Params LED_defaultParams = {
49 .pwmPeriod = LED_PWMPERIOD_1MS, /* Default PWM period is 1 ms*/
50 .brightness = LED_BRIGHTNESS_MAX, /* Start at max brightness*/
51 .setState = LED_STATE_OFF, /* Set LED state to OFF*/
52 .blinkPeriod = 0 /* No blinking*/
53 };
54
55 extern LED_Config LED_config[];
56
57 /* Number of user defined LED configs */
58 extern const uint_least8_t LED_count;
59
60 /* Local functions: */
61
62 /*
63 * ======== clockTimeoutHandler ========
64 * Called when blinker clock times out
65 */
clockTimeoutHandler(uintptr_t arg)66 static void clockTimeoutHandler(uintptr_t arg)
67 {
68 LED_Object * obj = ((LED_Handle)arg)->object;
69 ClockP_setTimeout(obj->clockHandle, obj->togglePeriod);
70 ClockP_start(obj->clockHandle);
71 LED_toggle((LED_Handle)arg);
72
73 /* Handle blink counting */
74 if(obj->blinkCount != LED_BLINK_FOREVER &&
75 obj->blinkCount != 0)
76 {
77 obj->blinkCount--;
78 if(obj->blinkCount == 0)
79 {
80 /* Hit the requested number of blinks */
81 LED_stopBlinking((LED_Handle)arg);
82 }
83 }
84 }
85
86 /* API functions: */
87
88 /*
89 * ======== LED_close ========
90 * Closes an instance of a LED sensor.
91 */
LED_close(LED_Handle ledHandle)92 void LED_close(LED_Handle ledHandle)
93 {
94 LED_Object *obj = (LED_Object *)(ledHandle->object);
95
96 LED_stopBlinking(ledHandle);
97 LED_setOff(ledHandle);
98
99 /* Delete clock instance */
100 ClockP_delete(obj->clockHandle);
101 obj->clockHandle = NULL;
102
103 /* Close PWM handle if applicable */
104 if(obj->pwmHandle != NULL)
105 {
106 PWM_close(obj->pwmHandle);
107 obj->pwmHandle = NULL;
108 }
109
110 /* Set type to NONE to indicate the instance is closed */
111 obj->ledType = LED_NONE;
112 }
113
114 /*
115 * ======== LED_getState ========
116 */
LED_getState(LED_Handle ledHandle)117 LED_State LED_getState(LED_Handle ledHandle)
118 {
119 LED_Object *obj = (LED_Object *)(ledHandle->object);
120
121 return(obj->state);
122 }
123
124 /*
125 * ======== LED_init ========
126 */
LED_init()127 void LED_init()
128 {
129 /*
130 * Must NOT call GPIO or PWM init here as the weak symbol may be used.
131 * Init will be done at open time.
132 */
133 }
134
135 /*
136 * ======== LED_open ========
137 * Sets up LED and returns LED_Handle
138 */
LED_open(uint_least8_t ledIndex,LED_Params * params)139 LED_Handle LED_open(uint_least8_t ledIndex, LED_Params *params)
140 {
141 LED_Handle ledHandle;
142 LED_Object *obj;
143 LED_HWAttrs *hw;
144 PWM_Params pwmParams;
145 ClockP_Params clockParams;
146
147 ledHandle = (LED_Handle)(&LED_config[ledIndex]);
148 obj = (LED_Object *)(LED_config[ledIndex].object);
149 hw = (LED_HWAttrs *)(LED_config[ledIndex].hwAttrs);
150
151 /* ledIndex cannot be more than number of available LEDs */
152 if((ledIndex >= LED_count))
153 {
154 return(NULL);
155 }
156
157 /* If params are NULL use defaults. */
158 if(params == NULL)
159 {
160 params = (LED_Params *)&LED_defaultParams;
161 }
162
163 /* If we already have a ledType then the instance is already open */
164 if(obj->ledType != LED_NONE)
165 {
166 return(NULL);
167 }
168
169 if(hw->type == LED_PWM_CONTROLLED)
170 {
171 obj->ledType = LED_PWM_CONTROLLED;
172
173 /* PWM init must only be called if a PWM_config is strongly declared */
174 PWM_init();
175
176 PWM_Params_init(&pwmParams);
177 pwmParams.periodValue = params->pwmPeriod;
178 pwmParams.dutyUnits = PWM_DUTY_US;
179 pwmParams.periodUnits = PWM_PERIOD_US;
180 pwmParams.dutyValue = 0;
181
182 /* Open the PWM instance */
183 obj->pwmHandle = PWM_open(hw->index, &pwmParams);
184 if(NULL == obj->pwmHandle)
185 {
186 return(NULL);
187 }
188
189 /* Handle params and start conditions */
190 PWM_start(obj->pwmHandle);
191 if(params->setState != LED_STATE_OFF)
192 {
193 PWM_setDuty(obj->pwmHandle,
194 params->pwmPeriod * params->brightness / 100);
195 }
196 }
197 else
198 {
199 /* GPIO init must only be called if a GPIO_config is strongly declared */
200 GPIO_init();
201 /* Store gpio index so we don't have to potentially read from flash */
202 obj->gpioIndex = hw->index;
203 obj->ledType = LED_GPIO_CONTROLLED;
204 }
205
206 /* Create the clock instance */
207 ClockP_Params_init(&clockParams);
208 clockParams.startFlag = false;
209 clockParams.arg = (uintptr_t)ledHandle;
210 clockParams.period = 0; // One shot clock
211 obj->clockHandle = ClockP_create(clockTimeoutHandler, 0, &clockParams);
212 if(NULL == obj->clockHandle)
213 {
214 /* Also close PWM if we opened one */
215 if(obj->pwmHandle != NULL)
216 {
217 PWM_close(obj->pwmHandle);
218 }
219 return(NULL);
220 }
221
222 /* Update Object fields*/
223 obj->pwmPeriod = params->pwmPeriod;
224 obj->brightness = params->brightness;
225
226 /* Set LED state, default is Off if setState is not modified by user*/
227 switch (params->setState)
228 {
229 case LED_STATE_OFF:
230 LED_setOff(ledHandle);
231 break;
232
233 case LED_STATE_ON:
234 LED_setOn(ledHandle, obj->brightness);
235 break;
236
237 case LED_STATE_BLINKING:
238 LED_startBlinking(ledHandle,
239 (params->blinkPeriod)/2,
240 LED_BLINK_FOREVER);
241 break;
242
243 default:
244 /* Invalid setState value*/
245 break;
246 }
247
248 return(ledHandle);
249 }
250
251 /*
252 * ======== LED_Params_init ========
253 * Initialize a LED_Params struct to default settings.
254 */
LED_Params_init(LED_Params * params)255 void LED_Params_init(LED_Params *params)
256 {
257 *params = LED_defaultParams;
258 }
259
260 /*
261 * ======== LED_setBrightnessLevel ========
262 * Sets brightness level as requested.
263 */
LED_setBrightnessLevel(LED_Handle ledHandle,uint8_t level)264 bool LED_setBrightnessLevel(LED_Handle ledHandle, uint8_t level)
265 {
266 bool ret;
267 uint32_t duty = 0;
268 LED_Object *obj = (LED_Object *)(ledHandle->object);
269
270 /* If in GPIO mode or a pwm handle was not provided, fail */
271 if(NULL == obj->pwmHandle)
272 {
273 return(false);
274 }
275
276 /* Report false if brightness request is more than maximum(100%) level */
277 if(level > LED_BRIGHTNESS_MAX)
278 {
279 return(false);
280 }
281
282 /* Calculate duty based on requested level and set that */
283 duty = (obj->pwmPeriod * level)/100;
284 if(PWM_setDuty(obj->pwmHandle, duty) == PWM_STATUS_SUCCESS)
285 {
286 ret = true;
287 obj->brightness = level;
288 }
289 else
290 {
291 ret = false;
292 }
293
294 if(duty > 0)
295 {
296 obj->rawState = LED_STATE_ON;
297 }
298 else
299 {
300 obj->rawState = LED_STATE_OFF;
301 }
302
303 return(ret);
304 }
305
306 /*
307 * ======== LED_setOff ========
308 * Turns Off a specified a LED sensor.
309 */
LED_setOff(LED_Handle ledHandle)310 bool LED_setOff(LED_Handle ledHandle)
311 {
312 LED_Object *obj = (LED_Object *)(ledHandle->object);
313 uint16_t level;
314 bool ret = true;
315
316 obj->rawState = LED_STATE_OFF;
317
318 if(obj->ledType == LED_GPIO_CONTROLLED)
319 {
320 GPIO_write(obj->gpioIndex, LED_OFF);
321 }
322
323 /* For PWM LED, set brightness to zero
324 * Also, restoring brightness level which was there before turning it Off
325 * so that Toggle APIs can set same brightness while turning it On */
326 else
327 {
328 level = obj->brightness;
329 ret = LED_setBrightnessLevel(ledHandle, LED_BRIGHTNESS_MIN);
330 obj->brightness = level;
331 }
332
333 /* Set LED state
334 * If LED is blinking, which is a separate state(mix of ON + OFF), no need
335 * to change state; rawState contains the actual ON or OFF state*/
336 if(obj->state != LED_STATE_BLINKING)
337 {
338 obj->state = LED_STATE_OFF;
339 }
340
341 return(ret);
342 }
343
344 /*
345 * ======== LED_setOn ========
346 * Turns On a specified LED sensor.
347 */
LED_setOn(LED_Handle ledHandle,uint8_t brightness)348 bool LED_setOn(LED_Handle ledHandle, uint8_t brightness)
349 {
350 bool ret = true;
351 LED_Object *obj = (LED_Object *)(ledHandle->object);
352
353 if(obj->ledType == LED_GPIO_CONTROLLED)
354 {
355 GPIO_write(obj->gpioIndex, LED_ON);
356 }
357
358 /* For PWM LED, turn it On with requested level*/
359 else
360 {
361 ret = LED_setBrightnessLevel(ledHandle, brightness);
362 }
363
364 /* Set LED state(conditional) and rawState(always)*/
365 if(ret == true)
366 {
367 if(obj->state != LED_STATE_BLINKING)
368 {
369 obj->state = LED_STATE_ON;
370 }
371
372 obj->rawState = LED_STATE_ON;
373 }
374
375 return(ret);
376 }
377
378 /*
379 * ======== LED_startBlinking ========
380 * Starts blinking an LED with specified period and specified number of times
381 */
LED_startBlinking(LED_Handle ledHandle,uint16_t blinkPeriod,uint16_t blinkCount)382 void LED_startBlinking(LED_Handle ledHandle,
383 uint16_t blinkPeriod,
384 uint16_t blinkCount)
385 {
386 LED_Object *obj = (LED_Object *)(ledHandle->object);
387
388 if(blinkPeriod == 0 || blinkCount == 0)
389 {
390 LED_stopBlinking(ledHandle);
391 }
392 else
393 {
394 /*
395 * Start the periodic clock with period = blinkperiod in ms units. If
396 * not blinking forever, we set the number of timeouts to be twice the
397 * number of requested blinks since one blink is two toggles.
398 */
399 LED_setOff(ledHandle);
400 ClockP_stop(obj->clockHandle);
401
402 if (blinkCount == LED_BLINK_FOREVER)
403 {
404 /*
405 * clockTimeoutHandler knows not to decrement blinkCount when it
406 * is set to LED_BLINK_FOREVER
407 */
408 obj->blinkCount = LED_BLINK_FOREVER;
409 }
410 else
411 {
412 /* Don't overflow or set blinkCount to LED_BLINK_FOREVER */
413 if (blinkCount > 0x7FFF)
414 {
415 blinkCount = 0x7FFF;
416 }
417 obj->blinkCount = 2 * blinkCount;
418 }
419
420 obj->togglePeriod = ClockP_convertMsToSystemTicksRound(blinkPeriod/2);
421 ClockP_setTimeout(obj->clockHandle, obj->togglePeriod);
422 ClockP_start(obj->clockHandle);
423 obj->state = LED_STATE_BLINKING;
424 }
425 }
426
427 /*
428 * ======== LED_stopBlinking ========
429 * Stops blinking a led.
430 */
LED_stopBlinking(LED_Handle ledHandle)431 void LED_stopBlinking(LED_Handle ledHandle)
432 {
433 LED_Object *obj = (LED_Object *)(ledHandle->object);
434
435 if(obj->state == LED_STATE_BLINKING)
436 {
437 /* Stop the clock*/
438 ClockP_stop(obj->clockHandle);
439
440 /* After stopping blinking, sets LED Off, a default LED state*/
441 LED_setOff(ledHandle);
442 obj->state = LED_STATE_OFF;
443 }
444 else
445 {
446 /* If LED is not blinking, no need to stop it, so ignore the request*/
447 }
448 }
449
450 /*
451 * ======== LED_toggle ========
452 * Toggle a led.
453 */
LED_toggle(LED_Handle ledHandle)454 void LED_toggle(LED_Handle ledHandle)
455 {
456 LED_Object *obj = (LED_Object *)(ledHandle->object);
457
458 if(obj->rawState == LED_STATE_ON)
459 {
460 LED_setOff(ledHandle);
461 }
462 else if(obj->rawState == LED_STATE_OFF)
463 {
464 LED_setOn(ledHandle, obj->brightness);
465 }
466 }
467
468 /*
469 * ======== LED_write ========
470 * Set the state of an LED instance
471 */
LED_write(LED_Handle ledHandle,bool value)472 void LED_write(LED_Handle ledHandle, bool value)
473 {
474 LED_Object *obj = (LED_Object *)(ledHandle->object);
475 if(value)
476 {
477 LED_setOn(ledHandle, obj->brightness);
478 }
479 else
480 {
481 LED_setOff(ledHandle);
482 }
483 }
484