mirror of https://github.com/ARMmbed/mbed-os.git
Implement PWMOUT
parent
2a06ae20cc
commit
eebe00b9d1
|
@ -215,6 +215,70 @@ const SPI_Config SPI_config[CC3220SF_LAUNCHXL_SPICOUNT] = {
|
|||
|
||||
const uint_least8_t SPI_count = CC3220SF_LAUNCHXL_SPICOUNT;
|
||||
|
||||
/*
|
||||
* =============================== PWM ===============================
|
||||
*/
|
||||
#include <ti/drivers/PWM.h>
|
||||
#include <ti/drivers/pwm/PWMTimerCC32XX.h>
|
||||
|
||||
PWMTimerCC32XX_Object pwmTimerCC3220SObjects[CC3220SF_LAUNCHXL_PWMCOUNT];
|
||||
|
||||
const PWMTimerCC32XX_HWAttrsV2 pwmTimerCC3220SHWAttrs[CC3220SF_LAUNCHXL_PWMCOUNT] = {
|
||||
{ /* CC3220SF_LAUNCHXL_PWM6 */
|
||||
.pwmPin = PWMTimerCC32XX_PIN_01
|
||||
},
|
||||
{ /* CC3220SF_LAUNCHXL_PWM7 */
|
||||
.pwmPin = PWMTimerCC32XX_PIN_02
|
||||
},
|
||||
{ /* CC3220SF_LAUNCHXL_PWM0 */
|
||||
.pwmPin = PWMTimerCC32XX_PIN_17
|
||||
},
|
||||
{ /* CC3220SF_LAUNCHXL_PWM3 */
|
||||
.pwmPin = PWMTimerCC32XX_PIN_19
|
||||
},
|
||||
{ /* CC3220SF_LAUNCHXL_PWM2 */
|
||||
.pwmPin = PWMTimerCC32XX_PIN_21
|
||||
},
|
||||
{ /* CC3220SF_LAUNCHXL_PWM5 */
|
||||
.pwmPin = PWMTimerCC32XX_PIN_64
|
||||
},
|
||||
};
|
||||
|
||||
const PWM_Config PWM_config[CC3220SF_LAUNCHXL_PWMCOUNT] = {
|
||||
{
|
||||
.fxnTablePtr = &PWMTimerCC32XX_fxnTable,
|
||||
.object = &pwmTimerCC3220SObjects[CC3220SF_LAUNCHXL_PWM6],
|
||||
.hwAttrs = &pwmTimerCC3220SHWAttrs[CC3220SF_LAUNCHXL_PWM6]
|
||||
},
|
||||
{
|
||||
.fxnTablePtr = &PWMTimerCC32XX_fxnTable,
|
||||
.object = &pwmTimerCC3220SObjects[CC3220SF_LAUNCHXL_PWM7],
|
||||
.hwAttrs = &pwmTimerCC3220SHWAttrs[CC3220SF_LAUNCHXL_PWM7]
|
||||
},
|
||||
{
|
||||
.fxnTablePtr = &PWMTimerCC32XX_fxnTable,
|
||||
.object = &pwmTimerCC3220SObjects[CC3220SF_LAUNCHXL_PWM0],
|
||||
.hwAttrs = &pwmTimerCC3220SHWAttrs[CC3220SF_LAUNCHXL_PWM0]
|
||||
},
|
||||
{
|
||||
.fxnTablePtr = &PWMTimerCC32XX_fxnTable,
|
||||
.object = &pwmTimerCC3220SObjects[CC3220SF_LAUNCHXL_PWM3],
|
||||
.hwAttrs = &pwmTimerCC3220SHWAttrs[CC3220SF_LAUNCHXL_PWM3]
|
||||
},
|
||||
{
|
||||
.fxnTablePtr = &PWMTimerCC32XX_fxnTable,
|
||||
.object = &pwmTimerCC3220SObjects[CC3220SF_LAUNCHXL_PWM2],
|
||||
.hwAttrs = &pwmTimerCC3220SHWAttrs[CC3220SF_LAUNCHXL_PWM2]
|
||||
},
|
||||
{
|
||||
.fxnTablePtr = &PWMTimerCC32XX_fxnTable,
|
||||
.object = &pwmTimerCC3220SObjects[CC3220SF_LAUNCHXL_PWM5],
|
||||
.hwAttrs = &pwmTimerCC3220SHWAttrs[CC3220SF_LAUNCHXL_PWM5]
|
||||
}
|
||||
};
|
||||
|
||||
const uint_least8_t PWM_count = CC3220SF_LAUNCHXL_PWMCOUNT;
|
||||
|
||||
/*
|
||||
* =============================== DMA ===============================
|
||||
*/
|
||||
|
|
|
@ -138,7 +138,10 @@ typedef enum CC3220SF_LAUNCHXL_I2SName {
|
|||
typedef enum CC3220SF_LAUNCHXL_PWMName {
|
||||
CC3220SF_LAUNCHXL_PWM6 = 0,
|
||||
CC3220SF_LAUNCHXL_PWM7,
|
||||
|
||||
CC3220SF_LAUNCHXL_PWM0,
|
||||
CC3220SF_LAUNCHXL_PWM3,
|
||||
CC3220SF_LAUNCHXL_PWM2,
|
||||
CC3220SF_LAUNCHXL_PWM5,
|
||||
CC3220SF_LAUNCHXL_PWMCOUNT
|
||||
} CC3220SF_LAUNCHXL_PWMName;
|
||||
|
||||
|
|
|
@ -588,7 +588,7 @@ void CC3220SFInterface::_socket_background_thread()
|
|||
}
|
||||
_mutex.unlock();
|
||||
}
|
||||
wait_ms(READ_THREAD_SLEEP_MS);
|
||||
ThisThread::sleep_for(READ_THREAD_SLEEP_MS);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -378,7 +378,7 @@ int CC3200_SIMPLELINK::scan(WiFiAccessPoint *res, unsigned count)
|
|||
while(triggeredScanTrials < MAX_SCAN_ATTEMPTS)
|
||||
{
|
||||
/* We wait for one second for the NWP to complete the initiated scan and collect results */
|
||||
wait_ms(1000);
|
||||
ThisThread::sleep_for(1000);
|
||||
|
||||
/* Collect results form one-shot scans.*/
|
||||
ret = sl_WlanGetNetworkList(0, entries_count, netEntries);
|
||||
|
@ -663,7 +663,7 @@ nsapi_error_t CC3200_SIMPLELINK::connect_socket(uint32_t sd, const SocketAddress
|
|||
{
|
||||
if (status == SL_ERROR_BSD_EALREADY && 1 == nonBlocking)
|
||||
{
|
||||
wait_ms(1);
|
||||
ThisThread::sleep_for(1);
|
||||
continue;
|
||||
}
|
||||
else if (status < 0)
|
||||
|
@ -711,7 +711,7 @@ int CC3200_SIMPLELINK::sendto_socket(uint32_t sd, const void * buf, uint32_t buf
|
|||
status = sl_SendTo(sd, buf, bufLen, 0, sa, addrSize);
|
||||
if (status == SL_ERROR_BSD_EAGAIN && 1 == SOCKET_IS_NON_BLOCKING)
|
||||
{
|
||||
wait_ms(1);
|
||||
ThisThread::sleep_for(1);
|
||||
continue;
|
||||
}
|
||||
else if (status < 0)
|
||||
|
@ -733,7 +733,7 @@ int32_t CC3200_SIMPLELINK::send(int sd, const void *data, uint32_t size)
|
|||
status = sl_Send(sd, data, size, 0);
|
||||
if (status == SL_ERROR_BSD_EAGAIN && 1 == SOCKET_IS_NON_BLOCKING)
|
||||
{
|
||||
wait_ms(1);
|
||||
ThisThread::sleep_for(1);
|
||||
continue;
|
||||
}
|
||||
else if (status < 0)
|
||||
|
|
|
@ -40,8 +40,8 @@ struct gpio_irq_s {
|
|||
uint32_t port;
|
||||
PinName pin;
|
||||
uint32_t ch;
|
||||
unsigned long pin_mask;
|
||||
unsigned long irq_offset;
|
||||
unsigned long pin_mask;
|
||||
unsigned long irq_offset;
|
||||
};
|
||||
|
||||
struct port_s {
|
||||
|
@ -52,7 +52,9 @@ struct port_s {
|
|||
};
|
||||
|
||||
struct pwmout_s {
|
||||
uint32_t pwmPin;
|
||||
uint32_t period_us;
|
||||
float duty_percent;
|
||||
void * handle;
|
||||
PWMName pwm;
|
||||
};
|
||||
|
||||
|
|
|
@ -15,148 +15,89 @@
|
|||
*/
|
||||
#include "mbed_assert.h"
|
||||
#include "pwmout_api.h"
|
||||
#include "cmsis.h"
|
||||
#include "pinmap.h"
|
||||
#include "PeripheralPins.h"
|
||||
|
||||
#include <ti/devices/cc32xx/inc/hw_apps_config.h>
|
||||
#include <ti/devices/cc32xx/inc/hw_ocp_shared.h>
|
||||
#include <ti/devices/cc32xx/inc/hw_types.h>
|
||||
#include <ti/devices/cc32xx/inc/hw_timer.h>
|
||||
#include <ti/devices/cc32xx/inc/hw_memmap.h>
|
||||
#include <ti/devices/cc32xx/driverlib/rom.h>
|
||||
#include <ti/devices/cc32xx/driverlib/rom_map.h>
|
||||
#include <ti/devices/cc32xx/driverlib/gpio.h>
|
||||
#include <ti/devices/cc32xx/driverlib/pin.h>
|
||||
#include <ti/devices/cc32xx/driverlib/ti_timer.h>
|
||||
#include <ti/drivers/pwm/PWMTimerCC32XX.h>
|
||||
#include <ti/drivers/PWM.h>
|
||||
#include <CC3220SF_LAUNCHXL.h>
|
||||
|
||||
static const uint32_t timerBaseAddresses[4] = {
|
||||
CC3220SF_TIMERA0_BASE,
|
||||
CC3220SF_TIMERA1_BASE,
|
||||
CC3220SF_TIMERA2_BASE,
|
||||
CC3220SF_TIMERA3_BASE,
|
||||
};
|
||||
|
||||
static const uint32_t timerHalves[2] = {
|
||||
TIMER_A,
|
||||
TIMER_B,
|
||||
};
|
||||
|
||||
/*static const uint32_t gpioBaseAddresses[4] = {
|
||||
CC3220SF_GPIOA0_BASE,
|
||||
CC3220SF_GPIOA1_BASE,
|
||||
CC3220SF_GPIOA2_BASE,
|
||||
CC3220SF_GPIOA3_BASE,
|
||||
};*/
|
||||
|
||||
/*static const uint32_t gpioPinIndexes[8] = {
|
||||
GPIO_PIN_0,
|
||||
GPIO_PIN_1,
|
||||
GPIO_PIN_2,
|
||||
GPIO_PIN_3,
|
||||
GPIO_PIN_4,
|
||||
GPIO_PIN_5,
|
||||
GPIO_PIN_6,
|
||||
GPIO_PIN_7,
|
||||
};*/
|
||||
|
||||
#define PinConfigTimerPort(config) (((config) >> 28) & 0xF)
|
||||
#define PinConfigTimerHalf(config) (((config) >> 24) & 0xF)
|
||||
#define PinConfigGPIOPort(config) (((config) >> 20) & 0xF)
|
||||
#define PinConfigGPIOPinIndex(config) (((config) >> 16) & 0xF)
|
||||
#define PinConfigPinMode(config) (((config) >> 8) & 0xF)
|
||||
#define PinConfigPin(config) (((config) >> 0) & 0x3F)
|
||||
|
||||
#define PWMTimerCC32XX_T0A (0x00 << 24)
|
||||
#define PWMTimerCC32XX_T0B (0x01 << 24)
|
||||
#define PWMTimerCC32XX_T1A (0x10 << 24)
|
||||
#define PWMTimerCC32XX_T1B (0x11 << 24)
|
||||
#define PWMTimerCC32XX_T2A (0x20 << 24)
|
||||
#define PWMTimerCC32XX_T2B (0x21 << 24)
|
||||
#define PWMTimerCC32XX_T3A (0x30 << 24)
|
||||
#define PWMTimerCC32XX_T3B (0x31 << 24)
|
||||
|
||||
#define PWMTimerCC32XX_GPIO9 (0x11 << 16)
|
||||
#define PWMTimerCC32XX_GPIO10 (0x12 << 16)
|
||||
#define PWMTimerCC32XX_GPIO11 (0x13 << 16)
|
||||
#define PWMTimerCC32XX_GPIO24 (0x30 << 16)
|
||||
#define PWMTimerCC32XX_GPIO25 (0x31 << 16)
|
||||
|
||||
#define PWMTimerCC32XX_GPIONONE (0xFF << 16)
|
||||
|
||||
#define PWMTimerCC32XX_PIN_01 (PWMTimerCC32XX_T3A | PWMTimerCC32XX_GPIO10 | 0x0300)
|
||||
#define PWMTimerCC32XX_PIN_02 (PWMTimerCC32XX_T3B | PWMTimerCC32XX_GPIO11 | 0x0301)
|
||||
#define PWMTimerCC32XX_PIN_17 (PWMTimerCC32XX_T0A | PWMTimerCC32XX_GPIO24 | 0x0510)
|
||||
#define PWMTimerCC32XX_PIN_19 (PWMTimerCC32XX_T1B | PWMTimerCC32XX_GPIONONE | 0x0812)
|
||||
#define PWMTimerCC32XX_PIN_21 (PWMTimerCC32XX_T1A | PWMTimerCC32XX_GPIO25 | 0x0914)
|
||||
#define PWMTimerCC32XX_PIN_64 (PWMTimerCC32XX_T2B | PWMTimerCC32XX_GPIO9 | 0x033F)
|
||||
|
||||
//static unsigned int pwm_clock_mhz;
|
||||
extern const PWM_Config PWM_config[];
|
||||
|
||||
void pwmout_init(pwmout_t* obj, PinName pin) {
|
||||
PWM_Params pwmParams;
|
||||
int pwmIndex = CC3220SF_LAUNCHXL_PWMCOUNT;
|
||||
|
||||
PWM_init();
|
||||
|
||||
PWMName pwm = (PWMName)pinmap_peripheral(pin, PinMap_PWM);
|
||||
MBED_ASSERT(pwm != (PWMName)NC);
|
||||
|
||||
obj->pwm = pwm;
|
||||
|
||||
switch(pin) {
|
||||
case PIN_01: obj->pwmPin = PWMTimerCC32XX_PIN_01; break;
|
||||
case PIN_02: obj->pwmPin = PWMTimerCC32XX_PIN_02; break;
|
||||
case PIN_17: obj->pwmPin = PWMTimerCC32XX_PIN_17; break;
|
||||
case PIN_19: obj->pwmPin = PWMTimerCC32XX_PIN_19; break;
|
||||
case PIN_21: obj->pwmPin = PWMTimerCC32XX_PIN_21; break;
|
||||
case PIN_64: obj->pwmPin = PWMTimerCC32XX_PIN_64; break;
|
||||
default: break;
|
||||
case PIN_01:
|
||||
pwmIndex = CC3220SF_LAUNCHXL_PWM6;
|
||||
break;
|
||||
|
||||
case PIN_02:
|
||||
pwmIndex = CC3220SF_LAUNCHXL_PWM7;
|
||||
break;
|
||||
|
||||
case PIN_17:
|
||||
pwmIndex = CC3220SF_LAUNCHXL_PWM0;
|
||||
break;
|
||||
|
||||
case PIN_19:
|
||||
pwmIndex = CC3220SF_LAUNCHXL_PWM3;
|
||||
break;
|
||||
|
||||
case PIN_21:
|
||||
pwmIndex = CC3220SF_LAUNCHXL_PWM2;
|
||||
break;
|
||||
|
||||
case PIN_64:
|
||||
pwmIndex = CC3220SF_LAUNCHXL_PWM5;
|
||||
break;
|
||||
|
||||
default:
|
||||
while(1);
|
||||
}
|
||||
|
||||
uint32_t timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(obj->pwmPin)];
|
||||
uint16_t halfTimer = timerHalves[PinConfigTimerHalf(obj->pwmPin)];
|
||||
obj->handle = (void *)&PWM_config[pwmIndex];
|
||||
|
||||
MAP_TimerDisable(timerBaseAddr, halfTimer);
|
||||
// Initialize the PWM parameters
|
||||
PWM_Params_init(&pwmParams);
|
||||
|
||||
/*
|
||||
* The CC32XX SDK TimerConfigure API halts both timers when it is
|
||||
* used to configure a single half timer. The code below performs
|
||||
* the register operations necessary to configure each half timer
|
||||
* individually.
|
||||
*/
|
||||
/* Enable CCP to IO path */
|
||||
HWREG(APPS_CONFIG_BASE + APPS_CONFIG_O_GPT_TRIG_SEL) = 0xFF;
|
||||
obj->duty_percent = PWM_DEFAULT_DUTY_PERCENT;
|
||||
obj->period_us = PWM_DEFAULT_PERIOD_US;
|
||||
|
||||
/* Split the timer and configure it as a PWM */
|
||||
uint32_t timerConfigVal = ((halfTimer & (TIMER_CFG_A_PWM | TIMER_CFG_B_PWM)) |
|
||||
TIMER_CFG_SPLIT_PAIR);
|
||||
HWREG(timerBaseAddr + TIMER_O_CFG) |= (timerConfigVal >> 24);
|
||||
if (halfTimer & TIMER_A) {
|
||||
HWREG(timerBaseAddr + TIMER_O_TAMR) = timerConfigVal & 255;
|
||||
if (PWM_open(pwmIndex, &pwmParams))
|
||||
{
|
||||
PWM_start((PWM_Handle)obj->handle);
|
||||
}
|
||||
else {
|
||||
HWREG(timerBaseAddr + TIMER_O_TBMR) = (timerConfigVal >> 8) & 255;
|
||||
else
|
||||
{
|
||||
while(1);
|
||||
}
|
||||
|
||||
/* Set the peripheral output to active-high */
|
||||
MAP_TimerControlLevel(timerBaseAddr, halfTimer, true);
|
||||
|
||||
uint16_t mode = PinConfigPinMode(obj->pwmPin);
|
||||
|
||||
/* Start the timer & set pinmux to PWM mode */
|
||||
MAP_TimerEnable(timerBaseAddr, halfTimer);
|
||||
MAP_PinTypeTimer((unsigned long)pin, (unsigned long)mode);
|
||||
}
|
||||
|
||||
void pwmout_free(pwmout_t* obj) {
|
||||
// [TODO]
|
||||
PWM_stop((PWM_Handle)obj->handle);
|
||||
PWM_close((PWM_Handle)obj->handle);
|
||||
}
|
||||
|
||||
void pwmout_write(pwmout_t* obj, float value) {
|
||||
|
||||
PWM_setDuty((PWM_Handle)obj->handle, value*100);
|
||||
obj->duty_percent = value;
|
||||
}
|
||||
|
||||
float pwmout_read(pwmout_t* obj) {
|
||||
return 0;
|
||||
return (obj->duty_percent);
|
||||
}
|
||||
|
||||
void pwmout_period(pwmout_t* obj, float seconds) {
|
||||
pwmout_period_us(obj, seconds * 1000000.0f);
|
||||
pwmout_period_us(obj, seconds * 1000 * 1000);
|
||||
}
|
||||
|
||||
void pwmout_period_ms(pwmout_t* obj, int ms) {
|
||||
|
@ -165,7 +106,8 @@ void pwmout_period_ms(pwmout_t* obj, int ms) {
|
|||
|
||||
// Set the PWM period, keeping the duty cycle the same.
|
||||
void pwmout_period_us(pwmout_t* obj, int us) {
|
||||
|
||||
PWM_setPeriod((PWM_Handle)obj->handle, us);
|
||||
obj->period_us = us;
|
||||
}
|
||||
|
||||
void pwmout_pulsewidth(pwmout_t* obj, float seconds) {
|
||||
|
@ -177,7 +119,11 @@ void pwmout_pulsewidth_ms(pwmout_t* obj, int ms) {
|
|||
}
|
||||
|
||||
void pwmout_pulsewidth_us(pwmout_t* obj, int us) {
|
||||
|
||||
if (obj->period_us)
|
||||
{
|
||||
float value = (float)us / (float)obj->period_us;
|
||||
pwmout_write(obj, value);
|
||||
}
|
||||
}
|
||||
|
||||
const PinMap *pwmout_pinmap()
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (c) 2015-2017, Texas Instruments Incorporated
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/*
|
||||
* ======== PWM.c ========
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <ti/drivers/dpl/HwiP.h>
|
||||
#include <ti/drivers/PWM.h>
|
||||
|
||||
extern const PWM_Config PWM_config[];
|
||||
extern const uint_least8_t PWM_count;
|
||||
|
||||
/* Default PWM parameters structure */
|
||||
const PWM_Params PWM_defaultParams = {
|
||||
.periodUnits = PWM_PERIOD_US, /* Period is defined in Hz */
|
||||
.periodValue = PWM_DEFAULT_PERIOD_US, /* 1US */
|
||||
.dutyUnits = PWM_DUTY_FRACTION, /* Duty is fraction of period */
|
||||
.dutyValue = PWM_DEFAULT_DUTY_PERCENT, /* 0% duty cycle */
|
||||
.idleLevel = PWM_IDLE_LOW, /* Low idle level */
|
||||
.custom = NULL /* No custom params */
|
||||
};
|
||||
|
||||
static bool isInitialized = false;
|
||||
|
||||
/*
|
||||
* ======== PWM_close ========
|
||||
*/
|
||||
void PWM_close(PWM_Handle handle)
|
||||
{
|
||||
handle->fxnTablePtr->closeFxn(handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== PWM_control ========
|
||||
*/
|
||||
int_fast16_t PWM_control(PWM_Handle handle, uint_fast16_t cmd, void *arg)
|
||||
{
|
||||
return handle->fxnTablePtr->controlFxn(handle, cmd, arg);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== PWM_init ========
|
||||
*/
|
||||
void PWM_init(void)
|
||||
{
|
||||
uint_least8_t i;
|
||||
uint_fast32_t key;
|
||||
|
||||
key = HwiP_disable();
|
||||
|
||||
if (!isInitialized) {
|
||||
isInitialized = (bool) true;
|
||||
|
||||
/* Call each driver's init function */
|
||||
for (i = 0; i < PWM_count; i++) {
|
||||
PWM_config[i].fxnTablePtr->initFxn((PWM_Handle) &(PWM_config[i]));
|
||||
}
|
||||
}
|
||||
|
||||
HwiP_restore(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== PWM_open ========
|
||||
*/
|
||||
PWM_Handle PWM_open(uint_least8_t index, PWM_Params *params)
|
||||
{
|
||||
PWM_Handle handle = NULL;
|
||||
|
||||
if (isInitialized && (index < PWM_count)) {
|
||||
/* If params are NULL use defaults */
|
||||
if (params == NULL) {
|
||||
params = (PWM_Params *) &PWM_defaultParams;
|
||||
}
|
||||
|
||||
/* Get handle for this driver instance */
|
||||
handle = (PWM_Handle) &(PWM_config[index]);
|
||||
|
||||
handle = handle->fxnTablePtr->openFxn(handle, params);
|
||||
}
|
||||
|
||||
return (handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== PWM_Params_init ========
|
||||
*/
|
||||
void PWM_Params_init(PWM_Params *params)
|
||||
{
|
||||
*params = PWM_defaultParams;
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== PWM_setDuty ========
|
||||
*/
|
||||
int_fast16_t PWM_setDuty(PWM_Handle handle, uint32_t duty)
|
||||
{
|
||||
return(handle->fxnTablePtr->setDutyFxn(handle, duty));
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== PWM_setDuty ========
|
||||
*/
|
||||
int_fast16_t PWM_setPeriod(PWM_Handle handle, uint32_t period)
|
||||
{
|
||||
return(handle->fxnTablePtr->setPeriodFxn(handle, period));
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== PWM_start ========
|
||||
*/
|
||||
void PWM_start(PWM_Handle handle)
|
||||
{
|
||||
handle->fxnTablePtr->startFxn(handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== PWM_stop ========
|
||||
*/
|
||||
void PWM_stop(PWM_Handle handle)
|
||||
{
|
||||
handle->fxnTablePtr->stopFxn(handle);
|
||||
}
|
|
@ -0,0 +1,594 @@
|
|||
/*
|
||||
* Copyright (c) 2015-2017, Texas Instruments Incorporated
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/** ============================================================================
|
||||
* @file PWM.h
|
||||
* @brief PWM driver interface
|
||||
*
|
||||
* To use the PWM driver, ensure that the correct driver library for your
|
||||
* device is linked in and include this header file as follows:
|
||||
* @code
|
||||
* #include <ti/drivers/PWM.h>
|
||||
* @endcode
|
||||
*
|
||||
* This module serves as the main interface for applications. Its purpose
|
||||
* is to redirect the PWM APIs to specific driver implementations
|
||||
* which are specified using a pointer to a #PWM_FxnTable.
|
||||
*
|
||||
* # Overview #
|
||||
* The PWM driver in TI-RTOS facilitates the generation of Pulse Width
|
||||
* Modulated signals via simple and portable APIs. PWM instances must be
|
||||
* opened by calling PWM_open() while passing in a PWM index and a parameters
|
||||
* data structure.
|
||||
*
|
||||
* The driver APIs serve as an interface to a typical TI-RTOS application.
|
||||
* The specific peripheral implementations are responsible for creating all OS
|
||||
* specific primitives to allow for thread-safe operation.
|
||||
*
|
||||
* When a PWM instance is opened, the period, duty cycle and idle level are
|
||||
* configured and the PWM is stopped (waveforms not generated until PWM_start()
|
||||
* is called). The maximum period and duty supported is device dependent;
|
||||
* refer to the implementation specific documentation for values.
|
||||
*
|
||||
* PWM outputs are active-high, meaning the duty will control the duration of
|
||||
* high output on the pin (at 0% duty, the output is always low, at 100% duty,
|
||||
* the output is always high).
|
||||
*
|
||||
* # Usage #
|
||||
*
|
||||
* @code
|
||||
* PWM_Handle pwm;
|
||||
* PWM_Params pwmParams;
|
||||
*
|
||||
* // Initialize the PWM driver.
|
||||
* PWM_init();
|
||||
*
|
||||
* // Initialize the PWM parameters
|
||||
* PWM_Params_init(&pwmParams);
|
||||
* pwmParams.idleLevel = PWM_IDLE_LOW; // Output low when PWM is not running
|
||||
* pwmParams.periodUnits = PWM_PERIOD_HZ; // Period is in Hz
|
||||
* pwmParams.periodValue = 1e6; // 1MHz
|
||||
* pwmParams.dutyUnits = PWM_DUTY_FRACTION; // Duty is in fractional percentage
|
||||
* pwmParams.dutyValue = 0; // 0% initial duty cycle
|
||||
*
|
||||
* // Open the PWM instance
|
||||
* pwm = PWM_open(Board_PWM0, &pwmParams);
|
||||
*
|
||||
* if (pwm == NULL) {
|
||||
* // PWM_open() failed
|
||||
* while (1);
|
||||
* }
|
||||
*
|
||||
* PWM_start(pwm); // start PWM with 0% duty cycle
|
||||
*
|
||||
* PWM_setDuty(pwm,
|
||||
* (PWM_DUTY_FRACTION_MAX / 2)); // set duty cycle to 50%
|
||||
* @endcode
|
||||
*
|
||||
* Details for the example code above are described in the following
|
||||
* subsections.
|
||||
*
|
||||
* ### PWM Driver Configuration #
|
||||
*
|
||||
* In order to use the PWM APIs, the application is required
|
||||
* to provide device-specific PWM configuration in the Board.c file.
|
||||
* The PWM driver interface defines a configuration data structure:
|
||||
*
|
||||
* @code
|
||||
* typedef struct PWM_Config_ {
|
||||
* PWM_FxnTable const *fxnTablePtr;
|
||||
* void *object;
|
||||
* void const *hwAttrs;
|
||||
* } PWM_Config;
|
||||
* @endcode
|
||||
*
|
||||
* The application must declare an array of PWM_Config elements, named
|
||||
* PWM_config[]. Each element of PWM_config[] is populated with
|
||||
* pointers to a device specific PWM driver implementation's function
|
||||
* table, driver object, and hardware attributes. The hardware attributes
|
||||
* define properties such as which pin will be driven, and which timer peripheral
|
||||
* will be used. Each element in PWM_config[] corresponds to
|
||||
* a PWM instance, and none of the elements should have NULL pointers.
|
||||
*
|
||||
* Additionally, the PWM driver interface defines a global integer variable
|
||||
* 'PWM_count' which is initialized to the number of PWM instances the
|
||||
* application has defined in the PWM_Config array.
|
||||
*
|
||||
* You will need to check the device-specific PWM driver implementation's
|
||||
* header file for example configuration. Please also refer to the
|
||||
* Board.c file of any of your examples to see the PWM configuration.
|
||||
*
|
||||
* ### Initializing the PWM Driver #
|
||||
*
|
||||
* PWM_init() must be called before any other PWM APIs. This function
|
||||
* calls the device implementation's PWM initialization function, for each
|
||||
* element of PWM_config[].
|
||||
*
|
||||
* ### Opening the PWM Driver #
|
||||
*
|
||||
* Opening a PWM requires four steps:
|
||||
* 1. Create and initialize a PWM_Params structure.
|
||||
* 2. Fill in the desired parameters.
|
||||
* 3. Call PWM_open(), passing the index of the PWM in the PWM_config
|
||||
* structure, and the address of the PWM_Params structure. The
|
||||
* PWM instance is specified by the index in the PWM_config structure.
|
||||
* 4. Check that the PWM handle returned by PWM_open() is non-NULL,
|
||||
* and save it. The handle will be used to read and write to the
|
||||
* PWM you just opened.
|
||||
*
|
||||
* Only one PWM index can be used at a time; calling PWM_open() a second
|
||||
* time with the same index previously passed to PWM_open() will result in
|
||||
* an error. You can, though, re-use the index if the instance is closed
|
||||
* via PWM_close().
|
||||
* In the example code, Board_PWM0 is passed to PWM_open(). This macro
|
||||
* is defined in the example's Board.h file.
|
||||
*
|
||||
* ### Modes of Operation #
|
||||
*
|
||||
* A PWM instance can be configured to interpret the period as one of three
|
||||
* units:
|
||||
* - #PWM_PERIOD_US: The period is in microseconds.
|
||||
* - #PWM_PERIOD_HZ: The period is in (reciprocal) Hertz.
|
||||
* - #PWM_PERIOD_COUNTS: The period is in timer counts.
|
||||
*
|
||||
* A PWM instance can be configured to interpret the duty as one of three
|
||||
* units:
|
||||
* - #PWM_DUTY_US: The duty is in microseconds.
|
||||
* - #PWM_DUTY_FRACTION: The duty is in a fractional part of the period
|
||||
* where 0 is 0% and #PWM_DUTY_FRACTION_MAX is 100%.
|
||||
* - #PWM_DUTY_COUNTS: The period is in timer counts and must be less than
|
||||
* the period.
|
||||
*
|
||||
* The idle level parameter is used to set the output to high/low when the
|
||||
* PWM is not running (stopped or not started). The idle level can be
|
||||
* set to:
|
||||
* - #PWM_IDLE_LOW
|
||||
* - #PWM_IDLE_HIGH
|
||||
*
|
||||
* The default PWM configuration is to set a duty of 0% with a 1MHz frequency.
|
||||
* The default period units are in PWM_PERIOD_HZ and the default duty units
|
||||
* are in PWM_DUTY_FRACTION. Finally, the default output idle level is
|
||||
* PWM_IDLE_LOW. It is the application's responsibility to set the duty for
|
||||
* each PWM output used.
|
||||
*
|
||||
* ### Controlling the PWM Duty Cycle #
|
||||
*
|
||||
* Once the PWM instance has been opened and started, the primary API used
|
||||
* by the application will be #PWM_setDuty() to control the duty cycle of a
|
||||
* PWM pin:
|
||||
*
|
||||
* @code
|
||||
* PWM_setDuty(pwm, PWM_DUTY_FRACTION_MAX / 2); // Set 50% duty cycle
|
||||
* @endcode
|
||||
*
|
||||
* # Implementation #
|
||||
*
|
||||
* The PWM driver interface module is joined (at link time) to an
|
||||
* array of PWM_Config data structures named *PWM_config*.
|
||||
* PWM_config is implemented in the application with each entry being a
|
||||
* PWM instance. Each entry in *PWM_config* contains a:
|
||||
* - (PWM_FxnTable *) to a set of functions that implement a PWM peripheral
|
||||
* - (void *) data object that is associated with the PWM_FxnTable
|
||||
* - (void *) hardware attributes that are associated with the PWM_FxnTable
|
||||
*
|
||||
* The PWM APIs are redirected to the device specific implementations
|
||||
* using the PWM_FxnTable pointer of the PWM_config entry.
|
||||
* In order to use device specific functions of the PWM driver directly,
|
||||
* link in the correct driver library for your device and include the
|
||||
* device specific PWM driver header file (which in turn includes PWM.h).
|
||||
* For example, for the MSP432 family of devices, you would include the
|
||||
* following header file:
|
||||
* @code
|
||||
* #include <ti/drivers/pwm/PWMTimerMSP432.h>
|
||||
* @endcode
|
||||
*
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
#ifndef ti_drivers_PWM__include
|
||||
#define ti_drivers_PWM__include
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define PWM_DEFAULT_PERIOD_US 1
|
||||
#define PWM_DEFAULT_DUTY_PERCENT 0
|
||||
|
||||
/*!
|
||||
* @brief Maximum duty (100%) when configuring duty cycle as a fraction of
|
||||
* period.
|
||||
*/
|
||||
#define PWM_DUTY_FRACTION_MAX ((uint32_t) 100)
|
||||
|
||||
/*!
|
||||
* Common PWM_control command code reservation offset.
|
||||
* PWM driver implementations should offset command codes with PWM_CMD_RESERVED
|
||||
* growing positively.
|
||||
*
|
||||
* Example implementation specific command codes:
|
||||
* @code
|
||||
* #define PWMXYZ_COMMAND0 (PWM_CMD_RESERVED + 0)
|
||||
* #define PWMXYZ_COMMAND1 (PWM_CMD_RESERVED + 1)
|
||||
* @endcode
|
||||
*/
|
||||
#define PWM_CMD_RESERVED (32)
|
||||
|
||||
/*!
|
||||
* Common PWM_control status code reservation offset.
|
||||
* PWM driver implementations should offset status codes with
|
||||
* PWM_STATUS_RESERVED growing negatively.
|
||||
*
|
||||
* Example implementation specific status codes:
|
||||
* @code
|
||||
* #define PWMXYZ_STATUS_ERROR0 (PWM_STATUS_RESERVED - 0)
|
||||
* #define PWMXYZ_STATUS_ERROR1 (PWM_STATUS_RESERVED - 1)
|
||||
* #define PWMXYZ_STATUS_ERROR2 (PWM_STATUS_RESERVED - 2)
|
||||
* @endcode
|
||||
*/
|
||||
#define PWM_STATUS_RESERVED (-32)
|
||||
|
||||
/*!
|
||||
* @brief Success status code returned by:
|
||||
* PWM_control(), PWM_setDuty(), PWM_setPeriod().
|
||||
*
|
||||
* Functions return PWM_STATUS_SUCCESS if the call was executed
|
||||
* successfully.
|
||||
*/
|
||||
#define PWM_STATUS_SUCCESS (0)
|
||||
|
||||
/*!
|
||||
* @brief Generic error status code returned by PWM_control().
|
||||
*
|
||||
* PWM_control() returns PWM_STATUS_ERROR if the control code was not executed
|
||||
* successfully.
|
||||
*/
|
||||
#define PWM_STATUS_ERROR (-1)
|
||||
|
||||
/*!
|
||||
* @brief An error status code returned by PWM_control() for undefined
|
||||
* command codes.
|
||||
*
|
||||
* PWM_control() returns PWM_STATUS_UNDEFINEDCMD if the control code is not
|
||||
* recognized by the driver implementation.
|
||||
*/
|
||||
#define PWM_STATUS_UNDEFINEDCMD (-2)
|
||||
|
||||
/*!
|
||||
* @brief An error status code returned by PWM_setPeriod().
|
||||
*
|
||||
* PWM_setPeriod() returns PWM_STATUS_INVALID_PERIOD if the period argument is
|
||||
* invalid for the current configuration.
|
||||
*/
|
||||
#define PWM_STATUS_INVALID_PERIOD (-3)
|
||||
|
||||
/*!
|
||||
* @brief An error status code returned by PWM_setDuty().
|
||||
*
|
||||
* PWM_setDuty() returns PWM_STATUS_INVALID_DUTY if the duty cycle argument is
|
||||
* invalid for the current configuration.
|
||||
*/
|
||||
#define PWM_STATUS_INVALID_DUTY (-4)
|
||||
|
||||
/*!
|
||||
* @brief PWM period unit definitions. Refer to device specific
|
||||
* implementation if using PWM_PERIOD_COUNTS (raw PWM/Timer counts).
|
||||
*/
|
||||
typedef enum PWM_Period_Units_ {
|
||||
PWM_PERIOD_US, /*!< Period in microseconds */
|
||||
PWM_PERIOD_HZ, /*!< Period in (reciprocal) Hertz
|
||||
(for example 2MHz = 0.5us period) */
|
||||
PWM_PERIOD_COUNTS /*!< Period in timer counts */
|
||||
} PWM_Period_Units;
|
||||
|
||||
/*!
|
||||
* @brief PWM duty cycle unit definitions. Refer to device specific
|
||||
* implementation if using PWM_DUTY_COUNTS (raw PWM/Timer counts).
|
||||
*/
|
||||
typedef enum PWM_Duty_Units_ {
|
||||
PWM_DUTY_US, /*!< Duty cycle in microseconds */
|
||||
PWM_DUTY_FRACTION, /*!< Duty as a fractional part of PWM_DUTY_FRACTION_MAX */
|
||||
PWM_DUTY_COUNTS /*!< Duty in timer counts */
|
||||
} PWM_Duty_Units;
|
||||
|
||||
/*!
|
||||
* @brief Idle output level when PWM is not running (stopped / not started).
|
||||
*/
|
||||
typedef enum PWM_IdleLevel_ {
|
||||
PWM_IDLE_LOW = 0,
|
||||
PWM_IDLE_HIGH = 1,
|
||||
} PWM_IdleLevel;
|
||||
|
||||
/*!
|
||||
* @brief PWM Parameters
|
||||
*
|
||||
* PWM Parameters are used to with the PWM_open() call. Default values for
|
||||
* these parameters are set using PWM_Params_init().
|
||||
*
|
||||
* @sa PWM_Params_init()
|
||||
*/
|
||||
typedef struct PWM_Params_ {
|
||||
PWM_Period_Units periodUnits; /*!< Units in which the period is specified */
|
||||
uint32_t periodValue; /*!< PWM initial period */
|
||||
PWM_Duty_Units dutyUnits; /*!< Units in which the duty is specified */
|
||||
uint32_t dutyValue; /*!< PWM initial duty */
|
||||
PWM_IdleLevel idleLevel; /*!< Pin output when PWM is stopped. */
|
||||
void *custom; /*!< Custom argument used by driver
|
||||
implementation */
|
||||
} PWM_Params;
|
||||
|
||||
/*!
|
||||
* @brief A handle that is returned from a PWM_open() call.
|
||||
*/
|
||||
typedef struct PWM_Config_ *PWM_Handle;
|
||||
|
||||
/*!
|
||||
* @brief A function pointer to a driver specific implementation of
|
||||
* PWM_close().
|
||||
*/
|
||||
typedef void (*PWM_CloseFxn) (PWM_Handle handle);
|
||||
|
||||
/*!
|
||||
* @brief A function pointer to a driver specific implementation of
|
||||
* PWM_control().
|
||||
*/
|
||||
typedef int_fast16_t (*PWM_ControlFxn) (PWM_Handle handle, uint_fast16_t cmd,
|
||||
void *arg);
|
||||
/*!
|
||||
* @brief A function pointer to a driver specific implementation of
|
||||
* PWM_init().
|
||||
*/
|
||||
typedef void (*PWM_InitFxn) (PWM_Handle handle);
|
||||
|
||||
/*!
|
||||
* @brief A function pointer to a driver specific implementation of
|
||||
* PWM_open().
|
||||
*/
|
||||
typedef PWM_Handle (*PWM_OpenFxn) (PWM_Handle handle, PWM_Params *params);
|
||||
|
||||
/*!
|
||||
* @brief A function pointer to a driver specific implementation of
|
||||
* PWM_setDuty().
|
||||
*/
|
||||
typedef int_fast16_t (*PWM_SetDutyFxn) (PWM_Handle handle,
|
||||
uint32_t duty);
|
||||
|
||||
/*!
|
||||
* @brief A function pointer to a driver specific implementation of
|
||||
* PWM_setPeriod().
|
||||
*/
|
||||
typedef int_fast16_t (*PWM_SetPeriodFxn) (PWM_Handle handle,
|
||||
uint32_t period);
|
||||
|
||||
/*!
|
||||
* @brief A function pointer to a driver specific implementation of
|
||||
* PWM_start().
|
||||
*/
|
||||
typedef void (*PWM_StartFxn) (PWM_Handle handle);
|
||||
|
||||
/*!
|
||||
* @brief A function pointer to a driver specific implementation of
|
||||
* PWM_stop().
|
||||
*/
|
||||
typedef void (*PWM_StopFxn) (PWM_Handle handle);
|
||||
|
||||
/*!
|
||||
* @brief The definition of a PWM function table that contains the
|
||||
* required set of functions to control a specific PWM driver
|
||||
* implementation.
|
||||
*/
|
||||
typedef struct PWM_FxnTable_ {
|
||||
/*! Function to close the specified instance */
|
||||
PWM_CloseFxn closeFxn;
|
||||
/*! Function to driver implementation specific control function */
|
||||
PWM_ControlFxn controlFxn;
|
||||
/*! Function to initialize the given data object */
|
||||
PWM_InitFxn initFxn;
|
||||
/*! Function to open the specified instance */
|
||||
PWM_OpenFxn openFxn;
|
||||
/*! Function to set the duty cycle for a specific instance */
|
||||
PWM_SetDutyFxn setDutyFxn;
|
||||
/*! Function to set the period for a specific instance */
|
||||
PWM_SetPeriodFxn setPeriodFxn;
|
||||
/*! Function to start the PWM output for a specific instance */
|
||||
PWM_StartFxn startFxn;
|
||||
/*! Function to stop the PWM output for a specific instance */
|
||||
PWM_StopFxn stopFxn;
|
||||
} PWM_FxnTable;
|
||||
|
||||
/*!
|
||||
* @brief PWM Global configuration.
|
||||
*
|
||||
* The PWM_Config structure contains a set of pointers used to characterize
|
||||
* the PWM driver implementation.
|
||||
*
|
||||
*/
|
||||
typedef struct PWM_Config_ {
|
||||
/*! Pointer to a table of driver-specific implementations of PWM APIs */
|
||||
PWM_FxnTable const *fxnTablePtr;
|
||||
/*! Pointer to a driver specific data object */
|
||||
void *object;
|
||||
/*! Pointer to a driver specific hardware attributes structure */
|
||||
void const *hwAttrs;
|
||||
} PWM_Config;
|
||||
|
||||
/*!
|
||||
* @brief Function to close a PWM instance specified by the PWM handle.
|
||||
*
|
||||
* @pre PWM_open() must have been called first.
|
||||
* @pre PWM_stop() must have been called first if PWM was started.
|
||||
*
|
||||
* @param handle A PWM handle returned from PWM_open().
|
||||
*
|
||||
* @sa PWM_open()
|
||||
* @sa PWM_start()
|
||||
* @sa PWM_stop()
|
||||
*/
|
||||
extern void PWM_close(PWM_Handle handle);
|
||||
|
||||
/*!
|
||||
* @brief Function performs implementation specific features on a given
|
||||
* PWM_Handle.
|
||||
*
|
||||
* @pre PWM_open() must have been called first.
|
||||
*
|
||||
* @param handle A PWM handle returned from PWM_open().
|
||||
*
|
||||
* @param cmd A command value defined by the driver specific
|
||||
* implementation.
|
||||
*
|
||||
* @param arg A pointer to an optional R/W (read/write) argument that
|
||||
* is accompanied with cmd.
|
||||
*
|
||||
* @return A PWM_Status describing an error or success state. Negative values
|
||||
* indicate an error occurred.
|
||||
*
|
||||
* @sa PWM_open()
|
||||
*/
|
||||
extern int_fast16_t PWM_control(PWM_Handle handle, uint_fast16_t cmd,
|
||||
void *arg);
|
||||
|
||||
/*!
|
||||
* @brief This function initializes the PWM module.
|
||||
*
|
||||
* @pre The PWM_config structure must exist and be persistent before this
|
||||
* function can be called. This function must be called before any
|
||||
* other PWM driver APIs. This function does not modify any peripheral
|
||||
* registers & should only be called once.
|
||||
*/
|
||||
extern void PWM_init(void);
|
||||
|
||||
/*!
|
||||
* @brief This function opens a given PWM instance and sets the period,
|
||||
* duty and idle level to those specified in the params argument.
|
||||
*
|
||||
* @param index Logical instance number for the PWM indexed into
|
||||
* the PWM_config table.
|
||||
*
|
||||
* @param params Pointer to an parameter structure. If NULL default
|
||||
* values are used.
|
||||
*
|
||||
* @return A PWM_Handle if successful or NULL on an error or if it has been
|
||||
* opened already. If NULL is returned further PWM API calls will
|
||||
* result in undefined behavior.
|
||||
*
|
||||
* @sa PWM_close()
|
||||
*/
|
||||
extern PWM_Handle PWM_open(uint_least8_t index, PWM_Params *params);
|
||||
|
||||
/*!
|
||||
* @brief Function to initialize the PWM_Params structure to default values.
|
||||
*
|
||||
* @param params A pointer to PWM_Params structure for initialization.
|
||||
*
|
||||
* Defaults values are:
|
||||
* Period units: PWM_PERIOD_HZ
|
||||
* Period: 1e6 (1MHz)
|
||||
* Duty cycle units: PWM_DUTY_FRACTION
|
||||
* Duty cycle: 0%
|
||||
* Idle level: PWM_IDLE_LOW
|
||||
*/
|
||||
extern void PWM_Params_init(PWM_Params *params);
|
||||
|
||||
/*!
|
||||
* @brief Function to set the duty cycle of the specified PWM handle. PWM
|
||||
* instances run in active high output mode; 0% is always low output,
|
||||
* 100% is always high output. This API can be called while the PWM
|
||||
* is running & duty must always be lower than or equal to the period.
|
||||
* If an error occurs while calling the function the PWM duty cycle
|
||||
* will remain unchanged.
|
||||
*
|
||||
* @pre PWM_open() must have been called first.
|
||||
*
|
||||
* @param handle A PWM handle returned from PWM_open().
|
||||
*
|
||||
* @param duty Duty cycle in the units specified by the params used
|
||||
* in PWM_open().
|
||||
*
|
||||
* @return A PWM status describing an error or success. Negative values
|
||||
* indicate an error.
|
||||
*
|
||||
* @sa PWM_open()
|
||||
*/
|
||||
extern int_fast16_t PWM_setDuty(PWM_Handle handle, uint32_t duty);
|
||||
|
||||
/*!
|
||||
* @brief Function to set the period of the specified PWM handle. This API
|
||||
* can be called while the PWM is running & the period must always be
|
||||
* larger than the duty cycle.
|
||||
* If an error occurs while calling the function the PWM period
|
||||
* will remain unchanged.
|
||||
*
|
||||
* @pre PWM_open() must have been called first.
|
||||
*
|
||||
* @param handle A PWM handle returned from PWM_open().
|
||||
*
|
||||
* @param period Period in the units specified by the params used
|
||||
* in PWM_open().
|
||||
*
|
||||
* @return A PWM status describing an error or success state. Negative values
|
||||
* indicate an error.
|
||||
*
|
||||
* @sa PWM_open()
|
||||
*/
|
||||
extern int_fast16_t PWM_setPeriod(PWM_Handle handle, uint32_t period);
|
||||
|
||||
/*!
|
||||
* @brief Function to start the specified PWM handle with current settings.
|
||||
*
|
||||
* @pre PWM_open() has to have been called first.
|
||||
*
|
||||
* @param handle A PWM handle returned from PWM_open().
|
||||
*
|
||||
* @sa PWM_open()
|
||||
* @sa PWM_stop()
|
||||
*/
|
||||
extern void PWM_start(PWM_Handle handle);
|
||||
|
||||
/*!
|
||||
* @brief Function to stop the specified PWM handle. Output will set to the
|
||||
* idle level specified by params in PWM_open().
|
||||
*
|
||||
* @pre PWM_open() has to have been called first.
|
||||
*
|
||||
* @param handle A PWM handle returned from PWM_open().
|
||||
*
|
||||
* @sa PWM_open()
|
||||
* @sa PWM_start()
|
||||
*/
|
||||
extern void PWM_stop(PWM_Handle handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* ti_drivers_PWM__include */
|
|
@ -0,0 +1,549 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2018, Texas Instruments Incorporated
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/*!*****************************************************************************
|
||||
* @file Timer.h
|
||||
* @brief Timer driver interface
|
||||
*
|
||||
* The timer header file should be included in an application as follows:
|
||||
* @code
|
||||
* #include <ti/drivers/Timer.h>
|
||||
* @endcode
|
||||
*
|
||||
* # Overview #
|
||||
* The timer driver serves as the main interface for a typical RTOS
|
||||
* application. Its purpose is to redirect the timer APIs to device specific
|
||||
* implementations which are specified using a pointer to a #Timer_FxnTable.
|
||||
* The device specific implementations are responsible for creating all the
|
||||
* RTOS specific primitives to allow for thead-safe operation. This driver
|
||||
* does not have PWM or capture functionalities. These functionalities are
|
||||
* addressed in both the capture and PWM driver.
|
||||
*
|
||||
* The timer driver also handles the general purpose timer resource allocation.
|
||||
* For each driver that requires use of a general purpose timer, it calls
|
||||
* Timer_open() to occupy the specified timer, and calls Timer_close() to
|
||||
* release the occupied timer resource.
|
||||
*
|
||||
* # Usage #
|
||||
* The following example code opens a timer in continuous callback mode. The
|
||||
* period is set to 1000 Hz.
|
||||
*
|
||||
* @code
|
||||
* Timer_Handle handle;
|
||||
* Timer_Params params;
|
||||
*
|
||||
* Timer_Params_init(¶ms);
|
||||
* params.periodUnits = Timer_PERIOD_HZ;
|
||||
* params.period = 1000;
|
||||
* params.timerMode = Timer_CONTINUOUS_CALLBACK;
|
||||
* params.timerCallback = UserCallbackFunction;
|
||||
*
|
||||
* handle = Timer_open(Board_TIMER0, ¶ms);
|
||||
*
|
||||
* if (handle == NULL) {
|
||||
* // Timer_open() failed
|
||||
* while (1);
|
||||
* }
|
||||
*
|
||||
* status = Timer_start(handle);
|
||||
*
|
||||
* if (status == Timer_STATUS_ERROR) {
|
||||
* //Timer_start() failed
|
||||
* while (1);
|
||||
* }
|
||||
*
|
||||
* sleep(10000);
|
||||
*
|
||||
* Timer_stop(handle);
|
||||
* @endcode
|
||||
*
|
||||
* ### Timer Driver Configuration #
|
||||
*
|
||||
* In order to use the timer APIs, the application is required to provide
|
||||
* device specific timer configuration in the Board.c file. The timer driver
|
||||
* interface defines a configuration data structure:
|
||||
*
|
||||
* @code
|
||||
* typedef struct Timer_Config_ {
|
||||
* Timer_FxnTable const *fxnTablePtr;
|
||||
* void *object;
|
||||
* void const *hwAttrs;
|
||||
* } Timer_Config;
|
||||
* @endcode
|
||||
*
|
||||
* The application must declare an array of Timer_Config elements, named
|
||||
* Timer_config[]. Each element of Timer_config[] are populated with
|
||||
* pointers to a device specific timer driver implementation's function
|
||||
* table, driver object, and hardware attributes. The hardware attributes
|
||||
* define properties such as the timer peripheral's base address, interrupt
|
||||
* number and interrupt priority. Each element in Timer_config[] corresponds
|
||||
* to a timer instance, and none of the elements should have NULL pointers.
|
||||
* There is no correlation between the index and the peripheral designation
|
||||
* (such as TIMER0 or TIMER1). For example, it is possible to use
|
||||
* Timer_config[0] for TIMER1.
|
||||
*
|
||||
* You will need to check the device specific timer driver implementation's
|
||||
* header file for example configuration.
|
||||
*
|
||||
* ### Initializing the Timer Driver #
|
||||
*
|
||||
* Timer_init() must be called before any other timer APIs. This function
|
||||
* calls the device implementation's timer initialization function, for each
|
||||
* element of Timer_config[].
|
||||
*
|
||||
* ### Modes of Operation #
|
||||
*
|
||||
* The timer driver supports four modes of operation which may be specified in
|
||||
* the Timer_Params. The device specific implementation may configure the timer
|
||||
* peripheral as an up or down counter. In any case, Timer_getCount() will
|
||||
* return a value characteristic of an up counter.
|
||||
*
|
||||
* #Timer_ONESHOT_CALLBACK is non-blocking. After Timer_start() is called,
|
||||
* the calling thread will continue execution. When the timer interrupt
|
||||
* is triggered, the specified callback function will be called. The timer
|
||||
* will not generate another interrupt unless Timer_start() is called again.
|
||||
* Calling Timer_stop() or Timer_close() after Timer_start() but, before the
|
||||
* timer interrupt, will prevent the specified callback from ever being
|
||||
* invoked.
|
||||
*
|
||||
* #Timer_ONESHOT_BLOCKING is a blocking call. A semaphore is used to block
|
||||
* the calling thread's execution until the timer generates an interrupt. If
|
||||
* Timer_stop() is called, the calling thread will become unblocked
|
||||
* immediately. The behavior of the timer in this mode is similar to a sleep
|
||||
* function.
|
||||
*
|
||||
* #Timer_CONTINUOUS_CALLBACK is non-blocking. After Timer_start() is called,
|
||||
* the calling thread will continue execution. When the timer interrupt is
|
||||
* treiggered, the specified callback function will be called. The timer is
|
||||
* automatically restarted and will continue to periodically generate
|
||||
* interrupts until Timer_stop() is called.
|
||||
*
|
||||
* #Timer_FREE_RUNNING is non-blocking. After Timer_start() is called,
|
||||
* the calling thread will continue execution. The timer will not
|
||||
* generate an interrupt in this mode. The timer hardware will run until
|
||||
* Timer_stop() is called.
|
||||
*
|
||||
* # Implementation #
|
||||
*
|
||||
* The timer driver interface module is joined (at link time) to an
|
||||
* array of Timer_Config data structures named *Timer_config*.
|
||||
* Timer_config is implemented in the application with each entry being an
|
||||
* instance of a timer peripheral. Each entry in *Timer_config* contains a:
|
||||
* - (Timer_FxnTable *) to a set of functions that implement a timer peripheral
|
||||
* - (void *) data object that is associated with the Timer_FxnTable
|
||||
* - (void *) hardware attributes that are associated with the Timer_FxnTable
|
||||
*
|
||||
* The timer APIs are redirected to the device specific implementations
|
||||
* using the Timer_FxnTable pointer of the Timer_config entry.
|
||||
* In order to use device specific functions of the timer driver directly,
|
||||
* link in the correct driver library for your device and include the
|
||||
* device specific timer driver header file (which in turn includes Timer.h).
|
||||
* For example, for the MSP432 family of devices, you would include the
|
||||
* following header file:
|
||||
*
|
||||
* @code
|
||||
* #include <ti/drivers/timer/TimerMSP432.h>
|
||||
* @endcode
|
||||
*
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
#ifndef ti_drivers_Timer__include
|
||||
#define ti_drivers_Timer__include
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*!
|
||||
* Common Timer_control command code reservation offset.
|
||||
* Timer driver implementations should offset command codes with Timer_CMD_RESERVED
|
||||
* growing positively
|
||||
*
|
||||
* Example implementation specific command codes:
|
||||
* @code
|
||||
* #define TimerXYZ_CMD_COMMAND0 Timer_CMD_RESERVED + 0
|
||||
* #define TimerXYZ_CMD_COMMAND1 Timer_CMD_RESERVED + 1
|
||||
* @endcode
|
||||
*/
|
||||
#define Timer_CMD_RESERVED (32)
|
||||
|
||||
/*!
|
||||
* Common Timer_control status code reservation offset.
|
||||
* Timer driver implementations should offset status codes with
|
||||
* Timer_STATUS_RESERVED growing negatively.
|
||||
*
|
||||
* Example implementation specific status codes:
|
||||
* @code
|
||||
* #define TimerXYZ_STATUS_ERROR0 Timer_STATUS_RESERVED - 0
|
||||
* #define TimerXYZ_STATUS_ERROR1 Timer_STATUS_RESERVED - 1
|
||||
* @endcode
|
||||
*/
|
||||
#define Timer_STATUS_RESERVED (-32)
|
||||
|
||||
/*!
|
||||
* @brief Successful status code.
|
||||
*/
|
||||
#define Timer_STATUS_SUCCESS (0)
|
||||
|
||||
/*!
|
||||
* @brief Generic error status code.
|
||||
*/
|
||||
#define Timer_STATUS_ERROR (-1)
|
||||
|
||||
/*!
|
||||
* @brief An error status code returned by Timer_control() for undefined
|
||||
* command codes.
|
||||
*
|
||||
* Timer_control() returns Timer_STATUS_UNDEFINEDCMD if the control code is not
|
||||
* recognized by the driver implementation.
|
||||
*/
|
||||
#define Timer_STATUS_UNDEFINEDCMD (-2)
|
||||
|
||||
/*!
|
||||
* @brief A handle that is returned from a Timer_open() call.
|
||||
*/
|
||||
typedef struct Timer_Config_ *Timer_Handle;
|
||||
|
||||
/*!
|
||||
* @brief Timer mode settings
|
||||
*
|
||||
* This enum defines the timer modes that may be specified in #Timer_Params.
|
||||
*/
|
||||
typedef enum Timer_Mode_ {
|
||||
Timer_ONESHOT_CALLBACK, /*!< User routine doesn't get blocked and
|
||||
user-specified callback function is
|
||||
invoked once the timer interrupt happens
|
||||
for only one time */
|
||||
Timer_ONESHOT_BLOCKING, /*!< User routine gets blocked until timer
|
||||
interrupt happens for only one time. */
|
||||
Timer_CONTINUOUS_CALLBACK, /*!< User routine doesn't get blocked and
|
||||
user-specified callback function is
|
||||
invoked with every timer interrupt. */
|
||||
Timer_FREE_RUNNING
|
||||
} Timer_Mode;
|
||||
|
||||
/*!
|
||||
* @brief Timer period unit enum
|
||||
*
|
||||
* This enum defines the units that may be specified for the period
|
||||
* in #Timer_Params. This unit has no effect with Timer_getCounts.
|
||||
*/
|
||||
typedef enum Timer_PeriodUnits_ {
|
||||
Timer_PERIOD_US, /*!< Period specified in micro seconds. */
|
||||
Timer_PERIOD_HZ, /*!< Period specified in hertz; interrupts per
|
||||
second. */
|
||||
Timer_PERIOD_COUNTS /*!< Period specified in ticks or counts. Varies
|
||||
from board to board. */
|
||||
} Timer_PeriodUnits;
|
||||
|
||||
/*!
|
||||
* @brief Timer callback function
|
||||
*
|
||||
* User definable callback function prototype. The timer driver will call the
|
||||
* defined function and pass in the timer driver's handle and the pointer to the
|
||||
* user-specified the argument.
|
||||
*
|
||||
* @param handle Timer_Handle
|
||||
*/
|
||||
typedef void (*Timer_CallBackFxn)(Timer_Handle handle);
|
||||
|
||||
/*!
|
||||
* @brief Timer Parameters
|
||||
*
|
||||
* Timer parameters are used to with the Timer_open() call. Default values for
|
||||
* these parameters are set using Timer_Params_init().
|
||||
*
|
||||
*/
|
||||
typedef struct Timer_Params_ {
|
||||
/*! Mode to be used by the timer driver. */
|
||||
Timer_Mode timerMode;
|
||||
|
||||
/*! Units used to specify the period. */
|
||||
Timer_PeriodUnits periodUnits;
|
||||
|
||||
/*! Callback function called when timerMode is Timer_ONESHOT_CALLBACK or
|
||||
Timer_CONTINUOUS_CALLBACK. */
|
||||
Timer_CallBackFxn timerCallback;
|
||||
|
||||
/*! Period in units of periodUnits. */
|
||||
uint32_t period;
|
||||
} Timer_Params;
|
||||
|
||||
/*!
|
||||
* @brief A function pointer to a driver specific implementation of
|
||||
* Timer_control().
|
||||
*/
|
||||
typedef int_fast16_t (*Timer_ControlFxn)(Timer_Handle handle,
|
||||
uint_fast16_t cmd, void *arg);
|
||||
|
||||
/*!
|
||||
* @brief A function pointer to a driver specific implementation of
|
||||
* Timer_close().
|
||||
*/
|
||||
typedef void (*Timer_CloseFxn)(Timer_Handle handle);
|
||||
|
||||
/*!
|
||||
* @brief A function pointer to a driver specific implementation of
|
||||
* Timer_getCount().
|
||||
*/
|
||||
typedef uint32_t (*Timer_GetCountFxn)(Timer_Handle handle);
|
||||
|
||||
/*!
|
||||
* @brief A function pointer to a driver specific implementation of
|
||||
* Timer_init().
|
||||
*/
|
||||
typedef void (*Timer_InitFxn)(Timer_Handle handle);
|
||||
|
||||
/*!
|
||||
* @brief A function pointer to a driver specific implementation of
|
||||
* Timer_open().
|
||||
*/
|
||||
typedef Timer_Handle (*Timer_OpenFxn)(Timer_Handle handle,
|
||||
Timer_Params *params);
|
||||
|
||||
/*!
|
||||
* @brief A function pointer to a driver specific implementation of
|
||||
* Timer_start().
|
||||
*/
|
||||
typedef int32_t (*Timer_StartFxn)(Timer_Handle handle);
|
||||
|
||||
/*!
|
||||
* @brief A function pointer to a driver specific implementation of
|
||||
* Timer_stop().
|
||||
*/
|
||||
typedef void (*Timer_StopFxn)(Timer_Handle handle);
|
||||
|
||||
/*!
|
||||
* @brief The definition of a timer function table that contains the
|
||||
* required set of functions to control a specific timer driver
|
||||
* implementation.
|
||||
*/
|
||||
typedef struct Timer_FxnTable_ {
|
||||
/*! Function to close the specified peripheral. */
|
||||
Timer_CloseFxn closeFxn;
|
||||
|
||||
/*! Function to implementation specific control function. */
|
||||
Timer_ControlFxn controlFxn;
|
||||
|
||||
/*! Function to get the count of the specified peripheral. */
|
||||
Timer_GetCountFxn getCountFxn;
|
||||
|
||||
/*! Function to initialize the given data object. */
|
||||
Timer_InitFxn initFxn;
|
||||
|
||||
/*! Function to open the specified peripheral. */
|
||||
Timer_OpenFxn openFxn;
|
||||
|
||||
/*! Function to start the specified peripheral. */
|
||||
Timer_StartFxn startFxn;
|
||||
|
||||
/*! Function to stop the specified peripheral. */
|
||||
Timer_StopFxn stopFxn;
|
||||
} Timer_FxnTable;
|
||||
|
||||
/*!
|
||||
* @brief Timer Global configuration
|
||||
*
|
||||
* The Timer_Config structure contains a set of pointers used to characterize
|
||||
* the timer driver implementation.
|
||||
*
|
||||
* This structure needs to be defined before calling Timer_init() and it must
|
||||
* not be changed thereafter.
|
||||
*
|
||||
* @sa Timer_init()
|
||||
*/
|
||||
typedef struct Timer_Config_ {
|
||||
/*! Pointer to a table of driver-specific implementations of timer APIs. */
|
||||
Timer_FxnTable const *fxnTablePtr;
|
||||
|
||||
/*! Pointer to a driver specific data object. */
|
||||
void *object;
|
||||
|
||||
/*! Pointer to a driver specific hardware attributes structure. */
|
||||
void const *hwAttrs;
|
||||
} Timer_Config;
|
||||
|
||||
/*!
|
||||
* @brief Function to close a timer. The corresponding timer to the
|
||||
* Timer_Handle becomes an available timer resource.
|
||||
*
|
||||
* @pre Timer_open() has been called.
|
||||
*
|
||||
* @param handle A Timer_Handle returned from Timer_open().
|
||||
*
|
||||
* @sa Timer_open()
|
||||
*/
|
||||
extern void Timer_close(Timer_Handle handle);
|
||||
|
||||
/*!
|
||||
* @brief Function performs device specific features on a given
|
||||
* Timer_Handle.
|
||||
*
|
||||
* @pre Timer_open() has been called.
|
||||
*
|
||||
* @param handle A Timer_Handle returned from Timer_open().
|
||||
*
|
||||
* @param cmd A command value defined by the driver specific
|
||||
* implementation.
|
||||
*
|
||||
* @param arg A pointer to an optional R/W (read/write) argument that
|
||||
* is accompanied with cmd.
|
||||
*
|
||||
* @return A Timer_Status describing an error or success state. Negative values
|
||||
* indicate an error occurred.
|
||||
*
|
||||
* @sa Timer_open()
|
||||
*/
|
||||
extern int_fast16_t Timer_control(Timer_Handle handle, uint_fast16_t cmd,
|
||||
void *arg);
|
||||
|
||||
/*!
|
||||
* @brief Function to get the current count of a timer. The value returned
|
||||
* represents timer counts. The value returned is always
|
||||
* characteristic of an up counter. This is true even if the timer
|
||||
* peripheral is counting down. Some device specific implementations
|
||||
* may employ a prescaler in addition to this timer count.
|
||||
*
|
||||
* @pre Timer_open() has been called.
|
||||
*
|
||||
* @param handle A Timer_Handle returned from Timer_open().
|
||||
*
|
||||
* @sa Timer_open()
|
||||
*
|
||||
* @return The current count of the timer in timer ticks.
|
||||
*
|
||||
*/
|
||||
extern uint32_t Timer_getCount(Timer_Handle handle);
|
||||
|
||||
|
||||
/*!
|
||||
* @brief Function to initialize a timer module. This function will go through
|
||||
* all available hardware resources and mark them as "available".
|
||||
*
|
||||
* @pre The Timer_config structure must exist and be persistent before this
|
||||
* function can be called. This function must also be called before
|
||||
* any other timer driver APIs.
|
||||
*
|
||||
* @sa Timer_open()
|
||||
*/
|
||||
extern void Timer_init(void);
|
||||
|
||||
/*!
|
||||
* @brief Function to initialize a given timer peripheral specified by the
|
||||
* index argument. The Timer_Params specifies which mode the timer
|
||||
* will operate. The accuracy of the desired period is limited by the
|
||||
* the clock. For example, a 100 MHz clock will have a tick resolution
|
||||
* of 10 nanoseconds. This function takes care of timer resource
|
||||
* allocation. If the particular timer is available to use, the timer
|
||||
* driver owns it and returns a Timer_Handle.
|
||||
*
|
||||
* @pre Timer_init() has been called.
|
||||
*
|
||||
* @param index Logical peripheral number for the timer indexed into
|
||||
* the Timer_config table.
|
||||
*
|
||||
* @param params Pointer to an parameter block, if NULL it will use
|
||||
* default values.
|
||||
*
|
||||
* @return A Timer_Handle upon success or NULL. If the desired period results
|
||||
* in overflow, or saturation, of the timer, NULL is returned. If the
|
||||
* timer resource is already in use, NULL is returned.
|
||||
*
|
||||
* @sa Timer_init()
|
||||
* @sa Timer_close()
|
||||
*/
|
||||
extern Timer_Handle Timer_open(uint_least8_t index, Timer_Params *params);
|
||||
|
||||
/*!
|
||||
* @brief Function to initialize the Timer_Params struct to its defaults.
|
||||
*
|
||||
* @param params A pointer to Timer_Params structure for
|
||||
* initialization.
|
||||
*
|
||||
* Defaults values are:
|
||||
* timerMode = Timer_ONESHOT_BLOCKING
|
||||
* periodUnit = Timer_PERIOD_COUNTS
|
||||
* timerCallback = NULL
|
||||
* period = (uint16_t) ~0
|
||||
*/
|
||||
extern void Timer_Params_init(Timer_Params *params);
|
||||
|
||||
/*!
|
||||
* @brief Function to start the timer.
|
||||
*
|
||||
* @pre Timer_open() has been called.
|
||||
*
|
||||
* @param handle A Timer_Handle returned from Timer_open().
|
||||
*
|
||||
* @return Timer_STATUS_SUCCESS or Timer_STATUS_ERROR.
|
||||
*
|
||||
* @sa Timer_stop()
|
||||
*/
|
||||
extern int32_t Timer_start(Timer_Handle handle);
|
||||
|
||||
/*!
|
||||
* @brief Function to stop timer. If the timer is already stopped, this
|
||||
* function has no effect.
|
||||
*
|
||||
* @pre Timer_open() has been called.
|
||||
*
|
||||
* @param handle A Timer_Handle returned from Timer_open().
|
||||
*
|
||||
* @sa Timer_start()
|
||||
*/
|
||||
extern void Timer_stop(Timer_Handle handle);
|
||||
|
||||
/* The following are included for backwards compatibility. These should not be
|
||||
* used by the application.
|
||||
*/
|
||||
#define TIMER_CMD_RESERVED Timer_CMD_RESERVED
|
||||
#define TIMER_STATUS_RESERVED Timer_STATUS_RESERVED
|
||||
#define TIMER_STATUS_SUCCESS Timer_STATUS_SUCCESS
|
||||
#define TIMER_STATUS_ERROR Timer_STATUS_ERROR
|
||||
#define TIMER_STATUS_UNDEFINEDCMD Timer_STATUS_UNDEFINEDCMD
|
||||
#define TIMER_ONESHOT_CB Timer_ONESHOT_CALLBACK
|
||||
#define TIMER_ONESHOT_BLOCK Timer_ONESHOT_BLOCKING
|
||||
#define TIMER_CONTINUOUS_CB Timer_CONTINUOUS_CALLBACK
|
||||
#define TIMER_MODE_FREE_RUNNING Timer_FREE_RUNNING
|
||||
#define TIMER_PERIOD_US Timer_PERIOD_US
|
||||
#define TIMER_PERIOD_HZ Timer_PERIOD_HZ
|
||||
#define TIMER_PERIOD_COUNTS Timer_PERIOD_COUNTS
|
||||
#define Timer_Period_Units Timer_PeriodUnits
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ti_drivers_Timer__include */
|
|
@ -0,0 +1,728 @@
|
|||
/*
|
||||
* Copyright (c) 2015-2018, Texas Instruments Incorporated
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* By default disable both asserts and log for this module.
|
||||
* This must be done before DebugP.h is included.
|
||||
*/
|
||||
#ifndef DebugP_ASSERT_ENABLED
|
||||
#define DebugP_ASSERT_ENABLED 0
|
||||
#endif
|
||||
#ifndef DebugP_LOG_ENABLED
|
||||
#define DebugP_LOG_ENABLED 0
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <ti/drivers/dpl/ClockP.h>
|
||||
#include <ti/drivers/dpl/DebugP.h>
|
||||
#include <ti/drivers/dpl/HwiP.h>
|
||||
|
||||
#include <ti/drivers/Power.h>
|
||||
#include <ti/drivers/power/PowerCC32XX.h>
|
||||
#include <ti/drivers/pwm/PWMTimerCC32XX.h>
|
||||
#include <ti/drivers/timer/TimerCC32XX.h>
|
||||
|
||||
#include <ti/devices/cc32xx/inc/hw_apps_config.h>
|
||||
#include <ti/devices/cc32xx/inc/hw_memmap.h>
|
||||
#include <ti/devices/cc32xx/inc/hw_ocp_shared.h>
|
||||
#include <ti/devices/cc32xx/inc/hw_types.h>
|
||||
#include <ti/devices/cc32xx/inc/hw_timer.h>
|
||||
#include <ti/devices/cc32xx/driverlib/rom.h>
|
||||
#include <ti/devices/cc32xx/driverlib/rom_map.h>
|
||||
#include <ti/devices/cc32xx/driverlib/gpio.h>
|
||||
#include <ti/devices/cc32xx/driverlib/pin.h>
|
||||
#include <ti/devices/cc32xx/driverlib/ti_timer.h>
|
||||
|
||||
#define PAD_CONFIG_BASE (OCP_SHARED_BASE + OCP_SHARED_O_GPIO_PAD_CONFIG_0)
|
||||
#define PAD_RESET_STATE 0xC61
|
||||
|
||||
void PWMTimerCC32XX_close(PWM_Handle handle);
|
||||
int_fast16_t PWMTimerCC32XX_control(PWM_Handle handle, uint_fast16_t cmd,
|
||||
void *arg);
|
||||
void PWMTimerCC32XX_init(PWM_Handle handle);
|
||||
PWM_Handle PWMTimerCC32XX_open(PWM_Handle handle, PWM_Params *params);
|
||||
int_fast16_t PWMTimerCC32XX_setDuty(PWM_Handle handle, uint32_t dutyValue);
|
||||
int_fast16_t PWMTimerCC32XX_setPeriod(PWM_Handle handle, uint32_t periodValue);
|
||||
void PWMTimerCC32XX_start(PWM_Handle handle);
|
||||
void PWMTimerCC32XX_stop(PWM_Handle handle);
|
||||
|
||||
/* PWM function table for PWMTimerCC32XX implementation */
|
||||
const PWM_FxnTable PWMTimerCC32XX_fxnTable = {
|
||||
PWMTimerCC32XX_close,
|
||||
PWMTimerCC32XX_control,
|
||||
PWMTimerCC32XX_init,
|
||||
PWMTimerCC32XX_open,
|
||||
PWMTimerCC32XX_setDuty,
|
||||
PWMTimerCC32XX_setPeriod,
|
||||
PWMTimerCC32XX_start,
|
||||
PWMTimerCC32XX_stop
|
||||
};
|
||||
|
||||
/*
|
||||
* Internal value to notify an error has occurred while calculating a duty
|
||||
* or period.
|
||||
*/
|
||||
static const uint32_t PWM_INVALID_VALUE = (~0);
|
||||
|
||||
/*
|
||||
* GPT peripheral load & match registers are 16 bits wide. Max value which
|
||||
* can be set is 65535.
|
||||
*/
|
||||
static const uint16_t PWM_MAX_MATCH_REG_VALUE = (~0);
|
||||
|
||||
/*
|
||||
* GPT peripherals have 24 bit resolution. The max period value which be
|
||||
* set is 16777215.
|
||||
*/
|
||||
static const uint32_t PWM_MAX_PERIOD_COUNT = (0xFFFFFF);
|
||||
|
||||
/*
|
||||
* The following fields are used by CC32XX driverlib APIs and therefore
|
||||
* must be populated by driverlib macro definitions. For CC32XX driverlib
|
||||
* these definitions are found in:
|
||||
* - inc/hw_memmap.h
|
||||
* - driverlib/gpio.h
|
||||
* - driverlib/pin.h
|
||||
* - driverlib/timer.h
|
||||
*/
|
||||
static const uint32_t timerBaseAddresses[4] = {
|
||||
TIMERA0_BASE,
|
||||
TIMERA1_BASE,
|
||||
TIMERA2_BASE,
|
||||
TIMERA3_BASE,
|
||||
};
|
||||
|
||||
static const uint32_t timerHalves[2] = {
|
||||
TIMER_A,
|
||||
TIMER_B,
|
||||
};
|
||||
|
||||
static const uint32_t gpioBaseAddresses[4] = {
|
||||
GPIOA0_BASE,
|
||||
GPIOA1_BASE,
|
||||
GPIOA2_BASE,
|
||||
GPIOA3_BASE,
|
||||
};
|
||||
|
||||
static const uint32_t gpioPinIndexes[8] = {
|
||||
GPIO_PIN_0,
|
||||
GPIO_PIN_1,
|
||||
GPIO_PIN_2,
|
||||
GPIO_PIN_3,
|
||||
GPIO_PIN_4,
|
||||
GPIO_PIN_5,
|
||||
GPIO_PIN_6,
|
||||
GPIO_PIN_7,
|
||||
};
|
||||
|
||||
#define PinConfigTimerPort(config) (((config) >> 28) & 0xF)
|
||||
#define PinConfigTimerHalf(config) (((config) >> 24) & 0xF)
|
||||
#define PinConfigGPIOPort(config) (((config) >> 20) & 0xF)
|
||||
#define PinConfigGPIOPinIndex(config) (((config) >> 16) & 0xF)
|
||||
#define PinConfigPinMode(config) (((config) >> 8) & 0xF)
|
||||
#define PinConfigPin(config) (((config) >> 0) & 0x3F)
|
||||
|
||||
extern uint32_t SystemCoreClock;
|
||||
|
||||
/*
|
||||
* ======== getDutyCounts ========
|
||||
*/
|
||||
static uint32_t getDutyCounts(PWM_Duty_Units dutyUnits, uint32_t dutyValue,
|
||||
uint32_t periodCounts)
|
||||
{
|
||||
uint32_t duty = 0;
|
||||
ClockP_FreqHz freq;
|
||||
|
||||
freq.hi = 0;
|
||||
freq.lo = SystemCoreClock;
|
||||
|
||||
//ClockP_getCpuFreq(&freq);
|
||||
|
||||
switch (dutyUnits) {
|
||||
case PWM_DUTY_COUNTS:
|
||||
duty = dutyValue;
|
||||
break;
|
||||
|
||||
case PWM_DUTY_FRACTION:
|
||||
duty = (((uint64_t) dutyValue) * ((uint64_t) periodCounts)) /
|
||||
PWM_DUTY_FRACTION_MAX;
|
||||
break;
|
||||
|
||||
case PWM_DUTY_US:
|
||||
duty = (dutyValue != 0) ? (dutyValue * (freq.lo/1000000)) - 1 : 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Unsupported duty units return an invalid duty */
|
||||
duty = PWM_INVALID_VALUE;
|
||||
}
|
||||
|
||||
return (duty);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== getPeriodCounts ========
|
||||
*/
|
||||
static uint32_t getPeriodCounts(PWM_Period_Units periodUnits,
|
||||
uint32_t periodValue)
|
||||
{
|
||||
uint32_t period = 0;
|
||||
ClockP_FreqHz freq;
|
||||
|
||||
freq.hi = 0;
|
||||
freq.lo = SystemCoreClock;
|
||||
|
||||
//ClockP_getCpuFreq(&freq);
|
||||
|
||||
switch (periodUnits) {
|
||||
case PWM_PERIOD_COUNTS:
|
||||
period = periodValue;
|
||||
break;
|
||||
|
||||
case PWM_PERIOD_HZ:
|
||||
if (periodValue && periodValue <= freq.lo) {
|
||||
period = (freq.lo / periodValue) - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case PWM_PERIOD_US:
|
||||
period = (periodValue * (freq.lo/1000000)) - 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Unsupported period units return an invalid period */
|
||||
period = PWM_INVALID_VALUE;
|
||||
}
|
||||
|
||||
return (period);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== getPowerMgrId ========
|
||||
*/
|
||||
static uint_fast16_t getPowerMgrId(uint32_t baseAddr)
|
||||
{
|
||||
switch (baseAddr) {
|
||||
case GPIOA0_BASE:
|
||||
return (PowerCC32XX_PERIPH_GPIOA0);
|
||||
case GPIOA1_BASE:
|
||||
return (PowerCC32XX_PERIPH_GPIOA1);
|
||||
case GPIOA2_BASE:
|
||||
return (PowerCC32XX_PERIPH_GPIOA2);
|
||||
case GPIOA3_BASE:
|
||||
return (PowerCC32XX_PERIPH_GPIOA3);
|
||||
case GPIOA4_BASE:
|
||||
return (PowerCC32XX_PERIPH_GPIOA4);
|
||||
default:
|
||||
/* Should never get here */
|
||||
return ((unsigned int) -1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== initHw ========
|
||||
*/
|
||||
static int initHw(PWM_Handle handle, uint32_t period, uint32_t duty)
|
||||
{
|
||||
uintptr_t key;
|
||||
int32_t result;
|
||||
uint32_t timerConfigVal;
|
||||
PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
|
||||
uint32_t timerBaseAddr;
|
||||
uint16_t halfTimer;
|
||||
|
||||
timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
|
||||
halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)];
|
||||
|
||||
key = HwiP_disable();
|
||||
|
||||
MAP_TimerDisable(timerBaseAddr, halfTimer);
|
||||
|
||||
/*
|
||||
* The CC32XX SDK TimerConfigure API halts both timers when it is
|
||||
* used to configure a single half timer. The code below performs
|
||||
* the register operations necessary to configure each half timer
|
||||
* individually.
|
||||
*/
|
||||
/* Enable CCP to IO path */
|
||||
HWREG(APPS_CONFIG_BASE + APPS_CONFIG_O_GPT_TRIG_SEL) = 0xFF;
|
||||
|
||||
/* Split the timer and configure it as a PWM */
|
||||
timerConfigVal = ((halfTimer & (TIMER_CFG_A_PWM | TIMER_CFG_B_PWM)) |
|
||||
TIMER_CFG_SPLIT_PAIR);
|
||||
HWREG(timerBaseAddr + TIMER_O_CFG) |= (timerConfigVal >> 24);
|
||||
if (halfTimer & TIMER_A) {
|
||||
HWREG(timerBaseAddr + TIMER_O_TAMR) = timerConfigVal & 255;
|
||||
}
|
||||
else {
|
||||
HWREG(timerBaseAddr + TIMER_O_TBMR) = (timerConfigVal >> 8) & 255;
|
||||
}
|
||||
|
||||
/* Set the peripheral output to active-high */
|
||||
MAP_TimerControlLevel(timerBaseAddr, halfTimer, true);
|
||||
|
||||
HwiP_restore(key);
|
||||
|
||||
result = PWMTimerCC32XX_setPeriod(handle, period);
|
||||
if (result != PWM_STATUS_SUCCESS) {
|
||||
return (result);
|
||||
}
|
||||
|
||||
result = PWMTimerCC32XX_setDuty(handle, duty);
|
||||
if (result != PWM_STATUS_SUCCESS) {
|
||||
return (result);
|
||||
}
|
||||
|
||||
return (PWM_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== postNotifyFxn ========
|
||||
* Called by Power module when waking up from LPDS.
|
||||
*/
|
||||
static int postNotifyFxn(unsigned int eventType, uintptr_t eventArg,
|
||||
uintptr_t clientArg)
|
||||
{
|
||||
PWM_Handle handle = (PWM_Handle) clientArg;
|
||||
PWMTimerCC32XX_Object *object = handle->object;
|
||||
|
||||
initHw(handle, object->period, object->duty);
|
||||
|
||||
return (Power_NOTIFYDONE);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== PWMTimerCC32XX_close ========
|
||||
* @pre Function assumes that the handle is not NULL
|
||||
*/
|
||||
void PWMTimerCC32XX_close(PWM_Handle handle)
|
||||
{
|
||||
PWMTimerCC32XX_Object *object = handle->object;
|
||||
PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
|
||||
TimerCC32XX_SubTimer subTimer;
|
||||
uint32_t timerBaseAddr;
|
||||
uint32_t gpioBaseAddr;
|
||||
uint32_t padRegister;
|
||||
uintptr_t key;
|
||||
|
||||
timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
|
||||
|
||||
subTimer = (TimerCC32XX_SubTimer) (TimerCC32XX_timer16A +
|
||||
PinConfigTimerHalf(hwAttrs->pwmPin));
|
||||
|
||||
/*
|
||||
* Some PWM pins may not have GPIO capability; in these cases gpioBaseAddr
|
||||
* is set to 0 & the GPIO power dependencies are not released.
|
||||
*/
|
||||
gpioBaseAddr = (PinConfigGPIOPort(hwAttrs->pwmPin) == 0xF) ?
|
||||
0 : gpioBaseAddresses[PinConfigGPIOPort(hwAttrs->pwmPin)];
|
||||
|
||||
PWMTimerCC32XX_stop(handle);
|
||||
|
||||
key = HwiP_disable();
|
||||
|
||||
TimerCC32XX_freeTimerResource(timerBaseAddr, subTimer);
|
||||
|
||||
/* Remove GPIO power dependency if pin is GPIO capable */
|
||||
if (gpioBaseAddr) {
|
||||
Power_releaseDependency(getPowerMgrId(gpioBaseAddr));
|
||||
}
|
||||
|
||||
Power_unregisterNotify(&object->postNotify);
|
||||
|
||||
padRegister = (PinToPadGet((hwAttrs->pwmPin) & 0x3f)<<2) + PAD_CONFIG_BASE;
|
||||
HWREG(padRegister) = PAD_RESET_STATE;
|
||||
|
||||
object->isOpen = false;
|
||||
|
||||
HwiP_restore(key);
|
||||
|
||||
DebugP_log1("PWM:(%p) is closed", (uintptr_t) handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== PWMTimerCC32XX_control ========
|
||||
* @pre Function assumes that the handle is not NULL
|
||||
*/
|
||||
int_fast16_t PWMTimerCC32XX_control(PWM_Handle handle, uint_fast16_t cmd,
|
||||
void *arg)
|
||||
{
|
||||
/* No implementation yet */
|
||||
return (PWM_STATUS_UNDEFINEDCMD);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== PWMTimerCC32XX_init ========
|
||||
* @pre Function assumes that the handle is not NULL
|
||||
*/
|
||||
void PWMTimerCC32XX_init(PWM_Handle handle)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== PWMTimerCC32XX_open ========
|
||||
* @pre Function assumes that the handle is not NULL
|
||||
*/
|
||||
PWM_Handle PWMTimerCC32XX_open(PWM_Handle handle, PWM_Params *params)
|
||||
{
|
||||
uintptr_t key;
|
||||
PWMTimerCC32XX_Object *object = handle->object;
|
||||
PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
|
||||
TimerCC32XX_SubTimer subTimer;
|
||||
uint32_t timerBaseAddr;
|
||||
uint32_t gpioBaseAddr;
|
||||
uint16_t pin;
|
||||
|
||||
timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
|
||||
pin = PinConfigPin(hwAttrs->pwmPin);
|
||||
|
||||
subTimer = (TimerCC32XX_SubTimer) (TimerCC32XX_timer16A +
|
||||
PinConfigTimerHalf(hwAttrs->pwmPin));
|
||||
|
||||
key = HwiP_disable();
|
||||
|
||||
if (object->isOpen) {
|
||||
HwiP_restore(key);
|
||||
|
||||
DebugP_log1("PWM:(%p) already opened.", (uintptr_t) handle);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (!TimerCC32XX_allocateTimerResource(timerBaseAddr, subTimer)) {
|
||||
HwiP_restore(key);
|
||||
|
||||
DebugP_log1("Timer: 0x%X unavailable.", timerBaseAddr);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
object->isOpen = true;
|
||||
|
||||
HwiP_restore(key);
|
||||
|
||||
/*
|
||||
* Some PWM pins may not have GPIO capability; in these cases gpioBaseAddr
|
||||
* is set to 0 & the GPIO power dependencies are not set.
|
||||
*/
|
||||
gpioBaseAddr = (PinConfigGPIOPort(hwAttrs->pwmPin) == 0xF) ?
|
||||
0 : gpioBaseAddresses[PinConfigGPIOPort(hwAttrs->pwmPin)];
|
||||
|
||||
/* Set GPIO power dependency if pin is GPIO capable */
|
||||
if (gpioBaseAddr) {
|
||||
/* Check GPIO power resource Id */
|
||||
if (getPowerMgrId(gpioBaseAddr) == ((unsigned int) -1)) {
|
||||
TimerCC32XX_freeTimerResource(timerBaseAddr, subTimer);
|
||||
|
||||
object->isOpen = false;
|
||||
|
||||
DebugP_log1("PWM:(%p) Failed to determine GPIO power resource ID.",
|
||||
(uintptr_t) handle);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Register power dependency for GPIO port */
|
||||
Power_setDependency(getPowerMgrId(gpioBaseAddr));
|
||||
}
|
||||
|
||||
Power_registerNotify(&object->postNotify, PowerCC32XX_AWAKE_LPDS,
|
||||
postNotifyFxn, (uintptr_t) handle);
|
||||
|
||||
/*
|
||||
* Set PWM duty to initial value (not 0) - required when inverting
|
||||
* output polarity to generate a duty equal to 0 or period. See comments in
|
||||
* PWMTimerCC32XX_setDuty for more information.
|
||||
*/
|
||||
object->duty = 0;
|
||||
object->period = 0;
|
||||
object->dutyUnits = params->dutyUnits;
|
||||
object->idleLevel = params->idleLevel;
|
||||
object->periodUnits = params->periodUnits;
|
||||
object->pwmStarted = 0;
|
||||
|
||||
/* Initialize the peripheral & set the period & duty */
|
||||
if (initHw(handle, params->periodValue, params->dutyValue) !=
|
||||
PWM_STATUS_SUCCESS) {
|
||||
PWMTimerCC32XX_close(handle);
|
||||
|
||||
DebugP_log1("PWM:(%p) Failed set initial PWM configuration.",
|
||||
(uintptr_t) handle);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Configure the Power_pinParkState based on idleLevel param */
|
||||
PowerCC32XX_setParkState((PowerCC32XX_Pin) pin,
|
||||
(object->idleLevel == PWM_IDLE_HIGH));
|
||||
|
||||
/* Called to set the initial idleLevel */
|
||||
PWMTimerCC32XX_stop(handle);
|
||||
|
||||
DebugP_log3("PWM:(%p) opened; period set to: %d; duty set to: %d",
|
||||
(uintptr_t) handle, params->periodValue, params->dutyValue);
|
||||
|
||||
return (handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== PWMTimerCC32XX_setDuty ========
|
||||
* @pre Function assumes that handle is not NULL
|
||||
*/
|
||||
int_fast16_t PWMTimerCC32XX_setDuty(PWM_Handle handle, uint32_t dutyValue)
|
||||
{
|
||||
uintptr_t key;
|
||||
uint32_t duty;
|
||||
uint32_t period;
|
||||
PWMTimerCC32XX_Object *object = handle->object;
|
||||
PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
|
||||
uint32_t timerBaseAddr;
|
||||
uint16_t halfTimer;
|
||||
|
||||
timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
|
||||
halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)];
|
||||
|
||||
key = HwiP_disable();
|
||||
|
||||
period = object->period;
|
||||
duty = getDutyCounts(object->dutyUnits, dutyValue, period);
|
||||
|
||||
if (duty == PWM_INVALID_VALUE) {
|
||||
HwiP_restore(key);
|
||||
|
||||
DebugP_log1("PWM:(%p) duty units could not be determined.",
|
||||
(uintptr_t) handle);
|
||||
|
||||
return (PWM_STATUS_ERROR);
|
||||
}
|
||||
|
||||
if (duty > period) {
|
||||
HwiP_restore(key);
|
||||
|
||||
DebugP_log1("PWM:(%p) duty is out of range.", (uintptr_t) handle);
|
||||
|
||||
return (PWM_STATUS_INVALID_DUTY);
|
||||
}
|
||||
|
||||
/*
|
||||
* The timer peripheral cannot generate a duty equal to the period when
|
||||
* the timer is counting down. In these cases the PWM duty is set to the
|
||||
* period value (output remains low) and output polarity is inverted.
|
||||
* Additionally, if the output is changed from the period the PWM output
|
||||
* polarity must be inverted again.
|
||||
*
|
||||
* The code below uses the previous duty (object->duty) and the new duty to
|
||||
* determine if the polarity should be inverted.
|
||||
* For more details refer to the device specific datasheet and the following
|
||||
* E2E post:
|
||||
* http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/354826.aspx
|
||||
*/
|
||||
if (((duty == period) && (object->duty != period)) ||
|
||||
((duty != period) && (object->duty == period))) {
|
||||
HWREG(timerBaseAddr + TIMER_O_CTL) ^=
|
||||
(halfTimer & (TIMER_CTL_TAPWML | TIMER_CTL_TBPWML));
|
||||
}
|
||||
|
||||
/*
|
||||
* Set & store the new duty. IMPORTANT: this must be saved after output
|
||||
* inversion is determined and before the duty = 0 corner case.
|
||||
*/
|
||||
object->duty = duty;
|
||||
|
||||
/*
|
||||
* Special corner case, if duty is 0 we set it to the period without
|
||||
* inverting output
|
||||
*/
|
||||
if (duty == 0) {
|
||||
duty = period;
|
||||
}
|
||||
|
||||
MAP_TimerPrescaleMatchSet(timerBaseAddr, halfTimer,
|
||||
duty / PWM_MAX_MATCH_REG_VALUE);
|
||||
MAP_TimerMatchSet(timerBaseAddr, halfTimer,
|
||||
duty % PWM_MAX_MATCH_REG_VALUE);
|
||||
|
||||
HwiP_restore(key);
|
||||
|
||||
DebugP_log2("PWM:(%p) duty set to: %d", (uintptr_t) handle, dutyValue);
|
||||
|
||||
return (PWM_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== PWMTimerCC32XX_setPeriod ========
|
||||
* @pre Function assumes that handle is not NULL
|
||||
*/
|
||||
int_fast16_t PWMTimerCC32XX_setPeriod(PWM_Handle handle, uint32_t periodValue)
|
||||
{
|
||||
uintptr_t key;
|
||||
uint32_t duty;
|
||||
uint32_t period;
|
||||
PWMTimerCC32XX_Object *object = handle->object;
|
||||
PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
|
||||
uint32_t timerBaseAddr;
|
||||
uint16_t halfTimer;
|
||||
|
||||
timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
|
||||
halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)];
|
||||
|
||||
key = HwiP_disable();
|
||||
|
||||
duty = object->duty;
|
||||
period = getPeriodCounts(object->periodUnits, periodValue);
|
||||
|
||||
if (period == PWM_INVALID_VALUE) {
|
||||
HwiP_restore(key);
|
||||
|
||||
DebugP_log1("PWM:(%p) period units could not be determined.",
|
||||
(uintptr_t) handle);
|
||||
|
||||
return (PWM_STATUS_ERROR);
|
||||
}
|
||||
|
||||
if ((period == 0) || (period <= duty) || (period > PWM_MAX_PERIOD_COUNT)) {
|
||||
HwiP_restore(key);
|
||||
|
||||
DebugP_log1("PWM:(%p) period is out of range.", (uintptr_t) handle);
|
||||
|
||||
return (PWM_STATUS_INVALID_PERIOD);
|
||||
}
|
||||
|
||||
/* Set the new period */
|
||||
object->period = period;
|
||||
MAP_TimerPrescaleSet(timerBaseAddr, halfTimer,
|
||||
period / PWM_MAX_MATCH_REG_VALUE);
|
||||
MAP_TimerLoadSet(timerBaseAddr, halfTimer,
|
||||
period % PWM_MAX_MATCH_REG_VALUE);
|
||||
|
||||
HwiP_restore(key);
|
||||
|
||||
DebugP_log2("PWM:(%p) period set to: %d", (uintptr_t) handle, periodValue);
|
||||
|
||||
return (PWM_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== PWMTimerCC32XX_start ========
|
||||
* @pre Function assumes that handle is not NULL
|
||||
*/
|
||||
void PWMTimerCC32XX_start(PWM_Handle handle)
|
||||
{
|
||||
uintptr_t key;
|
||||
PWMTimerCC32XX_Object *object = handle->object;
|
||||
PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
|
||||
uint32_t timerBaseAddr;
|
||||
uint16_t halfTimer;
|
||||
uint16_t pin;
|
||||
uint16_t mode;
|
||||
|
||||
timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
|
||||
halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)];
|
||||
pin = PinConfigPin(hwAttrs->pwmPin);
|
||||
mode = PinConfigPinMode(hwAttrs->pwmPin);
|
||||
|
||||
key = HwiP_disable();
|
||||
|
||||
/*
|
||||
* GP timer ticks only in Active mode. Cannot be used in HIB or LPDS.
|
||||
* Set constraint to disallow LPDS.
|
||||
*/
|
||||
if (!(object->pwmStarted)) {
|
||||
Power_setConstraint(PowerCC32XX_DISALLOW_LPDS);
|
||||
object->pwmStarted = true;
|
||||
}
|
||||
|
||||
/* Start the timer & set pinmux to PWM mode */
|
||||
MAP_TimerEnable(timerBaseAddr, halfTimer);
|
||||
|
||||
MAP_PinTypeTimer((unsigned long)pin, (unsigned long)mode);
|
||||
|
||||
HwiP_restore(key);
|
||||
|
||||
DebugP_log1("PWM:(%p) started.", (uintptr_t) handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== PWMTimerCC32XX_stop ========
|
||||
* @pre Function assumes that handle is not NULL
|
||||
*/
|
||||
void PWMTimerCC32XX_stop(PWM_Handle handle)
|
||||
{
|
||||
uintptr_t key;
|
||||
uint8_t output;
|
||||
PWMTimerCC32XX_Object *object = handle->object;
|
||||
PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
|
||||
uint32_t timerBaseAddr;
|
||||
uint16_t halfTimer;
|
||||
uint32_t gpioBaseAddr;
|
||||
uint8_t gpioPinIndex;
|
||||
uint16_t pin;
|
||||
|
||||
timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
|
||||
halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)];
|
||||
pin = PinConfigPin(hwAttrs->pwmPin);
|
||||
|
||||
/*
|
||||
* Some PWM pins may not have GPIO capability; in these cases gpioBaseAddr
|
||||
* is set to 0 & the GPIO power dependencies are not set.
|
||||
*/
|
||||
gpioBaseAddr = (PinConfigGPIOPort(hwAttrs->pwmPin) == 0xF) ?
|
||||
0 : gpioBaseAddresses[PinConfigGPIOPort(hwAttrs->pwmPin)];
|
||||
gpioPinIndex = (PinConfigGPIOPinIndex(hwAttrs->pwmPin) == 0xF) ?
|
||||
0 : gpioPinIndexes[PinConfigGPIOPinIndex(hwAttrs->pwmPin)];
|
||||
|
||||
key = HwiP_disable();
|
||||
|
||||
/* Remove the dependency to allow LPDS */
|
||||
if (object->pwmStarted) {
|
||||
Power_releaseConstraint(PowerCC32XX_DISALLOW_LPDS);
|
||||
object->pwmStarted = false;
|
||||
}
|
||||
|
||||
/* Set pin as GPIO with IdleLevel value & stop the timer */
|
||||
output = (object->idleLevel) ? gpioPinIndex : 0;
|
||||
MAP_PinTypeGPIO((unsigned long)pin, PIN_MODE_0, false);
|
||||
|
||||
/* Only configure the pin as GPIO if the pin is GPIO capable */
|
||||
if (gpioBaseAddr) {
|
||||
MAP_GPIODirModeSet(gpioBaseAddr, gpioPinIndex, GPIO_DIR_MODE_OUT);
|
||||
MAP_GPIOPinWrite(gpioBaseAddr, gpioPinIndex, output);
|
||||
}
|
||||
|
||||
/* Stop the Timer */
|
||||
MAP_TimerDisable(timerBaseAddr, halfTimer);
|
||||
|
||||
HwiP_restore(key);
|
||||
|
||||
DebugP_log1("PWM:(%p) stopped.", (uintptr_t) handle);
|
||||
}
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* Copyright (c) 2015-2018, Texas Instruments Incorporated
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/*! ============================================================================
|
||||
* @file PWMTimerCC32XX.h
|
||||
*
|
||||
* @brief PWM driver implementation using CC32XX General Purpose Timers.
|
||||
*
|
||||
* The PWM header file should be included in an application as follows:
|
||||
* @code
|
||||
* #include <ti/drivers/PWM.h>
|
||||
* #include <ti/drivers/pwm/PWMTimerCC32XX.h>
|
||||
* @endcode
|
||||
*
|
||||
* Refer to @ref PWM.h for a complete description of the PWM
|
||||
* driver APIs provided and examples of their use.
|
||||
*
|
||||
* ## Overview #
|
||||
* This driver configures a CC32XX General Purpose Timer (GPT) in PWM mode.
|
||||
* When in PWM mode, each GPT is divided into 2 PWM outputs. This driver
|
||||
* manages each output as an independent PWM instance. The timer is
|
||||
* automatically configured in count-down mode using the system clock as
|
||||
* the source.
|
||||
*
|
||||
* The timers operate at the system clock frequency (80 MHz). So each timer
|
||||
* tick is 12.5 ns. The period and duty registers are 16 bits wide; thus
|
||||
* 8-bit prescalars are used to extend period and duty registers. The
|
||||
* maximum value supported is 16777215 timer counts ((2^24) - 1) or
|
||||
* 209715 microseconds. Updates to a PWM's period or duty will occur
|
||||
* instantaneously (GPT peripherals do not have shadow registers).
|
||||
*
|
||||
* When stopped, the driver will configure the pin in GPIO mode & set the
|
||||
* output to the PWM_IdleLevel specified in the params used during open. Users
|
||||
* need be aware that while PIN 19 can be used for PWM it is not GPIO capable,
|
||||
* so it cannot be set to the PWM_IdleLevel. Output voltage will be PWM output
|
||||
* at the moment it is stopped.
|
||||
*
|
||||
* Finally, when this driver is opened, it automatically changes the
|
||||
* PWM pin's parking configuration (used when entering low power modes) to
|
||||
* correspond with the PWM_IDLE_LEVEL set in the PWM_params. However, this
|
||||
* setting is not reverted once the driver is closed, it is the users
|
||||
* responsibility to change the parking configuration if necessary.
|
||||
*
|
||||
* ### CC32xx PWM Driver Configuration #
|
||||
*
|
||||
* In order to use the PWM APIs, the application is required
|
||||
* to define 4 configuration items in the application Board.c file:
|
||||
*
|
||||
* 1. An array of PWMTimerCC32XX_Object elements, which will be used by
|
||||
* by the driver to maintain instance state.
|
||||
* Below is an example PWMTimerCC32XX_Object array appropriate for the CC3220SF Launchpad
|
||||
* board:
|
||||
* @code
|
||||
* #include <ti/drivers/PWM.h>
|
||||
* #include <ti/drivers/pwm/PWMTimerCC32XX.h>
|
||||
*
|
||||
* PWMTimerCC32XX_Object pwmTimerCC3220SObjects[CC3220SF_LAUNCHXL_PWMCOUNT];
|
||||
* @endcode
|
||||
*
|
||||
* 2. An array of PWMTimerCC32XX_HWAttrsV2 elements that defines which
|
||||
* pin will be used by the corresponding PWM instance
|
||||
* (see @ref pwmPinIdentifiersCC32XX).
|
||||
* Below is an example PWMTimerCC32XX_HWAttrsV2 array appropriate for the CC3220SF Launchpad
|
||||
* board:
|
||||
* @code
|
||||
* const PWMTimerCC32XX_HWAttrsV2 pwmTimerCC3220SHWAttrs[CC3220SF_LAUNCHXL_PWMCOUNT] = {
|
||||
* {
|
||||
* .pwmPin = PWMTimerCC32XX_PIN_01
|
||||
* },
|
||||
* {
|
||||
* .pwmPin = PWMTimerCC32XX_PIN_02
|
||||
* }
|
||||
* };
|
||||
* @endcode
|
||||
*
|
||||
* 3. An array of @ref PWM_Config elements, one for each PWM instance. Each
|
||||
* element of this array identifies the device-specific API function table,
|
||||
* the device specific PWM object instance, and the device specific Hardware
|
||||
* Attributes to be used for each PWM channel.
|
||||
* Below is an example @ref PWM_Config array appropriate for the CC3220SF Launchpad
|
||||
* board:
|
||||
* @code
|
||||
* const PWM_Config PWM_config[CC3220SF_LAUNCHXL_PWMCOUNT] = {
|
||||
* {
|
||||
* .fxnTablePtr = &PWMTimerCC32XX_fxnTable,
|
||||
* .object = &pwmTimerCC3220SObjects[CC3220SF_LAUNCHXL_PWM6],
|
||||
* .hwAttrs = &pwmTimerCC3220SHWAttrs[CC3220SF_LAUNCHXL_PWM6]
|
||||
* },
|
||||
* {
|
||||
* .fxnTablePtr = &PWMTimerCC32XX_fxnTable,
|
||||
* .object = &pwmTimerCC3220SObjects[CC3220SF_LAUNCHXL_PWM7],
|
||||
* .hwAttrs = &pwmTimerCC3220SHWAttrs[CC3220SF_LAUNCHXL_PWM7]
|
||||
* }
|
||||
* };
|
||||
* @endcode
|
||||
*
|
||||
* 4. A global variable, PWM_count, that informs the driver how many PWM
|
||||
* instances are defined:
|
||||
* @code
|
||||
* const uint_least8_t PWM_count = CC3220SF_LAUNCHXL_PWMCOUNT;
|
||||
* @endcode
|
||||
*
|
||||
* ### Power Management #
|
||||
* The TI-RTOS power management framework will try to put the device into the most
|
||||
* power efficient mode whenever possible. Please see the technical reference
|
||||
* manual for further details on each power mode.
|
||||
*
|
||||
* The PWMTimerCC32XX driver explicitly sets a power constraint when the
|
||||
* PWM is running to prevent LPDS.
|
||||
* The following statements are valid:
|
||||
* - After PWM_open(): Clocks are enabled to the timer resource and the
|
||||
* configured pwmPin. The device is still allowed
|
||||
* to enter LPDS.
|
||||
* - After PWM_start(): LPDS is disabled when PWM is running.
|
||||
* - After PWM_stop(): Conditions are equal as for after PWM_open
|
||||
* - After PWM_close(): The underlying GPTimer is turned off, and the clocks
|
||||
* to the timer and pin are disabled..
|
||||
*
|
||||
* =============================================================================
|
||||
*/
|
||||
|
||||
#ifndef ti_driver_pwm_PWMTimerCC32XX__include
|
||||
#define ti_driver_pwm_PWMTimerCC32XX__include
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <ti/drivers/Power.h>
|
||||
#include <ti/drivers/PWM.h>
|
||||
|
||||
/*! \cond */
|
||||
/*
|
||||
* PWMTimer port/pin defines for pin configuration.
|
||||
*
|
||||
* The timer id (0, 1, 2, or 3) is stored in bits 31 - 28
|
||||
* The timer half (0 = A, 1 = B) is stored in bits 27 - 24
|
||||
* The GPIO port (0, 1, 2, or 3) is stored in bits 23 - 20
|
||||
* The GPIO pin index within the port (0 - 7) is stored in bits 19 - 16
|
||||
* The pin mode is stored in bits 11 - 8
|
||||
* The pin number is stored in bits 7 - 0
|
||||
*
|
||||
*
|
||||
* 31 - 28 27 - 24 23 - 20 19 - 16 11 - 8 7 - 0
|
||||
* -----------------------------------------------------------------------
|
||||
* | Timer id | Timer half | GPIO port | GPIO pin index | pin mode | pin |
|
||||
* -----------------------------------------------------------------------
|
||||
*
|
||||
* The CC32XX has fixed GPIO assignments and pin modes for a given pin.
|
||||
* A PWM pin mode for a given pin has a fixed timer/timer-half.
|
||||
*/
|
||||
#define PWMTimerCC32XX_T0A (0x00 << 24)
|
||||
#define PWMTimerCC32XX_T0B (0x01 << 24)
|
||||
#define PWMTimerCC32XX_T1A (0x10 << 24)
|
||||
#define PWMTimerCC32XX_T1B (0x11 << 24)
|
||||
#define PWMTimerCC32XX_T2A (0x20 << 24)
|
||||
#define PWMTimerCC32XX_T2B (0x21 << 24)
|
||||
#define PWMTimerCC32XX_T3A (0x30 << 24)
|
||||
#define PWMTimerCC32XX_T3B (0x31 << 24)
|
||||
|
||||
#define PWMTimerCC32XX_GPIO9 (0x11 << 16)
|
||||
#define PWMTimerCC32XX_GPIO10 (0x12 << 16)
|
||||
#define PWMTimerCC32XX_GPIO11 (0x13 << 16)
|
||||
#define PWMTimerCC32XX_GPIO24 (0x30 << 16)
|
||||
#define PWMTimerCC32XX_GPIO25 (0x31 << 16)
|
||||
|
||||
#define PWMTimerCC32XX_GPIONONE (0xFF << 16)
|
||||
/*! \endcond */
|
||||
|
||||
/*!
|
||||
* \defgroup pwmPinIdentifiersCC32XX PWMTimerCC32XX_HWAttrs 'pwmPin' field options
|
||||
* @{
|
||||
*/
|
||||
/*!
|
||||
* @name PIN 01, GPIO10, uses Timer3A for PWM.
|
||||
* @{
|
||||
*/
|
||||
#define PWMTimerCC32XX_PIN_01 (PWMTimerCC32XX_T3A | PWMTimerCC32XX_GPIO10 | 0x0300) /*!< @hideinitializer */
|
||||
/*! @} */
|
||||
/*!
|
||||
* @name PIN 02, GPIO11, uses Timer3B for PWM.
|
||||
* @{
|
||||
*/
|
||||
#define PWMTimerCC32XX_PIN_02 (PWMTimerCC32XX_T3B | PWMTimerCC32XX_GPIO11 | 0x0301) /*!< @hideinitializer */
|
||||
/*! @} */
|
||||
/*!
|
||||
* @name PIN 17, GPIO24, uses Timer0A for PWM.
|
||||
* @{
|
||||
*/
|
||||
#define PWMTimerCC32XX_PIN_17 (PWMTimerCC32XX_T0A | PWMTimerCC32XX_GPIO24 | 0x0510) /*!< @hideinitializer */
|
||||
/*! @} */
|
||||
/*!
|
||||
* @name PIN 19, uses Timer1B for PWM.
|
||||
* @{
|
||||
*/
|
||||
#define PWMTimerCC32XX_PIN_19 (PWMTimerCC32XX_T1B | PWMTimerCC32XX_GPIONONE | 0x0812) /*!< @hideinitializer */
|
||||
/*! @} */
|
||||
/*!
|
||||
* @name PIN 21, GPIO25, uses Timer1A for PWM.
|
||||
* @{
|
||||
*/
|
||||
#define PWMTimerCC32XX_PIN_21 (PWMTimerCC32XX_T1A | PWMTimerCC32XX_GPIO25 | 0x0914) /*!< @hideinitializer */
|
||||
/*! @} */
|
||||
/*!
|
||||
* @name PIN 64, GPIO9, uses Timer2B for PWM.
|
||||
* @{
|
||||
*/
|
||||
#define PWMTimerCC32XX_PIN_64 (PWMTimerCC32XX_T2B | PWMTimerCC32XX_GPIO9 | 0x033F) /*!< @hideinitializer */
|
||||
/*! @} */
|
||||
/*! @} */
|
||||
|
||||
/**
|
||||
* @addtogroup PWM_STATUS
|
||||
* PWMTimerCC32XX_STATUS_* macros are command codes only defined in the
|
||||
* PWMTimerCC32XX.h driver implementation and need to:
|
||||
* @code
|
||||
* #include <ti/drivers/pwm/PWMTimerCC32XX.h>
|
||||
* @endcode
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* Add PWMTimerCC32XX_STATUS_* macros here */
|
||||
|
||||
/** @}*/
|
||||
|
||||
/**
|
||||
* @addtogroup PWM_CMD
|
||||
* PWMTimerCC32XX_CMD_* macros are command codes only defined in the
|
||||
* PWMTimerCC32XX.h driver implementation and need to:
|
||||
* @code
|
||||
* #include <ti/drivers/pwm/PWMTimerCC32XX.h>
|
||||
* @endcode
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* Add PWMTimerCC32XX_CMD_* macros here */
|
||||
|
||||
/** @}*/
|
||||
|
||||
/* PWM function table pointer */
|
||||
extern const PWM_FxnTable PWMTimerCC32XX_fxnTable;
|
||||
|
||||
/*!
|
||||
* @brief PWMTimerCC32XX Hardware attributes
|
||||
*
|
||||
* The 'pwmPin' field identifies which physical pin to use for a
|
||||
* particular PWM channel as well as the corresponding Timer resource used
|
||||
* to source the PWM signal. The encoded pin identifier macros for
|
||||
* initializing the 'pwmPin' field must be selected from the
|
||||
* @ref pwmPinIdentifiersCC32XX macros.
|
||||
*
|
||||
* A sample structure is shown below:
|
||||
* @code
|
||||
* const PWMTimerCC32XX_HWAttrsV2 pwmTimerCC32XXHWAttrs[] = {
|
||||
* {
|
||||
* .pwmPin = PWMTimerCC32XX_PIN_01,
|
||||
* },
|
||||
* {
|
||||
* .pwmPin = PWMTimerCC32XX_PIN_02,
|
||||
* }
|
||||
* };
|
||||
* @endcode
|
||||
*/
|
||||
typedef struct PWMTimerCC32XX_HWAttrsV2 {
|
||||
uint32_t pwmPin; /*!< Pin to output PWM signal on
|
||||
(see @ref pwmPinIdentifiersCC32XX) */
|
||||
} PWMTimerCC32XX_HWAttrsV2;
|
||||
|
||||
/*!
|
||||
* @brief PWMTimerCC32XX Object
|
||||
*
|
||||
* The application must not access any member variables of this structure!
|
||||
*/
|
||||
typedef struct PWMTimerCC32XX_Object {
|
||||
Power_NotifyObj postNotify;
|
||||
uint32_t duty; /* Current duty cycle in Duty_Unites */
|
||||
uint32_t period; /* Current period PERIOD_Units */
|
||||
PWM_Duty_Units dutyUnits; /* Current duty cycle unit */
|
||||
PWM_Period_Units periodUnits; /* Current period unit */
|
||||
PWM_IdleLevel idleLevel; /* PWM idle level when stopped / not started */
|
||||
bool pwmStarted; /* Used to gate Power_set/releaseConstraint() calls */
|
||||
bool isOpen; /* open flag used to check if PWM is opened */
|
||||
} PWMTimerCC32XX_Object;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ti_driver_pwm_PWMTimerCC32XX__include */
|
|
@ -0,0 +1,605 @@
|
|||
/*
|
||||
* Copyright (c) 2017-2018, Texas Instruments Incorporated
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <ti/drivers/dpl/HwiP.h>
|
||||
#include <ti/drivers/dpl/SemaphoreP.h>
|
||||
#include <ti/drivers/dpl/ClockP.h>
|
||||
|
||||
#include <ti/drivers/Timer.h>
|
||||
#include <ti/drivers/power/PowerCC32XX.h>
|
||||
#include <ti/drivers/timer/TimerCC32XX.h>
|
||||
|
||||
#include <ti/devices/cc32xx/inc/hw_types.h>
|
||||
#include <ti/devices/cc32xx/inc/hw_memmap.h>
|
||||
#include <ti/devices/cc32xx/inc/hw_timer.h>
|
||||
#include <ti/devices/cc32xx/driverlib/ti_timer.h>
|
||||
|
||||
/*
|
||||
* This macro is used to determine a logical shift value for the
|
||||
* timerState.bitMask. Each timer peripheral occupies two bits in
|
||||
* timerState.bitMask.
|
||||
*
|
||||
* The timer peripherals' base addresses have an offset of 0x1000 starting at
|
||||
* 0x40030000. That byte is masked using 0xF000 which can result in a value
|
||||
* ranging from 0x0000 to 0x3000 for this particular hardware instance. This
|
||||
* value is then shifted right by 12 into the LSB. Lastly, the value is
|
||||
* multiplied by two because there are two bits in the timerState.bitMask for
|
||||
* each timer. The value returned is used for the logical shift.
|
||||
*/
|
||||
#define timerMaskShift(baseAddress) ((((baseAddress) & 0XF000) >> 12) * 2)
|
||||
|
||||
void TimerCC32XX_close(Timer_Handle handle);
|
||||
int_fast16_t TimerCC32XX_control(Timer_Handle handle,
|
||||
uint_fast16_t cmd, void *arg);
|
||||
uint32_t TimerCC32XX_getCount(Timer_Handle handle);
|
||||
void TimerCC32XX_init(Timer_Handle handle);
|
||||
Timer_Handle TimerCC32XX_open(Timer_Handle handle, Timer_Params *params);
|
||||
int32_t TimerCC32XX_start(Timer_Handle handle);
|
||||
void TimerCC32XX_stop(Timer_Handle handle);
|
||||
|
||||
/* Internal static Functions */
|
||||
static void initHw(Timer_Handle handle);
|
||||
static void getPrescaler(Timer_Handle handle);
|
||||
static uint32_t getPowerMgrId(uint32_t baseAddress);
|
||||
static int postNotifyFxn(unsigned int eventType, uintptr_t eventArg,
|
||||
uintptr_t clientArg);
|
||||
static void TimerCC32XX_hwiIntFunction(uintptr_t arg);
|
||||
|
||||
/* Function table for TimerCC32XX implementation */
|
||||
const Timer_FxnTable TimerCC32XX_fxnTable = {
|
||||
.closeFxn = TimerCC32XX_close,
|
||||
.openFxn = TimerCC32XX_open,
|
||||
.startFxn = TimerCC32XX_start,
|
||||
.stopFxn = TimerCC32XX_stop,
|
||||
.initFxn = TimerCC32XX_init,
|
||||
.getCountFxn = TimerCC32XX_getCount,
|
||||
.controlFxn = TimerCC32XX_control
|
||||
};
|
||||
|
||||
/*
|
||||
* Internal Timer status structure
|
||||
*
|
||||
* bitMask: Each timer peripheral occupies two bits in the bitMask. The least
|
||||
* significant bit represents the first half width timer, TimerCC32XX_timer16A
|
||||
* and the most significant bit represents the second half width timer,
|
||||
* TimerCC32XX_timer16B. If the full width timer, TimerCC32XX_timer32, is used,
|
||||
* both bits are set to 1.
|
||||
|
||||
* 31 - 8 7 - 6 5 - 4 3 - 2 1 - 0
|
||||
* ------------------------------------------------
|
||||
* | Reserved | Timer3 | Timer2 | Timer1 | Timer0 |
|
||||
* ------------------------------------------------
|
||||
*/
|
||||
static struct {
|
||||
uint32_t bitMask;
|
||||
} timerState;
|
||||
|
||||
/*
|
||||
* ======== initHw ========
|
||||
*/
|
||||
static void initHw(Timer_Handle handle)
|
||||
{
|
||||
TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
|
||||
TimerCC32XX_Object const *object = handle->object;
|
||||
|
||||
/* Ensure the timer is disabled */
|
||||
TimerDisable(hwAttrs->baseAddress, object->timer);
|
||||
|
||||
if (object->timer == TIMER_A) {
|
||||
|
||||
HWREG(hwAttrs->baseAddress + TIMER_O_TAMR) = TIMER_TAMR_TAMR_PERIOD;
|
||||
}
|
||||
else {
|
||||
|
||||
HWREG(hwAttrs->baseAddress + TIMER_O_TBMR) = TIMER_TBMR_TBMR_PERIOD;
|
||||
}
|
||||
|
||||
if (hwAttrs->subTimer == TimerCC32XX_timer32) {
|
||||
|
||||
HWREG(hwAttrs->baseAddress + TIMER_O_CFG) = TIMER_CFG_32_BIT_TIMER;
|
||||
}
|
||||
else {
|
||||
|
||||
HWREG(hwAttrs->baseAddress + TIMER_O_CFG) = TIMER_CFG_16_BIT;
|
||||
}
|
||||
|
||||
/* Disable all interrupts */
|
||||
HWREG(hwAttrs->baseAddress + TIMER_O_IMR) = ~object->timer;
|
||||
|
||||
/* Writing the PSR Register has no effect for full width 32-bit mode */
|
||||
TimerPrescaleSet(hwAttrs->baseAddress, object->timer, object->prescaler);
|
||||
TimerLoadSet(hwAttrs->baseAddress, object->timer, object->period);
|
||||
|
||||
/* This function controls the stall response for the timer. When true,
|
||||
* the timer stops counting if the processor enters debug mode. The
|
||||
* default setting for the hardware is false.
|
||||
*/
|
||||
TimerControlStall(hwAttrs->baseAddress, object->timer, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* ========= getPrescaler =========
|
||||
* This function calculates the prescaler and timer interval load register
|
||||
* for a half timer. The handle is assumed to contain a object->period which
|
||||
* represents the number of clock cycles in the desired period. The calling
|
||||
* function, TimerCC32XX_open() checks for overflow before calling this function.
|
||||
* Therefore, this function is guaranteed to never fail.
|
||||
*/
|
||||
static void getPrescaler(Timer_Handle handle)
|
||||
{
|
||||
TimerCC32XX_Object *object = handle->object;
|
||||
uint32_t bestDiff = ~0, bestPsr = 0, bestIload = 0;
|
||||
uint32_t diff, intervalLoad, prescaler;
|
||||
|
||||
/* Loop over the 8-bit prescaler */
|
||||
for (prescaler = 1; prescaler < 256; prescaler++) {
|
||||
|
||||
/* Calculate timer interval load */
|
||||
intervalLoad = object->period / (prescaler + 1);
|
||||
|
||||
/* Will this fit in 16-bits? */
|
||||
if (intervalLoad > (uint16_t) ~0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* How close is the intervalLoad to what we actually want? */
|
||||
diff = object->period - intervalLoad * (prescaler + 1);
|
||||
|
||||
/* If it is closer to what we want */
|
||||
if (diff <= bestDiff) {
|
||||
|
||||
/* If its a perfect match */
|
||||
if (diff == 0) {
|
||||
object->period = intervalLoad;
|
||||
object->prescaler = prescaler;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Snapshot in case we don't find something better */
|
||||
bestDiff = diff;
|
||||
bestPsr = prescaler;
|
||||
bestIload = intervalLoad;
|
||||
}
|
||||
}
|
||||
|
||||
/* Never found a perfect match, settle for the best */
|
||||
object->period = bestIload;
|
||||
object->prescaler = bestPsr;
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== getPowerMgrId ========
|
||||
*/
|
||||
static uint32_t getPowerMgrId(uint32_t baseAddress)
|
||||
{
|
||||
switch (baseAddress) {
|
||||
|
||||
case TIMERA0_BASE:
|
||||
|
||||
return (PowerCC32XX_PERIPH_TIMERA0);
|
||||
|
||||
case TIMERA1_BASE:
|
||||
|
||||
return (PowerCC32XX_PERIPH_TIMERA1);
|
||||
|
||||
case TIMERA2_BASE:
|
||||
|
||||
return (PowerCC32XX_PERIPH_TIMERA2);
|
||||
|
||||
case TIMERA3_BASE:
|
||||
|
||||
return (PowerCC32XX_PERIPH_TIMERA3);
|
||||
|
||||
default:
|
||||
|
||||
return ((uint32_t) -1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== postNotifyFxn ========
|
||||
* This functions is called when a transition from LPDS mode is made.
|
||||
* clientArg should be a handle of a previously opened Timer instance.
|
||||
*/
|
||||
static int postNotifyFxn(unsigned int eventType, uintptr_t eventArg,
|
||||
uintptr_t clientArg)
|
||||
{
|
||||
initHw((Timer_Handle) clientArg);
|
||||
|
||||
return (Power_NOTIFYDONE);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== TimerCC32XX_allocateTimerResource ========
|
||||
*/
|
||||
bool TimerCC32XX_allocateTimerResource(uint32_t baseAddress,
|
||||
TimerCC32XX_SubTimer subTimer)
|
||||
{
|
||||
uintptr_t key;
|
||||
uint32_t mask;
|
||||
uint32_t powerMgrId;
|
||||
bool status;
|
||||
|
||||
powerMgrId = getPowerMgrId(baseAddress);
|
||||
|
||||
if (powerMgrId == (uint32_t) -1) {
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
mask = subTimer << timerMaskShift(baseAddress);
|
||||
|
||||
key = HwiP_disable();
|
||||
|
||||
if (timerState.bitMask & mask) {
|
||||
|
||||
status = false;
|
||||
}
|
||||
else {
|
||||
|
||||
Power_setDependency(powerMgrId);
|
||||
timerState.bitMask = timerState.bitMask | mask;
|
||||
status = true;
|
||||
}
|
||||
|
||||
HwiP_restore(key);
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== TimerCC32XX_close ========
|
||||
*/
|
||||
void TimerCC32XX_close(Timer_Handle handle)
|
||||
{
|
||||
TimerCC32XX_Object *object = handle->object;
|
||||
TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
|
||||
|
||||
/* Stopping the Timer before closing it */
|
||||
TimerCC32XX_stop(handle);
|
||||
|
||||
Power_unregisterNotify(&(object->notifyObj));
|
||||
|
||||
if (object->hwiHandle) {
|
||||
|
||||
HwiP_clearInterrupt(hwAttrs->intNum);
|
||||
HwiP_delete(object->hwiHandle);
|
||||
object->hwiHandle = NULL;
|
||||
}
|
||||
|
||||
if (object->timerSem) {
|
||||
|
||||
SemaphoreP_delete(object->timerSem);
|
||||
object->timerSem = NULL;
|
||||
}
|
||||
|
||||
TimerCC32XX_freeTimerResource(hwAttrs->baseAddress, hwAttrs->subTimer);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== TimerCC32XX_control ========
|
||||
*/
|
||||
int_fast16_t TimerCC32XX_control(Timer_Handle handle,
|
||||
uint_fast16_t cmd, void *arg)
|
||||
{
|
||||
return (Timer_STATUS_UNDEFINEDCMD);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== TimerCC32XX_freeTimerResource ========
|
||||
*/
|
||||
void TimerCC32XX_freeTimerResource(uint32_t baseAddress,
|
||||
TimerCC32XX_SubTimer subTimer)
|
||||
{
|
||||
uintptr_t key;
|
||||
uint32_t mask;
|
||||
|
||||
mask = subTimer << timerMaskShift(baseAddress);
|
||||
|
||||
key = HwiP_disable();
|
||||
|
||||
timerState.bitMask = (timerState.bitMask & ~mask);
|
||||
|
||||
Power_releaseDependency(getPowerMgrId(baseAddress));
|
||||
|
||||
HwiP_restore(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== TimerCC32XX_getCount ========
|
||||
*/
|
||||
uint32_t TimerCC32XX_getCount(Timer_Handle handle)
|
||||
{
|
||||
TimerCC32XX_HWAttrs const *hWAttrs = handle->hwAttrs;
|
||||
TimerCC32XX_Object const *object = handle->object;
|
||||
uint32_t count;
|
||||
|
||||
if (object->timer == TIMER_A) {
|
||||
count = HWREG(hWAttrs->baseAddress + TIMER_O_TAR);
|
||||
}
|
||||
else {
|
||||
count = HWREG(hWAttrs->baseAddress + TIMER_O_TBR);
|
||||
}
|
||||
|
||||
/* Virtual up counter */
|
||||
count = object->period - count;
|
||||
|
||||
return (count);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== TimerCC32XX_hwiIntFunction ========
|
||||
*/
|
||||
void TimerCC32XX_hwiIntFunction(uintptr_t arg)
|
||||
{
|
||||
Timer_Handle handle = (Timer_Handle) arg;
|
||||
TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
|
||||
TimerCC32XX_Object const *object = handle->object;
|
||||
uint32_t interruptMask;
|
||||
|
||||
/* Only clear the interrupt for this->object->timer */
|
||||
interruptMask = object->timer & (TIMER_TIMA_TIMEOUT | TIMER_TIMB_TIMEOUT);
|
||||
TimerIntClear(hwAttrs->baseAddress, interruptMask);
|
||||
|
||||
/* Hwi is not created when using Timer_FREE_RUNNING */
|
||||
if (object->mode != Timer_CONTINUOUS_CALLBACK) {
|
||||
TimerCC32XX_stop(handle);
|
||||
}
|
||||
|
||||
if (object-> mode != Timer_ONESHOT_BLOCKING) {
|
||||
object->callBack(handle);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== TimerCC32XX_init ========
|
||||
*/
|
||||
void TimerCC32XX_init(Timer_Handle handle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== TimerCC32XX_open ========
|
||||
*/
|
||||
Timer_Handle TimerCC32XX_open(Timer_Handle handle, Timer_Params *params)
|
||||
{
|
||||
TimerCC32XX_Object *object = handle->object;
|
||||
TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
|
||||
SemaphoreP_Params semParams;
|
||||
HwiP_Params hwiParams;
|
||||
ClockP_FreqHz clockFreq;
|
||||
|
||||
/* Check for valid parameters */
|
||||
if (((params->timerMode == Timer_ONESHOT_CALLBACK ||
|
||||
params->timerMode == Timer_CONTINUOUS_CALLBACK) &&
|
||||
params->timerCallback == NULL) ||
|
||||
params->period == 0) {
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (!TimerCC32XX_allocateTimerResource(hwAttrs->baseAddress,
|
||||
hwAttrs->subTimer)) {
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
Power_registerNotify(&(object->notifyObj), PowerCC32XX_AWAKE_LPDS,
|
||||
postNotifyFxn, (uintptr_t) handle);
|
||||
|
||||
object->mode = params->timerMode;
|
||||
object->isRunning = false;
|
||||
object->callBack = params->timerCallback;
|
||||
object->period = params->period;
|
||||
object->prescaler = 0;
|
||||
|
||||
if (hwAttrs->subTimer == TimerCC32XX_timer16B) {
|
||||
|
||||
object->timer = TIMER_B;
|
||||
}
|
||||
else {
|
||||
|
||||
object->timer = TIMER_A;
|
||||
}
|
||||
|
||||
if (object->mode != Timer_FREE_RUNNING) {
|
||||
|
||||
HwiP_Params_init(&hwiParams);
|
||||
hwiParams.arg = (uintptr_t) handle;
|
||||
hwiParams.priority = hwAttrs->intPriority;
|
||||
object->hwiHandle = HwiP_create(hwAttrs->intNum,
|
||||
TimerCC32XX_hwiIntFunction, &hwiParams);
|
||||
|
||||
if (object->hwiHandle == NULL) {
|
||||
|
||||
TimerCC32XX_close(handle);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Creating the semaphore if mode is blocking */
|
||||
if (params->timerMode == Timer_ONESHOT_BLOCKING) {
|
||||
|
||||
SemaphoreP_Params_init(&semParams);
|
||||
semParams.mode = SemaphoreP_Mode_BINARY;
|
||||
object->timerSem = SemaphoreP_create(0, &semParams);
|
||||
|
||||
if (object->timerSem == NULL) {
|
||||
|
||||
TimerCC32XX_close(handle);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Formality; CC32XX System Clock fixed to 80.0 MHz */
|
||||
ClockP_getCpuFreq(&clockFreq);
|
||||
|
||||
if (params->periodUnits == Timer_PERIOD_US) {
|
||||
|
||||
/* Checks if the calculated period will fit in 32-bits */
|
||||
if (object->period >= ((uint32_t) ~0) / (clockFreq.lo / 1000000)) {
|
||||
|
||||
TimerCC32XX_close(handle);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
object->period = object->period * (clockFreq.lo / 1000000);
|
||||
}
|
||||
else if (params->periodUnits == Timer_PERIOD_HZ) {
|
||||
|
||||
/* If (object->period) > clockFreq */
|
||||
if ((object->period = clockFreq.lo / object->period) == 0) {
|
||||
|
||||
TimerCC32XX_close(handle);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* If using a half timer */
|
||||
if (hwAttrs->subTimer != TimerCC32XX_timer32) {
|
||||
|
||||
if (object->period > 0xFFFF) {
|
||||
|
||||
/* 24-bit resolution for the half timer */
|
||||
if (object->period >= (1 << 24)) {
|
||||
|
||||
TimerCC32XX_close(handle);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
getPrescaler(handle);
|
||||
}
|
||||
}
|
||||
|
||||
initHw(handle);
|
||||
|
||||
return (handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== TimerCC32XX_start ========
|
||||
*/
|
||||
int32_t TimerCC32XX_start(Timer_Handle handle)
|
||||
{
|
||||
TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
|
||||
TimerCC32XX_Object *object = handle->object;
|
||||
uint32_t interruptMask;
|
||||
uintptr_t key;
|
||||
|
||||
interruptMask = object->timer & (TIMER_TIMB_TIMEOUT | TIMER_TIMA_TIMEOUT);
|
||||
|
||||
key = HwiP_disable();
|
||||
|
||||
if (object->isRunning) {
|
||||
|
||||
HwiP_restore(key);
|
||||
|
||||
return (Timer_STATUS_ERROR);
|
||||
}
|
||||
|
||||
object->isRunning = true;
|
||||
|
||||
if (object->hwiHandle) {
|
||||
|
||||
TimerIntEnable(hwAttrs->baseAddress, interruptMask);
|
||||
}
|
||||
|
||||
Power_setConstraint(PowerCC32XX_DISALLOW_LPDS);
|
||||
|
||||
/* Reload the timer */
|
||||
if (object->timer == TIMER_A) {
|
||||
HWREG(hwAttrs->baseAddress + TIMER_O_TAMR) |= TIMER_TAMR_TAILD;
|
||||
}
|
||||
else {
|
||||
HWREG(hwAttrs->baseAddress + TIMER_O_TBMR) |= TIMER_TBMR_TBILD;
|
||||
}
|
||||
|
||||
TimerEnable(hwAttrs->baseAddress, object->timer);
|
||||
|
||||
HwiP_restore(key);
|
||||
|
||||
if (object->mode == Timer_ONESHOT_BLOCKING) {
|
||||
|
||||
/* Pend forever, ~0 */
|
||||
SemaphoreP_pend(object->timerSem, ~0);
|
||||
}
|
||||
|
||||
return (Timer_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* ======== TimerCC32XX_stop ========
|
||||
*/
|
||||
void TimerCC32XX_stop(Timer_Handle handle)
|
||||
{
|
||||
TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
|
||||
TimerCC32XX_Object *object = handle->object;
|
||||
uint32_t interruptMask;
|
||||
uintptr_t key;
|
||||
bool flag = false;
|
||||
|
||||
interruptMask = object->timer & (TIMER_TIMB_TIMEOUT | TIMER_TIMA_TIMEOUT);
|
||||
|
||||
key = HwiP_disable();
|
||||
|
||||
if (object->isRunning) {
|
||||
|
||||
object->isRunning = false;
|
||||
|
||||
/* Post the Semaphore when called from the Hwi */
|
||||
if (object->mode == Timer_ONESHOT_BLOCKING) {
|
||||
flag = true;
|
||||
}
|
||||
|
||||
TimerDisable(hwAttrs->baseAddress, object->timer);
|
||||
TimerIntDisable(hwAttrs->baseAddress, interruptMask);
|
||||
Power_releaseConstraint(PowerCC32XX_DISALLOW_LPDS);
|
||||
}
|
||||
|
||||
HwiP_restore(key);
|
||||
|
||||
if (flag) {
|
||||
SemaphoreP_post(object->timerSem);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* Copyright (c) 2017-2018, Texas Instruments Incorporated
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/*!*****************************************************************************
|
||||
* @file TimerCC32XX.h
|
||||
* @brief Timer driver interface for CC32XX devices
|
||||
*
|
||||
* # Operation #
|
||||
* This driver implements half and full width general purpose timers for the
|
||||
* CC32XX device. For CC32XX devices, the system clock is 80 MHz and a 16-bit
|
||||
* timer has an 8-bit prescaler. The desired period may not always be
|
||||
* achieved due to hardware limitations, such as the aforementioned. The timer
|
||||
* resolution is limited to 12.5ns due to the 80 MHz clock. A timer period no
|
||||
* greater than 209,714us can be achieved when operating in 16-bit mode.
|
||||
* Similarly, a period no greater than 53,687,090us can be achieved when
|
||||
* operating in 32-bit mode. The same time constraints apply to the 16-bit
|
||||
* timer when attempting to use a frequency less than 5 Hertz. For additional
|
||||
* details, refer to the device's technical reference manual.
|
||||
*
|
||||
* The timer always operates in count down mode. When using a half width timer,
|
||||
* an 8-bit prescaler will be implemented by the driver if necessary. If the
|
||||
* timer is operating in Timer_FREE_RUNNING, the timer will count down from the
|
||||
* specified period to 0 before restarting.
|
||||
*
|
||||
* When using a half width timer, Timer_getCount() will return the
|
||||
* value of the counter in bits 15:0 and bits 23:16 will contain the
|
||||
* current free-running value of the prescaler. Bits 31:24 are always 0.
|
||||
* When using a full width timer, Timer_getCount() will return the
|
||||
* the value of the 32-bit timer.
|
||||
*
|
||||
* #Timer_ONESHOT_CALLBACK is non-blocking. After Timer_start() is called,
|
||||
* the calling thread will continue execution. When the timer interrupt
|
||||
* is triggered, the specified callback function will be called. The timer
|
||||
* will not generate another interrupt unless Timer_start() is called again.
|
||||
* Calling Timer_stop() or Timer_close() after Timer_start() but, before the
|
||||
* timer interrupt, will prevent the specified callback from ever being
|
||||
* invoked.
|
||||
*
|
||||
* #Timer_ONESHOT_BLOCKING is a blocking call. A semaphore is used to block
|
||||
* the calling thead's execution until the timer generates an interrupt. If
|
||||
* Timer_stop() is called, the calling thread will become unblocked
|
||||
* immediately. The behavior of the timer in this mode is similar to a sleep
|
||||
* function.
|
||||
*
|
||||
* #Timer_CONTINUOUS_CALLBACK is non-blocking. After Timer_start() is called,
|
||||
* the calling thread will continue execution. When the timer interrupt is
|
||||
* treiggered, the specified callback function will be called. The timer is
|
||||
* automatically restarted and will continue to periodically generate
|
||||
* interrupts until Timer_stop() is called.
|
||||
*
|
||||
* #Timer_FREE_RUNNING is non-blocking. After Timer_start() is called,
|
||||
* the calling thread will continue execution. The timer will not
|
||||
* generate an interrupt in this mode. The timer will count down from the
|
||||
* specified period until it reaches 0. The timer will automatically reload
|
||||
* the period and start over. The timer will continue running until
|
||||
* Timer_stop() is called.
|
||||
*
|
||||
* # Resource Allocation #
|
||||
* Each general purpose timer block contains two timers, Timer A and Timer B,
|
||||
* that can be configured to operate independently; or concatenated to operate
|
||||
* as one 32-bit timer. This behavior is managed through a set of resource
|
||||
* allocation APIs. For example, the TimerCC32XX_allocateTimerResource API
|
||||
* will allocate a timer for exclusive use. Any attempt to allocate this
|
||||
* resource in the future will result in a false value being returned from the
|
||||
* allocation API. To free a timer resource, the TimerCC32XX_freeTimerResource
|
||||
* is used. The application is not responsible for calling these allocation
|
||||
* APIs directly.
|
||||
*
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
#ifndef ti_drivers_timer_TimerCC32XX__include
|
||||
#define ti_drivers_timer_TimerCC32XX__include
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <ti/drivers/Timer.h>
|
||||
#include <ti/drivers/Power.h>
|
||||
#include <ti/drivers/dpl/HwiP.h>
|
||||
#include <ti/drivers/dpl/SemaphoreP.h>
|
||||
|
||||
/*!
|
||||
* @def TimerCC32XX_SubTimer
|
||||
*
|
||||
* @brief Sub-timers on the CC32XX
|
||||
*
|
||||
* The timer peripheral supports full width and half width timer operation.
|
||||
* Use the definitions in this enumerated type to specify a full width timer
|
||||
* (32-bit) or half width timer (16-bit) in the hardware attributes. There are
|
||||
* two half width timers per single timer peripheral. A 16-bit timer on this
|
||||
* device has an 8-bit prescaler.
|
||||
*/
|
||||
typedef enum TimerCC32XX_SubTimer_ {
|
||||
TimerCC32XX_timer16A = 0x0001, /*!< Half width timer A */
|
||||
TimerCC32XX_timer16B = 0x0002, /*!< Half width timer B */
|
||||
TimerCC32XX_timer32 = 0x0003, /*!< Full width timer */
|
||||
} TimerCC32XX_SubTimer;
|
||||
|
||||
extern const Timer_FxnTable TimerCC32XX_fxnTable;
|
||||
|
||||
/*!
|
||||
* @brief TimerCC32XX Hardware Attributes
|
||||
*
|
||||
* Timer hardware attributes that tell the TimerCC32XX driver specific hardware
|
||||
* configurations and interrupt/priority settings.
|
||||
*
|
||||
* A sample structure is shown below:
|
||||
* @code
|
||||
* const TimerCC32XX_HWAttrs timerCC32XXHWAttrs[] =
|
||||
* {
|
||||
* {
|
||||
* .baseAddress = TIMERA0_BASE,
|
||||
* .subTimer = TimerCC32XX_timer32,
|
||||
* .intNum = INT_TIMERA0A,
|
||||
* .intPriority = ~0
|
||||
* },
|
||||
* {
|
||||
* .baseAddress = TIMERA1_BASE,
|
||||
* .subTimer = TimerCC32XX_timer16A,
|
||||
* .intNum = INT_TIMERA1A,
|
||||
* .intPriority = ~0
|
||||
* },
|
||||
* {
|
||||
* .baseAddress = TIMERA1_BASE,
|
||||
* .subTimer = TimerCC32XX_timer16B,
|
||||
* .intNum = INT_TIMERA1B,
|
||||
* .intPriority = ~0
|
||||
* }
|
||||
* };
|
||||
* @endcode
|
||||
*/
|
||||
typedef struct TimerCC32XX_HWAttrs_ {
|
||||
/*! The base address of the timer peripheral. */
|
||||
uint32_t baseAddress;
|
||||
|
||||
/*! Specifies a full width timer or half-width timer. */
|
||||
TimerCC32XX_SubTimer subTimer;
|
||||
|
||||
/*! The hardware interrupt number for the timer peripheral. */
|
||||
uint32_t intNum;
|
||||
|
||||
/*! The interrupt priority. */
|
||||
uint32_t intPriority;
|
||||
} TimerCC32XX_HWAttrs;
|
||||
|
||||
/*!
|
||||
* @brief TimerCC32XX_Object
|
||||
*
|
||||
* The application must not access any member variables of this structure!
|
||||
*/
|
||||
typedef struct TimerCC32XX_Object_ {
|
||||
HwiP_Handle hwiHandle;
|
||||
Power_NotifyObj notifyObj;
|
||||
SemaphoreP_Handle timerSem;
|
||||
Timer_CallBackFxn callBack;
|
||||
Timer_Mode mode;
|
||||
uint32_t timer;
|
||||
uint32_t period;
|
||||
uint32_t prescaler;
|
||||
bool isRunning;
|
||||
} TimerCC32XX_Object;
|
||||
|
||||
/*!
|
||||
* @brief Function to allocate a timer peripheral.
|
||||
*
|
||||
* This function is intended to be used by any driver which implements a
|
||||
* timer hardware peripheral. Calling this function will enable power to the
|
||||
* timer peripheral specified by the parameter, baseAddress.
|
||||
*
|
||||
* @param baseAddress The base address of a timer hardware peripheral.
|
||||
*
|
||||
* @param subTimer The TimerCC32XX_subTimer to be allocated.
|
||||
*
|
||||
* @return A bool returning true if the timer resource was successfully
|
||||
* allocated. If the base address is not valid or if the resource is
|
||||
* not available, false is returned.
|
||||
*
|
||||
* @sa TimerCC32XX_freeTimerResource()
|
||||
*/
|
||||
extern bool TimerCC32XX_allocateTimerResource(uint32_t baseAddress,
|
||||
TimerCC32XX_SubTimer subTimer);
|
||||
|
||||
/*!
|
||||
* @brief Function to de-allocate a timer peripheral.
|
||||
*
|
||||
* This function is intended to be used by any driver which implements a
|
||||
* timer hardware peripheral. Calling this function will disable power to the
|
||||
* timer peripheral specified by the parameter, baseAddress, if and only if
|
||||
* the timer peripheral is no longer in use.
|
||||
*
|
||||
* @pre A successful call to TimerCC32XX_allocateTimerResource() using the
|
||||
* baseAddress and subTimer must have been made prior to calling this
|
||||
* API.
|
||||
*
|
||||
* @param baseAddress The base address of a timer hardware peripheral.
|
||||
*
|
||||
* @param subTimer The TimerCC32XX_subTimer to be freed.
|
||||
*
|
||||
* @sa TimerCC32XX_allocateTimerResource()
|
||||
*/
|
||||
extern void TimerCC32XX_freeTimerResource(uint32_t baseAddress,
|
||||
TimerCC32XX_SubTimer subTimer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ti_drivers_timer_TimerCC32XX__include */
|
Loading…
Reference in New Issue