This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

RTOS/CC1310: PWM

Part Number: CC1310


Tool/software: TI-RTOS

Hi

My goal is to use PWM to preform a frequncy sweep, without using task nor a scond timer .

For example, when pwm timeout is over, I want to call  to a callback function,  which changes period and duty.

I wrote a small code:

GPTimerCC26XX_Handle hTimer;

Task_Struct timerTask;
char timerTaskStack[512];


/*
   counts = (fo/f)

   fo - cpu frequency   48[MHz],   f - pwm frequency [Hz]


 if we set count = 1

 1 = 48e6/f

 f = 48e6[Hz] =48[MHz]

 T(1count) = 1/f = 1/48MHz = (1/48)[usec] = (1000/48)[nsec] = (125/6)[nsec]

 count(1nsec) = (6/125)count


 generic formula:

 count = 6t/125 , t[nsec]

*/
#define PWM_GET_COUNT(t) ( ( 6*(t)/125 )  -1  )


PIN_State gpTimerPinState;
PIN_Handle gpTimerPinHandle;


#define PWM_0 IOID_4

// 1msec = 1000000nsec in pwm count term
#define PWM_PERIOD_1 PWM_GET_COUNT(1000000)
// 2msec = 2000000nsec in pwm count term
#deine  PWM_PERIOD_2 PWM_GET_COUNT(2000000)

#define PWM_DUTY_FACTOR (2)

PIN_Config pwmPin[] =  {
 PWM_0 | PIN_INPUT_DIS | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH|
                PIN_INV_INOUT | PIN_PUSHPULL | PIN_DRVSTR_MAX,
                         PIN_TERMINATE };

//////// Prototypes//////////
void timerCallback(GPTimerCC26XX_Handle handle, GPTimerCC26XX_IntMask interruptMask);
void taskFxn(UArg a0, UArg a1);


/////////// implementation///////////////
void main(void)
{
 Board_initGeneral();


 Task_Params taskParams;


  /* Configure the OS task */
  Task_Params_init(&taskParams);
  taskParams.stack = timerTaskStack;
  taskParams.stackSize = sizeof(timerTaskStack);
  taskParams.priority = 1;
  Task_construct(&timerTask, taskFxn, &taskParams, NULL);

 BIOS_start();
}


  void taskFxn(UArg a0, UArg a1)
  {
    GPTimerCC26XX_Params params;
   GPTimerCC26XX_Params_init(&params);
    params.width          = GPT_CONFIG_16BIT;
     params.mode           = GPT_MODE_PERIODIC_UP;
 //params.mode           = GPT_MODE_PWM;

  params.debugStallMode = GPTimerCC26XX_DEBUG_STALL_ON;
 //params.debugStallMode = GPTimerCC26XX_DEBUG_STALL_OFF;
     hTimer = GPTimerCC26XX_open(0, &params);


  gpTimerPinHandle = PIN_open(&gpTimerPinState,pwmPin);

     Types_FreqHz  freq;
     BIOS_getCpuFreq(&freq);
     GPTimerCC26XX_Value loadVal = PWM_PERIOD_1
     GPTimerCC26XX_setLoadValue(hTimer, loadVal);
  GPTimerCC26XX_setMatchValue(hTimer,loadVal/PWM_DUTY_FACTOR);
     GPTimerCC26XX_registerInterrupt(hTimer, timerCallback, GPT_INT_TIMEOUT);

  PINCC26XX_setMux(gpTimerPinHandle, PWM_0, GPT_PIN_0A);

     GPTimerCC26XX_start(hTimer);

     while(1)
  {
   Task_sleep(BIOS_WAIT_FOREVER);
     }
   }


#pragma optimize=none
void timerCallback(GPTimerCC26XX_Handle handle, GPTimerCC26XX_IntMask interruptMask)
{
 static int s_indicator = 0;
 

 
 UInt32 period = (0 == s_indicator) ?  : PWM_PERIOD_1;
 
 if(0 == s_indicator)
 {
  period = PWM_PERIOD_2;
  s_indicator = 1;
 }
 else
 {
  period = PWM_PERIOD_1;
  s_indicator = 0;
 }
 
  GPTimerCC26XX_setLoadValue(hTimer, period);
  GPTimerCC26XX_setMatchValue(hTimer,period/PWM_DUTY_FACTOR);
 
 
}

My problem is when I set pwm mode to GPT_MODE_PERIODIC_UP, I do reach to callback, yet I don't see the pwm when I connect scope to PWM_0.

 

However when I set pwm mode to   GPT_MODE_PWM , I can see the pwm when I connect scope to PWM_0, yet I don't reach to callback.

 

  • Hi,

    you need two driver instances:

    • GPTimer in PWM mode or the PWM driver
    • GPTimer in edge counting mode

    You need to route these two drivers to two different IO pins and create a physical connection. The GPTimer on the CC13xx cannot generate a callback in PWM mode.

  • must I use two different timers? it is very nonconvential thing to do?

    can you answer to my scenarios?

    1)when I set pwm mode to GPT_MODE_PERIODIC_UP, I do reach to callback, yet I don't see the pwm when I connect scope to PWM_0.



    2)However when I set pwm mode to GPT_MODE_PWM , I can see the pwm when I connect scope to PWM_0, yet I don't reach to callback.
  • I even wrote a simmilar code: using low level functions instead of ti-rtos timer driver




    Here i do get to the callback, but I don't see wave on the scope


    GPTimerCC26XX_Handle hTimer;

    Task_Struct timerTask;
    char timerTaskStack[512];


    #define PWM_MGR_TIMEOUTS_BUFF_MAX_LEN (30)

    //buffer contains timeouts between callback i and callback (i-1)
    static UInt32 g_timeoutsBuff[PWM_MGR_TIMEOUTS_BUFF_MAX_LEN];


    #define PWM_0 (IOID_4)


    ////////////////////function prototypes//////////////////////////////////////////

    ///// low level pwm//////

    typedef void (*TPwmIntHandler)(void);



    void LLPWM_Init(TPwmIntHandler a_pPwmIntHandler);

    void LLPWM_Start(void);


    void LLPWM_IntDisable(void);
    void LLPWM_IntEnable(void);
    void LLPWM_IntClr(void);

    void LLPWM_SetPeriod(UInt32 a_Period);
    void LLPWM_SetDuty(UInt32 a_Duty);


    //////// test functions////////////
    void taskFxn(UArg a0, UArg a1);
    void PwmLowLevelCallback(void);

    // auxilary functions
    static UInt32 CalculateInterval(UInt32 a_CurrTs, UInt32 a_PrevTS);



    ////////////////////function implementations/////////////////////////////////////////

    ///// main function
    void main(void)
    {
    Board_initGeneral();


    Task_Params taskParams;

    /* Configure the OS task */
    Task_Params_init(&taskParams);
    taskParams.stack = timerTaskStack;
    taskParams.stackSize = sizeof(timerTaskStack);
    taskParams.priority = 1;
    Task_construct(&timerTask, taskFxn, &taskParams, NULL);

    BIOS_start();
    }




    ///// low level pwm//////

    void LLPWM_Init(void* a_pPinHandle, TPwmIntHandler a_pPwmIntHandler)
    {
    //power configurations
    Power_setDependency(PowerCC26XX_PERIPH_GPT0);
    Power_setDependency(PowerCC26XX_PERIPH_GPIO );

    //debug configurations
    TimerStallControl(GPT0_BASE, TIMER_A, true);
    //interrupt configurations
    TimerIntClear(GPT0_BASE, TIMER_TIMA_TIMEOUT);
    TimerIntEnable(GPT0_BASE, TIMER_TIMA_TIMEOUT);

    //mode configuration
    TimerConfigure(GPT0_BASE,LLPWM_MODE_PWM);
    //callback register
    TimerIntRegister(GPT0_BASE,TIMER_A,a_pPwmIntHandler);

    }

    void LLPWM_Start(void)
    {
    TimerEnable(GPT0_BASE,TIMER_A);
    Power_setConstraint(PowerCC26XX_SB_DISALLOW);
    }

    void LLPWM_IntDisable(void)
    {
    TimerIntDisable(GPT0_BASE, TIMER_TIMA_TIMEOUT);
    }

    void LLPWM_IntEnable(void)
    {
    TimerEnable(GPT0_BASE, TIMER_TIMA_TIMEOUT);
    }

    void LLPWM_IntClr(void)
    {
    TimerIntClear(GPT0_BASE, TIMER_TIMA_TIMEOUT);
    }

    void LLPWM_SetPeriod(UInt32 a_Period)
    {
    UInt32 periodCount = LLPWM_GET_COUNT(a_Period);
    TimerLoadSet(GPT0_BASE, TIMER_A, periodCount);
    }
    void LLPWM_SetDuty(UInt32 a_Duty)
    {
    UInt32 dutyCount = LLPWM_GET_COUNT(a_Duty);
    TimerMatchSet(GPT0_BASE, TIMER_A,dutyCount);
    }


    //////// test functions////////////


    void taskFxn(UArg a0, UArg a1)
    {
    //init low level pwm
    LLPWM_Init(PwmLowLevelCallback);

    //configure IO to timer
    IOCPortConfigureSet(PWM_MANAGER_IO,IOC_PORT_MCU_PORT_EVENT0,IOC_IOMODE_NORMAL|IOC_STRENGTH_MAX|IOC_SLEW_DISABLE);

    UInt32 loadVal = 1e6; // 1msec = 1000000nsec
    LLPWM_SetPeriod(loadVal);
    LLPWM_SetDuty(loadVal/2);

    //start low level pwm timer
    LLPWM_Start();

    while(1)
    {
    Task_sleep(BIOS_WAIT_FOREVER);
    }
    }




    void PwmLowLevelCallback(void)
    {
    //clear interrupt
    LLPWM_IntClr();

    //start timestamp
    static UInt32 s_startTS = 0;
    //buffer index
    static int s_i =0;

    //current timestamp
    UInt32 curr = Clock_getTicks();

    //calculate time elpased between callback i and callback (i-1)
    UInt32 delta = CalculateInterval(curr,s_startTS);

    //put result in buffer, and increment index
    g_timeoutsBuff[s_i++] = delta;

    //verify there is no overflow (cyclic counter)
    if(s_i >= PWM_MGR_TIMEOUTS_BUFF_MAX_LEN )
    {
    s_i=0;
    }

    //update start counter before next callback
    s_startTS = curr;

    }

    // auxilary functions
    static UInt32 CalculateInterval(UInt32 a_CurrTs, UInt32 a_PrevTS)
    {

    UInt32 res;
    if(a_CurrTs >= a_PrevTS)
    {
    res = a_CurrTs - a_PrevTS;
    }
    else
    { //rep around safe
    res = (0xFFFFFFFF - a_PrevTS) + a_CurrTs + 1 ;
    }

    return res;

    }
  • In reply to Richard W.:
    must I use two different timers? it is very nonconvential thing to do?

    can you answer to my scenarios?

    1)when I set pwm mode to GPT_MODE_PERIODIC_UP, I do reach to callback, yet I don't see the pwm when I connect scope to PWM_0.



    2)However when I set pwm mode to GPT_MODE_PWM , I can see the pwm when I connect scope to PWM_0, yet I don't reach to callback.
  • Naor Yehuda said:
    1)when I set pwm mode to GPT_MODE_PERIODIC_UP, I do reach to callback, yet I don't see the pwm when I connect scope to PWM_0.

    Outputting signals is not supported in GPT_MODE_PERIODIC_UP, but only in PWM mode. Other MCUs, for instance AVR is able to do that. The CC1310 is not.

    Naor Yehuda said:
    2)However when I set pwm mode to GPT_MODE_PWM , I can see the pwm when I connect scope to PWM_0, yet I don't reach to callback.

    According to the TRM section 13.4.4, you can configure a callback whenever the PWM output is toggled. Use TimerIntEnable(GPT0_BASE, (1 << 9)) (compare with the TAMR register description on page 1205 in the TRM) to enable the PWM interrupt. This option does not have a nice define in DriverLib. Use TimerEventControl() to choose the edge type. But if the PWM frequency is high, you will very likely miss interrupts and it will also generate a high CPU load. Using a second timer to count the edges of the first timer is likely to be more exact.

  • OK, I with frequencies of 5KHz max
  • Hi Richard

    In my code:

    I added the two lines


    TimerIntEnable(GPT0_BASE, (1 << 9));
    TimerEventControl(GPT0_BASE,TIMER_A,TIMER_EVENT_POS_EDGE);

    under the line : hTimer = GPTimerCC26XX_open(0, &params);


    I can see pwm in scope, yet I cant reach callback
  • Hi,

    I tried it and couldn't get this interrupt to work either. But there is a workaround: The edge detection unit of the IO pins work even when the pin has a special functionality. So you could use that one instead. Here is an example based upon the pinInterrupt driver example.

    /*
     *  ======== pinInterrupt.c ========
     */
    #include <unistd.h>
    
    /* Driver Header files */
    #include <ti/drivers/Power.h>
    #include <ti/drivers/power/PowerCC26XX.h>
    #include <ti/drivers/PIN.h>
    #include <ti/drivers/pin/PINCC26XX.h>
    #include <ti/drivers/pwm/PWMTimerCC26XX.h>
    #include <ti/devices/cc13x0/driverlib/timer.h>
    
    /* Example/Board Header files */
    #include "Board.h"
    
    static PIN_Handle ledPinHandle;
    static PIN_State ledPinState;
    
    // LED1 is the PWM output
    // LED2 is used to display an interrupt.
    // Enabled Edge detection on the PWM output pin. This functionality is independent from GPIO functionality.
    PIN_Config ledPinTable[] = {
        Board_PIN_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX | PIN_IRQ_POSEDGE,
        Board_PIN_LED2 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW  | PIN_PUSHPULL | PIN_DRVSTR_MAX,
        PIN_TERMINATE
    };
    
    
    void onPinValueChanged(PIN_Handle handle, PIN_Id pinId)
    {
        PIN_setOutputValue(ledPinHandle, Board_PIN_LED2, 1);
        PIN_setOutputValue(ledPinHandle, Board_PIN_LED2, 0);
    }
    
    void onTimerInterrupt()
    {
        PIN_setOutputValue(ledPinHandle, Board_PIN_LED2, 1);
        TimerIntClear(GPT0_BASE, 1 << 9);
        PIN_setOutputValue(ledPinHandle, Board_PIN_LED2, 0);
    }
    
    void *mainThread(void *arg0)
    {
        Power_setDependency(PowerCC26XX_PERIPH_GPT0);
    
        ledPinHandle = PIN_open(&ledPinState, ledPinTable);
        PIN_registerIntCb(ledPinHandle, &onPinValueChanged);
    
        // Route this pin to the GPT0A event signal.
        PINCC26XX_setMux(ledPinHandle, Board_PIN_LED1, PINCC26XX_MUX_MCU_PORT_EV_0);
    
        // Config as PWM
        TimerConfigure(GPT0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PWM);
    
        // Register callback.
        TimerEventControl(GPT0_BASE, TIMER_A, TIMER_EVENT_POS_EDGE);
        TimerIntRegister(GPT0_BASE, TIMER_A, &onTimerInterrupt);
        TimerIntEnable(GPT0_BASE, 1 << 9); // Seems to have no effect.
    
        // Set period to 5 kHz
        TimerLoadSet(GPT0_BASE, TIMER_A, 9599);
        // Set duty cycle 50%
        TimerMatchSet(GPT0_BASE, TIMER_A, 4800);
    
        // Start
        TimerEnable(GPT0_BASE, TIMER_A);
        Power_setConstraint(PowerCC26XX_SB_DISALLOW);
        for (;;);
    }