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