1 /*
2 * Copyright (c) 2015-2018, 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 * By default disable both asserts and log for this module.
35 * This must be done before DebugP.h is included.
36 */
37 #ifndef DebugP_ASSERT_ENABLED
38 #define DebugP_ASSERT_ENABLED 0
39 #endif
40 #ifndef DebugP_LOG_ENABLED
41 #define DebugP_LOG_ENABLED 0
42 #endif
43
44 #include <stdbool.h>
45
46 #include <ti/drivers/dpl/ClockP.h>
47 #include <ti/drivers/dpl/DebugP.h>
48 #include <ti/drivers/dpl/HwiP.h>
49
50 #include <ti/drivers/Power.h>
51 #include <ti/drivers/power/PowerCC32XX.h>
52 #include <ti/drivers/pwm/PWMTimerCC32XX.h>
53 #include <ti/drivers/timer/TimerCC32XX.h>
54
55 #include <ti/devices/cc32xx/inc/hw_apps_config.h>
56 #include <ti/devices/cc32xx/inc/hw_memmap.h>
57 #include <ti/devices/cc32xx/inc/hw_ocp_shared.h>
58 #include <ti/devices/cc32xx/inc/hw_types.h>
59 #include <ti/devices/cc32xx/inc/hw_timer.h>
60 #include <ti/devices/cc32xx/driverlib/rom.h>
61 #include <ti/devices/cc32xx/driverlib/rom_map.h>
62 #include <ti/devices/cc32xx/driverlib/gpio.h>
63 #include <ti/devices/cc32xx/driverlib/pin.h>
64 #include <ti/devices/cc32xx/driverlib/timer.h>
65
66 #define PAD_CONFIG_BASE (OCP_SHARED_BASE + OCP_SHARED_O_GPIO_PAD_CONFIG_0)
67 #define PAD_RESET_STATE 0xC61
68
69 /*!
70 * @brief If the PWM period is lower than this value, setDutyAndPeriod
71 * will briefly disable the PWM channel to set the new values.
72 *
73 * This is to prevent the case where the period, but not the duty, is
74 * applied before the timeout and the next cycle is in an undetermined state.
75 */
76 #define PWM_PERIOD_FOR_GLITCH_PROTECTION 0xF
77
78 void PWMTimerCC32XX_close(PWM_Handle handle);
79 int_fast16_t PWMTimerCC32XX_control(PWM_Handle handle, uint_fast16_t cmd,
80 void *arg);
81 void PWMTimerCC32XX_init(PWM_Handle handle);
82 PWM_Handle PWMTimerCC32XX_open(PWM_Handle handle, PWM_Params *params);
83 int_fast16_t PWMTimerCC32XX_setDuty(PWM_Handle handle, uint32_t dutyValue);
84 int_fast16_t PWMTimerCC32XX_setPeriod(PWM_Handle handle, uint32_t periodValue);
85 int_fast16_t PWMTimerCC32XX_setDutyAndPeriod(PWM_Handle handle, uint32_t dutyValue, uint32_t periodValue);
86 void PWMTimerCC32XX_start(PWM_Handle handle);
87 void PWMTimerCC32XX_stop(PWM_Handle handle);
88
89 /* PWM function table for PWMTimerCC32XX implementation */
90 const PWM_FxnTable PWMTimerCC32XX_fxnTable = {
91 PWMTimerCC32XX_close,
92 PWMTimerCC32XX_control,
93 PWMTimerCC32XX_init,
94 PWMTimerCC32XX_open,
95 PWMTimerCC32XX_setDuty,
96 PWMTimerCC32XX_setPeriod,
97 PWMTimerCC32XX_setDutyAndPeriod,
98 PWMTimerCC32XX_start,
99 PWMTimerCC32XX_stop
100 };
101
102 /*
103 * Internal value to notify an error has occurred while calculating a duty
104 * or period.
105 */
106 static const uint32_t PWM_INVALID_VALUE = (~0);
107
108 /*
109 * GPT peripheral load & match registers are 16 bits wide. Max value which
110 * can be set is 65535.
111 */
112 static const uint16_t PWM_MAX_MATCH_REG_VALUE = (~0);
113
114 /*
115 * GPT peripherals have 24 bit resolution. The max period value which be
116 * set is 16777215.
117 */
118 static const uint32_t PWM_MAX_PERIOD_COUNT = (0xFFFFFF);
119
120 /*
121 * The following fields are used by CC32XX driverlib APIs and therefore
122 * must be populated by driverlib macro definitions. For CC32XX driverlib
123 * these definitions are found in:
124 * - inc/hw_memmap.h
125 * - driverlib/gpio.h
126 * - driverlib/pin.h
127 * - driverlib/timer.h
128 */
129 static const uint32_t timerBaseAddresses[4] = {
130 TIMERA0_BASE,
131 TIMERA1_BASE,
132 TIMERA2_BASE,
133 TIMERA3_BASE,
134 };
135
136 static const uint32_t timerHalves[2] = {
137 TIMER_A,
138 TIMER_B,
139 };
140
141 #define NUMGPIOPORTS 4
142 static const uint32_t gpioBaseAddresses[NUMGPIOPORTS] = {
143 GPIOA0_BASE,
144 GPIOA1_BASE,
145 GPIOA2_BASE,
146 GPIOA3_BASE,
147 };
148
149 #define NUMGPIOPINS 8
150 static const uint32_t gpioPinIndexes[NUMGPIOPINS] = {
151 GPIO_PIN_0,
152 GPIO_PIN_1,
153 GPIO_PIN_2,
154 GPIO_PIN_3,
155 GPIO_PIN_4,
156 GPIO_PIN_5,
157 GPIO_PIN_6,
158 GPIO_PIN_7,
159 };
160
161 #define PinConfigTimerPort(config) (((config) >> 28) & 0xF)
162 #define PinConfigTimerHalf(config) (((config) >> 24) & 0xF)
163 #define PinConfigGPIOPort(config) (((config) >> 20) & 0xF)
164 #define PinConfigGPIOPinIndex(config) (((config) >> 16) & 0xF)
165 #define PinConfigPinMode(config) (((config) >> 8) & 0xF)
166 #define PinConfigPin(config) (((config) >> 0) & 0x3F)
167
168 /*
169 * ======== getDutyCounts ========
170 */
getDutyCounts(PWM_Duty_Units dutyUnits,uint32_t dutyValue,uint32_t periodCounts)171 static uint32_t getDutyCounts(PWM_Duty_Units dutyUnits, uint32_t dutyValue,
172 uint32_t periodCounts)
173 {
174 uint32_t duty = 0;
175 ClockP_FreqHz freq;
176
177 ClockP_getCpuFreq(&freq);
178
179 switch (dutyUnits) {
180 case PWM_DUTY_COUNTS:
181 duty = dutyValue;
182 break;
183
184 case PWM_DUTY_FRACTION:
185 duty = (((uint64_t) dutyValue) * ((uint64_t) periodCounts)) /
186 PWM_DUTY_FRACTION_MAX;
187 break;
188
189 case PWM_DUTY_US:
190 duty = (dutyValue != 0) ? (dutyValue * (freq.lo/1000000)) - 1 : 0;
191 break;
192
193 default:
194 /* Unsupported duty units return an invalid duty */
195 duty = PWM_INVALID_VALUE;
196 }
197
198 return (duty);
199 }
200
201 /*
202 * ======== getPeriodCounts ========
203 */
getPeriodCounts(PWM_Period_Units periodUnits,uint32_t periodValue)204 static uint32_t getPeriodCounts(PWM_Period_Units periodUnits,
205 uint32_t periodValue)
206 {
207 uint32_t period = 0;
208 ClockP_FreqHz freq;
209
210 ClockP_getCpuFreq(&freq);
211
212 switch (periodUnits) {
213 case PWM_PERIOD_COUNTS:
214 period = periodValue;
215 break;
216
217 case PWM_PERIOD_HZ:
218 if (periodValue && periodValue <= freq.lo) {
219 period = (freq.lo / periodValue) - 1;
220 }
221 break;
222
223 case PWM_PERIOD_US:
224 period = (periodValue * (freq.lo/1000000)) - 1;
225 break;
226
227 default:
228 /* Unsupported period units return an invalid period */
229 period = PWM_INVALID_VALUE;
230 }
231
232 return (period);
233 }
234
235 /*
236 * ======== getPowerMgrId ========
237 */
getPowerMgrId(uint32_t baseAddr)238 static uint_fast16_t getPowerMgrId(uint32_t baseAddr)
239 {
240 switch (baseAddr) {
241 case GPIOA0_BASE:
242 return (PowerCC32XX_PERIPH_GPIOA0);
243 case GPIOA1_BASE:
244 return (PowerCC32XX_PERIPH_GPIOA1);
245 case GPIOA2_BASE:
246 return (PowerCC32XX_PERIPH_GPIOA2);
247 case GPIOA3_BASE:
248 return (PowerCC32XX_PERIPH_GPIOA3);
249 case GPIOA4_BASE:
250 return (PowerCC32XX_PERIPH_GPIOA4);
251 default:
252 /* Should never get here */
253 return ((unsigned int) -1);
254 }
255 }
256
257 /*
258 * ======== initHw ========
259 */
initHw(PWM_Handle handle,uint32_t period,uint32_t duty)260 static int initHw(PWM_Handle handle, uint32_t period, uint32_t duty)
261 {
262 uintptr_t key;
263 int32_t result;
264 uint32_t timerConfigVal;
265 PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
266 uint32_t timerBaseAddr;
267 uint16_t halfTimer;
268
269 timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
270 halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)];
271
272 key = HwiP_disable();
273
274 MAP_TimerDisable(timerBaseAddr, halfTimer);
275
276 /*
277 * The CC32XX SDK TimerConfigure API halts both timers when it is
278 * used to configure a single half timer. The code below performs
279 * the register operations necessary to configure each half timer
280 * individually.
281 */
282 /* Enable CCP to IO path */
283 HWREG(APPS_CONFIG_BASE + APPS_CONFIG_O_GPT_TRIG_SEL) = 0xFF;
284
285 /* Split the timer and configure it as a PWM */
286 timerConfigVal = ((halfTimer & (TIMER_CFG_A_PWM | TIMER_CFG_B_PWM)) |
287 TIMER_CFG_SPLIT_PAIR);
288 HWREG(timerBaseAddr + TIMER_O_CFG) |= (timerConfigVal >> 24);
289 if (halfTimer & TIMER_A) {
290 HWREG(timerBaseAddr + TIMER_O_TAMR) = timerConfigVal & 255;
291 }
292 else {
293 HWREG(timerBaseAddr + TIMER_O_TBMR) = (timerConfigVal >> 8) & 255;
294 }
295
296 /* Set the peripheral output to active-high */
297 MAP_TimerControlLevel(timerBaseAddr, halfTimer, true);
298
299 HwiP_restore(key);
300
301 result = PWMTimerCC32XX_setPeriod(handle, period);
302 if (result != PWM_STATUS_SUCCESS) {
303 return (result);
304 }
305
306 result = PWMTimerCC32XX_setDuty(handle, duty);
307 if (result != PWM_STATUS_SUCCESS) {
308 return (result);
309 }
310
311 return (PWM_STATUS_SUCCESS);
312 }
313
314 /*
315 * ======== postNotifyFxn ========
316 * Called by Power module when waking up from LPDS.
317 */
postNotifyFxn(unsigned int eventType,uintptr_t eventArg,uintptr_t clientArg)318 static int postNotifyFxn(unsigned int eventType, uintptr_t eventArg,
319 uintptr_t clientArg)
320 {
321 PWM_Handle handle = (PWM_Handle) clientArg;
322 PWMTimerCC32XX_Object *object = handle->object;
323
324 initHw(handle, object->period, object->duty);
325
326 return (Power_NOTIFYDONE);
327 }
328
329 /*
330 * ======== PWMTimerCC32XX_close ========
331 * @pre Function assumes that the handle is not NULL
332 */
PWMTimerCC32XX_close(PWM_Handle handle)333 void PWMTimerCC32XX_close(PWM_Handle handle)
334 {
335 PWMTimerCC32XX_Object *object = handle->object;
336 PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
337 TimerCC32XX_SubTimer subTimer;
338 uint32_t timerBaseAddr;
339 uint32_t gpioBaseAddr;
340 uint32_t padRegister;
341 uintptr_t key;
342
343 timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
344
345 subTimer = (TimerCC32XX_SubTimer) (TimerCC32XX_timer16A +
346 PinConfigTimerHalf(hwAttrs->pwmPin));
347
348 /*
349 * Some PWM pins may not have GPIO capability; in these cases gpioBaseAddr
350 * is set to 0 & the GPIO power dependencies are not released.
351 */
352 gpioBaseAddr = (PinConfigGPIOPort(hwAttrs->pwmPin) >= NUMGPIOPORTS) ?
353 0 : gpioBaseAddresses[PinConfigGPIOPort(hwAttrs->pwmPin)];
354
355 PWMTimerCC32XX_stop(handle);
356
357 key = HwiP_disable();
358
359 TimerCC32XX_freeTimerResource(timerBaseAddr, subTimer);
360
361 /* Remove GPIO power dependency if pin is GPIO capable */
362 if (gpioBaseAddr) {
363 Power_releaseDependency(getPowerMgrId(gpioBaseAddr));
364 }
365
366 Power_unregisterNotify(&object->postNotify);
367
368 padRegister = (PinToPadGet((hwAttrs->pwmPin) & 0x3f)<<2) + PAD_CONFIG_BASE;
369 HWREG(padRegister) = PAD_RESET_STATE;
370
371 object->isOpen = false;
372
373 HwiP_restore(key);
374
375 DebugP_log1("PWM:(%p) is closed", (uintptr_t) handle);
376 }
377
378 /*
379 * ======== PWMTimerCC32XX_control ========
380 * @pre Function assumes that the handle is not NULL
381 */
PWMTimerCC32XX_control(PWM_Handle handle,uint_fast16_t cmd,void * arg)382 int_fast16_t PWMTimerCC32XX_control(PWM_Handle handle, uint_fast16_t cmd,
383 void *arg)
384 {
385 /* No implementation yet */
386 return (PWM_STATUS_UNDEFINEDCMD);
387 }
388
389 /*
390 * ======== PWMTimerCC32XX_init ========
391 * @pre Function assumes that the handle is not NULL
392 */
PWMTimerCC32XX_init(PWM_Handle handle)393 void PWMTimerCC32XX_init(PWM_Handle handle)
394 {
395 }
396
397 /*
398 * ======== PWMTimerCC32XX_open ========
399 * @pre Function assumes that the handle is not NULL
400 */
PWMTimerCC32XX_open(PWM_Handle handle,PWM_Params * params)401 PWM_Handle PWMTimerCC32XX_open(PWM_Handle handle, PWM_Params *params)
402 {
403 uintptr_t key;
404 PWMTimerCC32XX_Object *object = handle->object;
405 PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
406 TimerCC32XX_SubTimer subTimer;
407 uint32_t timerBaseAddr;
408 uint32_t gpioBaseAddr;
409 uint16_t pin;
410
411 timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
412 pin = PinConfigPin(hwAttrs->pwmPin);
413
414 subTimer = (TimerCC32XX_SubTimer) (TimerCC32XX_timer16A +
415 PinConfigTimerHalf(hwAttrs->pwmPin));
416
417 key = HwiP_disable();
418
419 if (object->isOpen) {
420 HwiP_restore(key);
421
422 DebugP_log1("PWM:(%p) already opened.", (uintptr_t) handle);
423
424 return (NULL);
425 }
426
427 if (!TimerCC32XX_allocateTimerResource(timerBaseAddr, subTimer)) {
428 HwiP_restore(key);
429
430 DebugP_log1("Timer: 0x%X unavailable.", timerBaseAddr);
431
432 return (NULL);
433 }
434
435 object->isOpen = true;
436
437 HwiP_restore(key);
438
439 /*
440 * Some PWM pins may not have GPIO capability; in these cases gpioBaseAddr
441 * is set to 0 & the GPIO power dependencies are not set.
442 */
443 gpioBaseAddr = (PinConfigGPIOPort(hwAttrs->pwmPin) >= NUMGPIOPORTS) ?
444 0 : gpioBaseAddresses[PinConfigGPIOPort(hwAttrs->pwmPin)];
445
446 /* Set GPIO power dependency if pin is GPIO capable */
447 if (gpioBaseAddr) {
448 /* Check GPIO power resource Id */
449 if (getPowerMgrId(gpioBaseAddr) == ((unsigned int) -1)) {
450 TimerCC32XX_freeTimerResource(timerBaseAddr, subTimer);
451
452 object->isOpen = false;
453
454 DebugP_log1("PWM:(%p) Failed to determine GPIO power resource ID.",
455 (uintptr_t) handle);
456
457 return (NULL);
458 }
459
460 /* Register power dependency for GPIO port */
461 Power_setDependency(getPowerMgrId(gpioBaseAddr));
462 }
463
464 Power_registerNotify(&object->postNotify, PowerCC32XX_AWAKE_LPDS,
465 postNotifyFxn, (uintptr_t) handle);
466
467 /*
468 * Set PWM duty to initial value (not 0) - required when inverting
469 * output polarity to generate a duty equal to 0 or period. See comments in
470 * PWMTimerCC32XX_setDuty for more information.
471 */
472 object->duty = 0;
473 object->period = 0;
474 object->dutyUnits = params->dutyUnits;
475 object->idleLevel = params->idleLevel;
476 object->periodUnits = params->periodUnits;
477 object->pwmStarted = 0;
478
479 /* Initialize the peripheral & set the period & duty */
480 if (initHw(handle, params->periodValue, params->dutyValue) !=
481 PWM_STATUS_SUCCESS) {
482 PWMTimerCC32XX_close(handle);
483
484 DebugP_log1("PWM:(%p) Failed set initial PWM configuration.",
485 (uintptr_t) handle);
486
487 return (NULL);
488 }
489
490 /* Configure the Power_pinParkState based on idleLevel param */
491 PowerCC32XX_setParkState((PowerCC32XX_Pin) pin,
492 (object->idleLevel == PWM_IDLE_HIGH));
493
494 /* Called to set the initial idleLevel */
495 PWMTimerCC32XX_stop(handle);
496
497 DebugP_log3("PWM:(%p) opened; period set to: %d; duty set to: %d",
498 (uintptr_t) handle, params->periodValue, params->dutyValue);
499
500 return (handle);
501 }
502
503 /*
504 * ======== PWMTimerCC32XX_setDuty ========
505 * @pre Function assumes that handle is not NULL
506 */
PWMTimerCC32XX_setDuty(PWM_Handle handle,uint32_t dutyValue)507 int_fast16_t PWMTimerCC32XX_setDuty(PWM_Handle handle, uint32_t dutyValue)
508 {
509 uintptr_t key;
510 uint32_t duty;
511 uint32_t period;
512 PWMTimerCC32XX_Object *object = handle->object;
513 PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
514 uint32_t timerBaseAddr;
515 uint16_t halfTimer;
516
517 timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
518 halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)];
519
520 key = HwiP_disable();
521
522 period = object->period;
523 duty = getDutyCounts(object->dutyUnits, dutyValue, period);
524
525 if (duty == PWM_INVALID_VALUE) {
526 HwiP_restore(key);
527
528 DebugP_log1("PWM:(%p) duty units could not be determined.",
529 (uintptr_t) handle);
530
531 return (PWM_STATUS_ERROR);
532 }
533
534 if (duty > period) {
535 HwiP_restore(key);
536
537 DebugP_log1("PWM:(%p) duty is out of range.", (uintptr_t) handle);
538
539 return (PWM_STATUS_INVALID_DUTY);
540 }
541
542 /*
543 * The timer peripheral cannot generate a duty equal to the period when
544 * the timer is counting down. In these cases the PWM duty is set to the
545 * period value (output remains low) and output polarity is inverted.
546 * Additionally, if the output is changed from the period the PWM output
547 * polarity must be inverted again.
548 *
549 * The code below uses the previous duty (object->duty) and the new duty to
550 * determine if the polarity should be inverted.
551 * For more details refer to the device specific datasheet and the following
552 * E2E post:
553 * http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/354826.aspx
554 */
555 if (((duty == period) && (object->duty != period)) ||
556 ((duty != period) && (object->duty == period))) {
557 HWREG(timerBaseAddr + TIMER_O_CTL) ^=
558 (halfTimer & (TIMER_CTL_TAPWML | TIMER_CTL_TBPWML));
559 }
560
561 /*
562 * Set & store the new duty. IMPORTANT: this must be saved after output
563 * inversion is determined and before the duty = 0 corner case.
564 */
565 object->duty = duty;
566
567 /*
568 * Special corner case, if duty is 0 we set it to the period without
569 * inverting output
570 */
571 if (duty == 0) {
572 duty = period;
573 }
574
575 MAP_TimerPrescaleMatchSet(timerBaseAddr, halfTimer,
576 duty / PWM_MAX_MATCH_REG_VALUE);
577 MAP_TimerMatchSet(timerBaseAddr, halfTimer,
578 duty % PWM_MAX_MATCH_REG_VALUE);
579
580 HwiP_restore(key);
581
582 DebugP_log2("PWM:(%p) duty set to: %d", (uintptr_t) handle, dutyValue);
583
584 return (PWM_STATUS_SUCCESS);
585 }
586
587 /*
588 * ======== PWMTimerCC32XX_setPeriod ========
589 * @pre Function assumes that handle is not NULL
590 */
PWMTimerCC32XX_setPeriod(PWM_Handle handle,uint32_t periodValue)591 int_fast16_t PWMTimerCC32XX_setPeriod(PWM_Handle handle, uint32_t periodValue)
592 {
593 uintptr_t key;
594 uint32_t duty;
595 uint32_t period;
596 PWMTimerCC32XX_Object *object = handle->object;
597 PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
598 uint32_t timerBaseAddr;
599 uint16_t halfTimer;
600
601 timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
602 halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)];
603
604 key = HwiP_disable();
605
606 duty = object->duty;
607 period = getPeriodCounts(object->periodUnits, periodValue);
608
609 if (period == PWM_INVALID_VALUE) {
610 HwiP_restore(key);
611
612 DebugP_log1("PWM:(%p) period units could not be determined.",
613 (uintptr_t) handle);
614
615 return (PWM_STATUS_ERROR);
616 }
617
618 if ((period == 0) || (period <= duty) || (period > PWM_MAX_PERIOD_COUNT)) {
619 HwiP_restore(key);
620
621 DebugP_log1("PWM:(%p) period is out of range.", (uintptr_t) handle);
622
623 return (PWM_STATUS_INVALID_PERIOD);
624 }
625
626 /* Set the new period */
627 object->period = period;
628 MAP_TimerPrescaleSet(timerBaseAddr, halfTimer,
629 period / PWM_MAX_MATCH_REG_VALUE);
630 MAP_TimerLoadSet(timerBaseAddr, halfTimer,
631 period % PWM_MAX_MATCH_REG_VALUE);
632
633 HwiP_restore(key);
634
635 DebugP_log2("PWM:(%p) period set to: %d", (uintptr_t) handle, periodValue);
636
637 return (PWM_STATUS_SUCCESS);
638 }
639
640 /*
641 * ======== PWMTimerCC32XX_setDutyAndPeriod ========
642 * @pre Function assumes that handle is not NULL
643 */
PWMTimerCC32XX_setDutyAndPeriod(PWM_Handle handle,uint32_t dutyValue,uint32_t periodValue)644 int_fast16_t PWMTimerCC32XX_setDutyAndPeriod(PWM_Handle handle, uint32_t dutyValue, uint32_t periodValue)
645 {
646 uintptr_t key;
647 uint32_t duty;
648 uint32_t period;
649 bool stopped = false;
650 PWMTimerCC32XX_Object *object = handle->object;
651 PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
652 uint32_t oldPeriod;
653 uint32_t timerBaseAddr;
654 uint16_t halfTimer;
655
656 timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
657 halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)];
658
659 key = HwiP_disable();
660
661 oldPeriod = getPeriodCounts(object->periodUnits, object->period);
662 period = getPeriodCounts(object->periodUnits, periodValue);
663 duty = getDutyCounts(object->dutyUnits, dutyValue, period);
664
665 if (period == PWM_INVALID_VALUE) {
666 HwiP_restore(key);
667
668 DebugP_log1("PWM:(%p) period units could not be determined.", (uintptr_t) handle);
669
670 return (PWM_STATUS_ERROR);
671 }
672
673 if ((period == 0) || (period < duty) || (period > PWM_MAX_PERIOD_COUNT)) {
674 HwiP_restore(key);
675
676 DebugP_log1("PWM:(%p) period is out of range.", (uintptr_t) handle);
677
678 return (PWM_STATUS_INVALID_PERIOD);
679 }
680
681 /* Set the new period */
682 object->period = period;
683
684 /*
685 * The timer peripheral cannot generate a duty equal to the period when
686 * the timer is counting down. In these cases the PWM duty is set to the
687 * period value (output remains low) and output polarity is inverted.
688 * Additionally, if the output is changed from the period the PWM output
689 * polarity must be inverted again.
690 *
691 * The code below uses the previous duty (object->duty) and the new duty to
692 * determine if the polarity should be inverted.
693 * For more details refer to the device specific datasheet and the following
694 * E2E post:
695 * http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/354826.aspx
696 */
697 if (((duty == period) && (object->duty != period)) ||
698 ((duty != period) && (object->duty == period))) {
699 HWREG(timerBaseAddr + TIMER_O_CTL) ^=
700 (halfTimer & (TIMER_CTL_TAPWML | TIMER_CTL_TBPWML));
701 }
702
703 /*
704 * Set & store the new duty. IMPORTANT: this must be saved after output
705 * inversion is determined and before the duty = 0 corner case.
706 */
707 object->duty = duty;
708
709 /*
710 * Special corner case, if duty is 0 we set it to the period without
711 * inverting output
712 */
713 if (duty == 0) {
714 duty = period;
715 }
716
717 if (object->pwmStarted && oldPeriod <= PWM_PERIOD_FOR_GLITCH_PROTECTION) {
718 stopped = true;
719 MAP_TimerDisable(timerBaseAddr, halfTimer);
720 }
721
722 MAP_TimerPrescaleSet(timerBaseAddr, halfTimer, period / PWM_MAX_MATCH_REG_VALUE);
723 MAP_TimerPrescaleMatchSet(timerBaseAddr, halfTimer, duty / PWM_MAX_MATCH_REG_VALUE);
724
725 MAP_TimerLoadSet(timerBaseAddr, halfTimer, period % PWM_MAX_MATCH_REG_VALUE);
726 MAP_TimerMatchSet(timerBaseAddr, halfTimer, duty % PWM_MAX_MATCH_REG_VALUE);
727
728 if (stopped) {
729 MAP_TimerEnable(timerBaseAddr, halfTimer);
730 }
731
732 HwiP_restore(key);
733 return (PWM_STATUS_SUCCESS);
734 }
735
736 /*
737 * ======== PWMTimerCC32XX_start ========
738 * @pre Function assumes that handle is not NULL
739 */
PWMTimerCC32XX_start(PWM_Handle handle)740 void PWMTimerCC32XX_start(PWM_Handle handle)
741 {
742 uintptr_t key;
743 PWMTimerCC32XX_Object *object = handle->object;
744 PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
745 uint32_t timerBaseAddr;
746 uint16_t halfTimer;
747 uint16_t pin;
748 uint16_t mode;
749
750 timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
751 halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)];
752 pin = PinConfigPin(hwAttrs->pwmPin);
753 mode = PinConfigPinMode(hwAttrs->pwmPin);
754
755 key = HwiP_disable();
756
757 /*
758 * GP timer ticks only in Active mode. Cannot be used in HIB or LPDS.
759 * Set constraint to disallow LPDS.
760 */
761 if (!(object->pwmStarted)) {
762 Power_setConstraint(PowerCC32XX_DISALLOW_LPDS);
763 object->pwmStarted = true;
764 }
765
766 /* Start the timer & set pinmux to PWM mode */
767 MAP_TimerEnable(timerBaseAddr, halfTimer);
768 MAP_PinTypeTimer((unsigned long)pin, (unsigned long)mode);
769
770 HwiP_restore(key);
771 }
772
773 /*
774 * ======== PWMTimerCC32XX_stop ========
775 * @pre Function assumes that handle is not NULL
776 */
PWMTimerCC32XX_stop(PWM_Handle handle)777 void PWMTimerCC32XX_stop(PWM_Handle handle)
778 {
779 uintptr_t key;
780 uint8_t output;
781 PWMTimerCC32XX_Object *object = handle->object;
782 PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
783 uint32_t timerBaseAddr;
784 uint16_t halfTimer;
785 uint32_t gpioBaseAddr;
786 uint8_t gpioPinIndex;
787 uint16_t pin;
788
789 timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
790 halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)];
791 pin = PinConfigPin(hwAttrs->pwmPin);
792
793 /*
794 * Some PWM pins may not have GPIO capability; in these cases gpioBaseAddr
795 * is set to 0 & the GPIO power dependencies are not set.
796 */
797 gpioBaseAddr = (PinConfigGPIOPort(hwAttrs->pwmPin) >= NUMGPIOPORTS) ?
798 0 : gpioBaseAddresses[PinConfigGPIOPort(hwAttrs->pwmPin)];
799 gpioPinIndex = (PinConfigGPIOPinIndex(hwAttrs->pwmPin) >= NUMGPIOPINS) ?
800 0 : gpioPinIndexes[PinConfigGPIOPinIndex(hwAttrs->pwmPin)];
801
802 key = HwiP_disable();
803
804 /* Remove the dependency to allow LPDS */
805 if (object->pwmStarted) {
806 Power_releaseConstraint(PowerCC32XX_DISALLOW_LPDS);
807 object->pwmStarted = false;
808 }
809
810 /* Set pin as GPIO with IdleLevel value & stop the timer */
811 output = (object->idleLevel) ? gpioPinIndex : 0;
812 MAP_PinTypeGPIO((unsigned long)pin, PIN_MODE_0, false);
813
814 /* Only configure the pin as GPIO if the pin is GPIO capable */
815 if (gpioBaseAddr) {
816 MAP_GPIODirModeSet(gpioBaseAddr, gpioPinIndex, GPIO_DIR_MODE_OUT);
817 MAP_GPIOPinWrite(gpioBaseAddr, gpioPinIndex, output);
818 }
819
820 /* Stop the Timer */
821 MAP_TimerDisable(timerBaseAddr, halfTimer);
822 HwiP_restore(key);
823 }
824