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.

CC2640: PWM

Part Number: CC2640
Other Parts Discussed in Thread: CC1350, CC2650

Hello TI Community,

Currently I am developing firmware with detail information as below

1. MCU: TI CC2640

2. Debugger tool:  SmartRF06 Evaluation Board Kit

3. IDE: 

Code Composer Studio
Version: 8.0.0.00016

OS: Windows 10, v.10.0, x86 / win32

4. BLE Stack: ble_cc26xx_2_01_00_44423

I would like to reduce the LED light intensity.I already did self researched and is being aware that I have to use task/timer/pwm and task_sleep in order to control the output of LEDs.

Another solution is duty cycle, for example, within 1 seconds, I will set LEDs is ON for 600ms and is OFF for 400ms (duty cycle is 60%) so that I can also reduce the LEDs light intensity.

However, I still don't know exactly the way I have to start. Could you please do a little favour and share me how to start from the scratches ?

Best Regards,

Richard.

  • Hi Richard,

    I recommend you have a look on the "pwmled" example, it showcase the PWM functionality by changing the intensity of the LEDs of the Dev-kit.
  • Yes, I also already took a look at PWM sample as you mentioned ...
    However sorry for bothering if any ... but I am still stuck ...


    We just simply to invoke function LED_status_output to set ON/OFF the LED we want.

    For example:

    LED_status_output(XLED_STATUS_RED, XLED_STATE_OFF); => Turn Red LED to OFF

    Anyway, just simply use GPIOPinWrite to allow electricity to LED light so it will be ON.

    My concern is how we can use PWM with a specific GPIO_PIN ?


    Regards,

    Richard.

  • Hi Richard,

    If you have a second look at the PWM example you will find that the the GPIO connected to the PWM signal is assigned inside the board files (CC2640XX_LAUNCHXL.c/h or similar).

    For example, when calling PWM_open(Board_PWM0, &params) you setup PWM functionality on the PIN assigned to the index given by Board_PWM0. In the CC2640XX_LAUNCHXL.c file you will find the PWMTimerCC26XX_HwAttrs structure which contains the PIN and GPTimer related to each index.

    In the example index 0 and 1 are opened and you can see these corresponds to CC1350_LAUNCHXL_PWMPIN0-1 in the PWMTimerCC26XX_HwAttrs structure. If you then look for the definition of CC1350_LAUNCHXL_PWMPIN0 and CC1350_LAUNCHXL_PWMPIN1 you will see that these are equal to the LED pins.
  • Hello All,

    Here is more detail information about BLE Stack I am using now:

    1. BLE Stack 2_01_00_4423
    2. tirtos_simplelink_2_13_00_06
    3. xdctools_3_31_01_33
    4. Code Composer Studio Version: 8.0.0.00016
    5. Debugger: SmartRF06 Evaluation Board Kit

    I already successfully integrated PWM Driver 4760.gptimer_pwm_cc26xx_cc13xx.zip from
    e2e.ti.com/.../1849959

    However, when flash and executed the LED is not "breathing" as PWM duty cycle as expected ...

    Here are some important code:

    ------------------
    --- board.h ---
    ------------------
    ...
    typedef enum CC2650_PWM {
    CC2650_PWM0 = 0,
    CC2650_PWM1,
    // CC2650_PWM2,
    // CC2650_PWM3,
    // CC2650_PWM4,
    // CC2650_PWM5,
    // CC2650_PWM6,
    // CC2650_PWM7,
    CC2650_PWMCOUNT
    } CC2650_PWM;

    #define Board_PWMPIN0 GPIO_PIN_10 //red
    #define Board_PWMPIN1 GPIO_PIN_11 //green
    #define Board_PWMPIN3 GPIO_PIN_3 //blue

    --------------------
    --- board.c ----
    ---------------------
    // GPTimer hardware attributes, one per timer unit (Timer 0A, 0B, 1A, 1B..)
    const GPTimerCC26XX_HWAttrs gptimerCC26xxHWAttrs[CC2650_GPTIMERUNITSCOUNT] = {
    {.baseAddr = GPT0_BASE, .intNum = INT_TIMER0A, .intPriority = (~0), .powerMngrId = PERIPH_GPT0, .pinMux = GPT_PIN_0A, },
    // {.baseAddr = GPT0_BASE, .intNum = INT_TIMER0B, .intPriority = (~0), .powerMngrId = PERIPH_GPT0, .pinMux = GPT_PIN_0B, },
    // {.baseAddr = GPT1_BASE, .intNum = INT_TIMER1A, .intPriority = (~0), .powerMngrId = PERIPH_GPT1, .pinMux = GPT_PIN_1A, },
    // {.baseAddr = GPT1_BASE, .intNum = INT_TIMER1B, .intPriority = (~0), .powerMngrId = PERIPH_GPT1, .pinMux = GPT_PIN_1B, },
    // {.baseAddr = GPT2_BASE, .intNum = INT_TIMER2A, .intPriority = (~0), .powerMngrId = PERIPH_GPT2, .pinMux = GPT_PIN_2A, },
    // {.baseAddr = GPT2_BASE, .intNum = INT_TIMER2B, .intPriority = (~0), .powerMngrId = PERIPH_GPT2, .pinMux = GPT_PIN_2B, },
    // {.baseAddr = GPT3_BASE, .intNum = INT_TIMER3A, .intPriority = (~0), .powerMngrId = PERIPH_GPT3, .pinMux = GPT_PIN_3A, },
    // {.baseAddr = GPT3_BASE, .intNum = INT_TIMER3B, .intPriority = (~0), .powerMngrId = PERIPH_GPT3, .pinMux = GPT_PIN_3B, },
    };

    // GPTimer objects, one per full-width timer (A+B) (Timer 0, Timer 1..)
    GPTimerCC26XX_Object gptimerCC26XXObjects[CC2650_GPTIMERCOUNT];

    // GPTimer configuration (used as GPTimer_Handle by driver and application)
    const GPTimerCC26XX_Config GPTimerCC26XX_config[CC2650_GPTIMERUNITSCOUNT] = {
    { &gptimerCC26XXObjects[0], &gptimerCC26xxHWAttrs[0], GPT_A},
    { &gptimerCC26XXObjects[0], &gptimerCC26xxHWAttrs[1], GPT_B},
    // { &gptimerCC26XXObjects[1], &gptimerCC26xxHWAttrs[2], GPT_A},
    // { &gptimerCC26XXObjects[1], &gptimerCC26xxHWAttrs[3], GPT_B},
    // { &gptimerCC26XXObjects[2], &gptimerCC26xxHWAttrs[4], GPT_A},
    // { &gptimerCC26XXObjects[2], &gptimerCC26xxHWAttrs[5], GPT_B},
    // { &gptimerCC26XXObjects[3], &gptimerCC26xxHWAttrs[6], GPT_A},
    // { &gptimerCC26XXObjects[3], &gptimerCC26xxHWAttrs[7], GPT_B},
    };

    // PWM configuration, one per PWM output
    PWMCC26XX_HwAttrs pwmCC26xxHWAttrs[CC2650_PWMCOUNT] = {
    { .pwmPin = Board_PWMPIN3, .gpTimerUnit = CC2650_GPTIMER0A } ,
    // { .pwmPin = Board_PWMPIN1, .gpTimerUnit = CC2650_GPTIMER0B } ,
    // { .pwmPin = Board_PWMPIN2, .gpTimerUnit = CC2650_GPTIMER1A } ,
    // { .pwmPin = Board_PWMPIN3, .gpTimerUnit = CC2650_GPTIMER1B } ,
    // { .pwmPin = Board_PWMPIN4, .gpTimerUnit = CC2650_GPTIMER2A } ,
    // { .pwmPin = Board_PWMPIN5, .gpTimerUnit = CC2650_GPTIMER2B } ,
    // { .pwmPin = Board_PWMPIN6, .gpTimerUnit = CC2650_GPTIMER3A } ,
    // { .pwmPin = Board_PWMPIN7, .gpTimerUnit = CC2650_GPTIMER3B } ,
    };

    // PWM object, one per PWM output
    PWMCC26XX_Object pwmCC26xxObjects[CC2650_PWMCOUNT];


    extern const PWM_FxnTable PWMCC26XX_fxnTable;
    //PWM configuration (used as PWM_Handle by driver and application)
    const PWM_Config PWM_config[CC2650_PWMCOUNT+1] = {
    { &PWMCC26XX_fxnTable, &pwmCC26xxObjects[0], &pwmCC26xxHWAttrs[0] },
    // { &PWMCC26XX_fxnTable, &pwmCC26xxObjects[1], &pwmCC26xxHWAttrs[1] },
    // { &PWMCC26XX_fxnTable, &pwmCC26xxObjects[2], &pwmCC26xxHWAttrs[2] },
    // { &PWMCC26XX_fxnTable, &pwmCC26xxObjects[3], &pwmCC26xxHWAttrs[3] },
    // { &PWMCC26XX_fxnTable, &pwmCC26xxObjects[4], &pwmCC26xxHWAttrs[4] },
    // { &PWMCC26XX_fxnTable, &pwmCC26xxObjects[5], &pwmCC26xxHWAttrs[5] },
    // { &PWMCC26XX_fxnTable, &pwmCC26xxObjects[6], &pwmCC26xxHWAttrs[6] },
    // { &PWMCC26XX_fxnTable, &pwmCC26xxObjects[7], &pwmCC26xxHWAttrs[7] },
    { NULL, NULL, NULL }
    };


    /*
    * ========================= IO driver initialization =========================
    * From main, PIN_init(BoardGpioInitTable) should be called to setup safe
    * settings for this board.
    * When a pin is allocated and then de-allocated, it will revert to the state
    * configured in this table.
    */
    PIN_Config BoardGpioInitTable[] = {

    //Board_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX, /* LED initially off */
    //Board_LED2 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX, /* LED initially off */
    //Board_LED3 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX, /* LED initially off */
    //Board_LED4 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX, /* LED initially off */
    //Board_KEY_SELECT | PIN_INPUT_EN | PIN_PULLUP | PIN_HYSTERESIS, /* Button is active low */
    //Board_KEY_UP | PIN_INPUT_EN | PIN_PULLUP | PIN_HYSTERESIS, /* Button is active low */
    //Board_KEY_DOWN | PIN_INPUT_EN | PIN_PULLUP | PIN_HYSTERESIS, /* Button is active low */
    //Board_KEY_LEFT | PIN_INPUT_EN | PIN_PULLUP | PIN_HYSTERESIS, /* Button is active low */
    //Board_KEY_RIGHT | PIN_INPUT_EN | PIN_PULLUP | PIN_HYSTERESIS, /* Button is active low */
    //Board_3V3_EN | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL, /* 3V3 domain off initially */
    //Board_LCD_MODE | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL, /* LCD pin high initially */
    //Board_LCD_RST | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL, /* LCD pin high initially */
    //Board_LCD_CSN | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL, /* LCD pin high initially */
    //Board_UART_TX | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL, /* UART TX pin at inactive level */
    PIN_ID(Board_PWMPIN3) | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX ,
    PIN_TERMINATE /* Terminate list */
    };

    /////////////
    main.c
    /////////////

    /*
    * ======== main ========
    */
    int main()
    {
    PIN_init(BoardGpioInitTable);


    #ifndef POWER_SAVING
    /* Set constraints for Standby, powerdown and idle mode */
    Power_setConstraint (Power_SB_DISALLOW);
    Power_setConstraint (Power_IDLE_PD_DISALLOW);
    #endif //POWER_SAVING

    /* Initialize ICall module */
    ICall_init();

    /* Start tasks of external images - Priority 5 */
    ICall_createRemoteTasks();

    /* Kick off profile - Priority 3 */
    GAPRole_createTask();

    /* Kick off application - Priority 1 */
    KeyFobDemo_createTask();

    /* enable interrupts and start SYS/BIOS */
    BIOS_start();

    return 0;
    }


    ////////////////////////////////
    /// keyfobdemo.c
    ///////////////////////////////
    static void KeyFobDemo_init(void)
    {
    uint8_t i;
    uint8_t buf;

    se2_inz();

    se2_byte_read((WORD)0, &buf);
    if(buf == 0xAA)
    {
    for( i = 0; i < 10; i++ )
    {
    se2_byte_read((WORD)(1+i), &buf);
    if(buf != 0xFF)
    {
    deviceName[2+i] = buf;
    attDeviceName[i] = buf;
    }
    }
    }

    //hvi1hc PWM
    PWM_Params pwmParams;
    PWM_Params_init(&pwmParams);
    pwmParams.idleLevel = PWM_IDLE_LOW;
    pwmParams.periodUnit = PWM_PERIOD_US;
    pwmParams.periodValue = PERIOD_US;
    pwmParams.dutyUnit = PWM_DUTY_FRACTION;
    pwmParams.dutyValue = 0;

    /* PWM open should will set to pin to idle level */
    hPWM0 = PWM_open(CC2650_PWM0, &pwmParams);
    if(hPWM0 == NULL) {
    //Log_error0("Opening PWM failed");
    while(1);
    }

    PWM_start(hPWM0);
    PWM_setDuty(hPWM0, 10);
    ......
    ......
    }



    Regards,
    Vinh.
  • Hi,

    Could you elaborate on " LED is not "breathing" as PWM duty cycle as expected"? Is the LED lit at all or is it simply dimmed without the breathing part?
  • Hi, thanks for your concern. "Breathing" mean I expected the LED will be off initially and be lighter a little by little until reached lightest. After that LED will be less light until it will be off. Next it will be lighter a little by little until reached lightest again. This cycle will be repeated again and again !

    I also concern and I am not sure PWM Driver I integrated whether it can invoke GPIO or not ...
  • I followed the sample main_pwm_sine.c from inside Driver 4760.gptimer_pwm_cc26xx_cc13xx

    7652.4760.gptimer_pwm_cc26xx_cc13xx.zip

  • Hi,

    So do I understand it correctly that in your case, the LED is not lit at all? Or is it somewhat working, just not as you expect?

    From what I can tell the PWM part of your code looks OK and I would expect a ~10% dusty cycle, what is the period you are using?
  • Yes the LED is still brightest and very annoyed our human eyes. Even it seems that I integrated PWM Driver into my code (actually build base on TI KeyFob sample project) successfully but I am sure PWM moduce can apply to specific GPIO as I wanted or not ... If you don't mind and feel free, comfortable. I will post the code so that we can debug together.

  • Hi,

    Are you able to probe the signal with a scope to see if there is any PWM signal there? I would recommend that you maybe try out a more basic example first and get the whole LED thing to work. For example you could work on the pwmled example to get your LEDs to behave as you expect. When that works you could start porting it to the keyfob example. This will make it easier to debug as we can verify the code works before putting it into the keyfob.
  • Yes, I totally agreed, I am doing as your suggestions now.

  • Hi, I used Oscilloscope to check but there are not PWM  signal on desire GPIO port. I have a confusion:

    - I integrated PWM Driver into current TI KeyFob sample project and compiled, executed successfully.

    hPWM = PWM_open(CC2650_PWM0, &params);

    hPWM is not NULL when finishing PWM_open.

    I turn LED on before invoking pwm_job, PWM is not working

    I turned LED off before invoking pwm_job, PWM is not working also.

    In my program structure, I defined 2 function: pwm_int and pwm_job

    pwm_int will be initialized only one time when the program start.

    pwm_job will be in the  loop

    //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
    
    static void KeyFobDemo_taskFxn(UArg a0, UArg a1){
    
    // Initialize application
    
     KeyFobDemo_init();
    
     ///////////////////////////////////////////////////////////
    
     // Application main loop
    
     for (;;)
    
     {
    
        .......
    
        .......
    
        .......
    
        if (events & KFD_APR_EVT)
    
       {
    
         events &= ~KFD_APR_EVT;
    
         apr_drv();
    
       }
    
        .............
    
        .............
    
        .............
    
     }
    
    }
    
    //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
    
    static void KeyFobDemo_init(void)
    
    {
    
          .........
    
          .........
    
          apr_inz();
    
    }
    
    //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
    
    *****************************************
    *										*
    *			Initialize					*
    *										*
    ****************************************
    */
    void apr_inz( void )
    {
    ......
    ...... pwm_int(); } /* ***************************************** ** * * * * ***************************************** */ void apr_drv( void ) { BYTE i; sw_job(); ad_job(); ble_connect_job(); blower_job(); check_charge(); led_job(); pwm_job(); sw_clear();
    .........
    .......... }

    /*
    *****************************************
    *                                       *
    *                                       *
    *                                       *
    *****************************************
    */
    static void pwm_int(void)   //hvi1hc
    {
        PWM_Params params;
        //uint16_t   pwmPeriod = 3000;      // Period and duty in microseconds
        //uint16_t   duty      = 0;
        //uint16_t   dutyInc   = 100;
    
        PWM_Params_init(&params);
        //params.dutyUnit = PWM_DUTY_US;
        params.dutyUnit = PWM_DUTY_FRACTION;
        params.dutyValue = 10;
        //params.periodUnit = PWM_PERIOD_US;
        params.periodUnit = PWM_PERIOD_US;
        params.periodValue = pwmPeriod;
        //pwm1 = PWM_open(Board_PWM0, &params);
        hPWM = PWM_open(CC2650_PWM0, &params);
    
        if (hPWM == NULL) {
            //System_abort("Board_PWM0 did not open");
            Task_exit();
        }
        PWM_start(hPWM);
    }
    
    /*
    *****************************************
    *                                       *
    *                                       *
    *                                       *
    *****************************************
    */
    
    static void pwm_job(void)
    {
    
        PWM_setDuty(hPWM, duty);
        duty = (duty + dutyInc);
        if (duty == pwmPeriod || (!duty)) {
            dutyInc = - dutyInc;
        }
    
    }

  • I also attached an email (HTML format) which described detail Steps I did to integrate PWM Driver into TI KeyFob Code.

    Kindly take a look and let me know if any.

    Many Thanks!

    EMAIL CLARIFICATION.html

  • Hi Richard,

    Thanks for the detailed clarification, it makes it easier to follow what you are doing. Before anything else I would like to ask if you did implement only the PWM in an empty example and got that to work before adding into the keyfob as previously suggested?

    I also see you have a "led_job", what does this do?
  • Discussion was continued here, I believe it is solved:
    e2e.ti.com/.../2579332
  • Hello M-W,

    Oh thanks, PWM can work perfectly now. I misunderstood PIN parameters for PWM_open. Before I used GPIO PIN address, it was wrong. I must be PIN index number.

    Now I changed to PIN index number and PWM is working well.

    Regards,

    Richard.