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.

Pre-release CC26XX / CC13XX PWM and GPTimer driver

Other Parts Discussed in Thread: CC2650, BLE-STACK, SYSBIOS, CC1310

There have been a lot of requests from customers on how to use the GPTimers to generate PWM outputs on the CC26XX.

With the complexity of the devices and the power management being handled by TI RTOS this can be a bit complex and we have therefore made a PWM driver and a GPTimer driver that is posted to this thread.

These drivers will work with TI RTOS >= 2.13.00.06 and will be officially included in the TI RTOS February release (unfortunately we were to late for the December release).

Features GPTimer driver

  • Support for both split (16+8 bits) and concatenated mode (32-bit) timer usage giving you up to 8 (or 4) timers in total
  • Modes
    • Oneshot mode
    • Periodic mode
    • Edge count - capture and count number of input edges from pins (falling, rising or both)
    • Edge time - capture and store time when input edge occured (falling, rising or both)
    • PWM
  • Set load and match value of timers
  • Built-in power management
  • Interrupt generation for match, capture and timeout
  • Debug stall - halt the timer when the debugger halts

Features PWM driver

  • Generates up to 8x PWM ouputs
  • Configurable period in microseconds, Hertz or raw timer counts
  • Configurable duty cycle in microseconds, fraction of period or raw timer counts
  • Duty and period can be changed at run-time
  • Configurable Idle level (output stops at this level when timer is not running)

None of the timers support DMA at the moment as this requires changes to the current DMA driver.

Usage

  • Add the necessary hardware descriptors, objects and configuration to your board files as shown in Board.c/h.
  • Add a compiler search path to the drivers base or add the drivers to your TI RTOS installation
  • Add the driver source to your workspace
  • Follow the examples included (main_*.c)

Let us know if you have any questions / feedback in this thread and we will try our best to help out!

4760.gptimer_pwm_cc26xx_cc13xx.zip

Update 2015-12-17: Modified timer driver to compile in CCS without enabling C99 mode

Update 2016-02-16 : Needed modification for using PWM/GPTimer driver with TI-RTOS version >=2.15, red font : original content ; green font : updated content 

1. GPTimerCC26XX.h

#include <ti/sysbios/family/arm/cc26xx/Power.h>
#include <ti/sysbios/family/arm/cc26xx/PowerCC2650.h>

to

#include <ti/drivers/Power.h>
#include <ti/drivers/power/PowerCC26XX.h>

2. change Power_SB_DISALLOW to PowerCC26XX_SB_DISALLOW in GPTimerCC26xx.c

3. For board.c, change the following 

{.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, },

to

{.baseAddr = GPT0_BASE, .intNum = INT_GPT0A, .intPriority = (~0), .powerMngrId = PowerCC26XX_PERIPH_GPT0, .pinMux = GPT_PIN_0A, }, 

{.baseAddr = GPT0_BASE, .intNum = INT_GPT0B, .intPriority = (~0), .powerMngrId = PowerCC26XX_PERIPH_GPT0, .pinMux = GPT_PIN_0B, },

4. PWMCC26XX.c, you can either comment out the log_error or just change the duty/period to dutyValue/periodValue if you got compile error. 

Regards,
Svend

  • I did. I have attached a zip of the project, if you have time to look i'd really appreciate it. If not I'd love to hear any more suggestions. 

    Thanks again

    empty_project.zip

  • did you physically include the ti/driver stuff in your ccs project? I included that into your project and managed to compile without any problem.(But I directly dropped into the workspace)

  • Ah! When I directly drop it into the workspace it works.

    Thanks a million for your help. I really appreciate your time and help!

    edit: Your last post doesnt seem to have a Verify Answer button but i'll click verify if it appears!

  • Hi,

    I have used this pre-release and it worked prio to TI-RTOS 2.15.00.17.
    Now I'm trying to use it on TI-RTOS 2.15.00.17 version . I've modified it to be compatible with this new release.
    The PWM is not working all the time. Looks like the CC1310 is entering in standby mode and its freezing the GPT.
    Are you going to update the pre-release? Or should I wait for the PWM driver to be included in the official release?

    Thank you,
    Milorad
  • PWM generated from GPTimer can't be used in standby no matter which TI-RTOS version you are using. GPTimer always count in the same speed as systembusclk which uses 48MHz.
  • I agree.
    But PWM_start function which is calling at the end
    Power_setConstraint(PowerCC26XX_SD_DISALLOW);
    should not allow the controller into standby more during idle task, isn't it?
    Is there any problem with set constraint function or do I need to do something in configuration?

    Regards,
    Milorad
  • Yes,

    You are correct, that function prevents controller goes into standby.
    Can you share what you did with PWM driver? What do you mean by it does not work all the time? under what condition did the device goes into standby?

    Did you do the same modification as following to make it to work with 2.15?

    1. GPTimerCC26XX.h

    #include <ti/sysbios/family/arm/cc26xx/Power.h>
    #include <ti/sysbios/family/arm/cc26xx/PowerCC2650.h>

    to

    #include <ti/drivers/Power.h>
    #include <ti/drivers/power/PowerCC26XX.h>

    2. change Power_SB_DISALLOW to PowerCC26XX_SB_DISALLOW in GPTimerCC26xx.c

    3. For board.c, change the following

    {.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, },

    to

    {.baseAddr = GPT0_BASE, .intNum = INT_GPT0A, .intPriority = (~0), .powerMngrId = PowerCC26XX_PERIPH_GPT0, .pinMux = GPT_PIN_0A, },

    {.baseAddr = GPT0_BASE, .intNum = INT_GPT0B, .intPriority = (~0), .powerMngrId = PowerCC26XX_PERIPH_GPT0, .pinMux = GPT_PIN_0B, },

    4. PWMCC26XX.c, you can either comment out the log_error or just change the duty/period to dutyValue/periodValue if you got compile error.
  • I can see with the oscilloscope that the PWM signal is generated for 200-300 microseconds then it just stop. The pin could be on High or Low level. Just before the end of sleep time the PWM signal is coming back.

    In the task I'm performing the following:

    PWM_start(hPWM_IR_LED_1);

    PIN_setOutputValue(irPinHandle, BOARD_IR_POWER, 1);   

    /*sleep 120ms*/
    Task_sleep((120*1000) / Clock_tickPeriod);

    Here is config file(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_GPT0A, .intPriority = (~0), .powerMngrId = PowerCC26XX_PERIPH_GPT0, .pinMux = GPT_PIN_0A, },
      {.baseAddr = GPT0_BASE, .intNum = INT_GPT0B, .intPriority = (~0), .powerMngrId = PowerCC26XX_PERIPH_GPT0, .pinMux = GPT_PIN_0B, },
      {.baseAddr = GPT1_BASE, .intNum = INT_GPT1A, .intPriority = (~0), .powerMngrId = PowerCC26XX_PERIPH_GPT1, .pinMux = GPT_PIN_1A, },
      {.baseAddr = GPT1_BASE, .intNum = INT_GPT1B, .intPriority = (~0), .powerMngrId = PowerCC26XX_PERIPH_GPT1, .pinMux = GPT_PIN_1B, },
    };

    /* 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},
    };

    /* PWM configuration, one per PWM output*/
    PWMCC26XX_HwAttrs pwmCC26xxHWAttrs[CC2650_PWMCOUNT] = {
      { .pwmPin = Board_PWMPIN0, .gpTimerUnit = CC2650_GPTIMER0A } ,
      { .pwmPin = Board_PWMPIN1, .gpTimerUnit = CC2650_GPTIMER0B } ,
      { .pwmPin = Board_PWMPIN2, .gpTimerUnit = CC2650_GPTIMER1A } ,
      { .pwmPin = Board_PWMPIN3, .gpTimerUnit = CC2650_GPTIMER1B } ,
    };

    /* 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] },
      { NULL,               NULL,                 NULL                 }
    };

    I've also changed the Power_SB_DISALLOW macro otherwise is not compiling.


    Regards,

    Milorad

  • What modification did you do to Power_SB_DISALLOW? You should just replace it with PowerCC26XX_SB_DISALLOW. If you follow what I posted, the project will compile and run.
    Have you tried to stop the PWM before you call task_sleep?
  • Yes, I've changed it:
    ----------------------------------------------------------------------------------------------
    /*!
    * @brief Set Standby constraint while using timer to avoid TI RTOS
    * going into standby when timer is running. As constraints are
    * counting, store constraint status and access atomically and only
    * once per timer resource.
    */
    static inline void GPTimerCC26XXThreadsafeConstraintSet(GPTimerCC26XX_Handle handle)
    {
    GPTimerCC26XX_Object *object = handle->object;

    uint32_t key = Hwi_disable();
    /* Only set if not already set */
    if (object->powerConstraint[handle->timerPart])
    {
    Hwi_restore(key);
    return;
    }
    object->powerConstraint[handle->timerPart] = true;
    Hwi_restore(key);
    Power_setConstraint(PowerCC26XX_SD_DISALLOW);
    }
    ----------------------------------------------------------------------------------------------

    I didn't try to stop the PWM before sleep function. I don't see a point. Are you suggesting that something else is stoping the timer?

    Now.. I've just changed the power policy to "wait for interrupt" (PowerCC26XX_doWFI):
    ----------------------------------------------------------------------------------------------
    /*
    * ============================= Power begin ===================================
    */
    const PowerCC26XX_Config PowerCC26XX_config = {
    .policyInitFxn = NULL,
    #if 0
    .policyFxn = &PowerCC26XX_standbyPolicy,
    #else
    .policyFxn = &PowerCC26XX_doWFI,
    #endif
    .calibrateFxn = &PowerCC26XX_calibrate,
    .enablePolicy = TRUE,
    .calibrateRCOSC_LF = TRUE,
    .calibrateRCOSC_HF = TRUE,
    };
    /*
    * ============================= Power end ===================================
    */
    ----------------------------------------------------------------------------------------------

    ..and is working fine. No problem.

    Would please check your power management?
    I think it is not taking into consideration the constraint or maybe there is a problem in the configuration.
    Do I need to activate these constraints?

    Regards,
    Milorad
  • Ok.. Now I see where I was wrong.
    I put PowerCC26XX_SD_DISALLOW instead of PowerCC26XX_SB_DISALLOW.
    .. problem solved.

    Regards,
    Milorad
  • Hi,

    Probably my question needs to be under this post\topic.

    My question is releated to setting up GPTimer as ADC trigger source.

    I am using the GPTimer drivers with the following setup.

    I am not recieving interrupts:

    ....

    //Setup ADC interrupt routine

    Hwi_construct(&hwi, INT_AUX_ADC, adcIsr, &hwiParams, NULL);  

    AUXWUCClockEnable(AUX_WUC_ADI_CLOCK|AUX_WUC_SOC_CLOCK);

    //Setup Timer

    GPTimerCC26XX_Params params;

    GPTimerCC26XX_Params_init(&params);

    params.width = GPT_CONFIG_16BIT;

    params.mode = GPT_MODE_PERIODIC_UP;

    params.debugStallMode = GPTimerCC26XX_DEBUG_STALL_OFF;

    hTimer = GPTimerCC26XX_open(CC2650_GPTIMER0A, &params);

    Types_FreqHz freq;

    BIOS_getCpuFreq(&freq);

    GPTimerCC26XX_Value loadVal = freq.lo / 1000 - 1; //47999

    GPTimerCC26XX_setLoadValue(hTimer, loadVal);

    // Set up ADC

    AUXADCEnableAsync(AUXADC_REF_VDDA_REL, AUXADC_TRIGGER_GPT0A);

    // Disallow STANDBY mode while using the ADC.

    Power_setConstraint(Power_SB_DISALLOW);

    Power_setDependency(XOSC_HF);

    //Set Initial Sampling Source

    AUXADCSelectInput(ADC_COMPB_IN_AUXIO6);

    GPTimerCC26XX_start(hTimer);

    Result: adcIsr interrupt routine is not called.

    Questions:

    1. Why isnt adcIsr reached in code. Shouldnt it be triggered?.

    2. Should I discard the the adcIsr and setup a Timer interrupt instead ?

    GPTimerCC26XX_registerInterrupt(hTimer, timerCallback, GPT_INT_TIMEOUT)

    and inside it do AUXADCReadFifo Like:

    void timerCallback(GPTimerCC26XX_Handle handle, GPTimerCC26XX_IntMask interruptMask)

    {

    sample = AUXADCReadFifo();

    //Change to other pin

    AUXADCSelectInput(...)

    }

    3. Is it possible at all to change the ADC input via AUXADCSelectInput while ADC is triggered via a Timer?.

    If yes , since I am changing the input in the ISR would the timer be still causing ADC conversions and putting them in the FIFO?, Should I flush the FIFO

    so the correct value is captured?.

    Thanks

    Tamir

  • Has anybody gotten the GPTimer driver to reliably generate interrupts with sub-1ms timer load values.  I've been doing some experimenting with the driver with a CC1310EM (SmartRF06 + EM) and I've having a hard time getting predictable results out of it.  I'm looking to write a Maxim 1-Wire driver using one of the timers but I can't get it to generate accurate enough pulses even for this.  (Want pulses accurate to 2us)  Here's some sample timings that I've gotten by measuring a pulse on a GPIO pin which is timed by one of the GPTimers.  I used an oscilloscope to measure the pulse timings.  Expected delay time is based on 48MHz clock on CC1310EM.

    Load Value Expected Delay Time (us) Actual Delay Time (us)
    48000 1000 1002
    4800 100 135
    2400 50 73
    1200 25 42
    600 12.5 34.8
    300 6.25 15.2
    150 3.125 13.2

    As you can see, the delay time of the GPIO pulse diverges from the expected time as it get shorted.  It's not, however, a fixed time (e.g.  actual != expected + some constant delay).  It seems to tend towards 9 or 10us.  Has anybody tried to use these drivers for something similar?  Could anybody who worked on these drivers give some insight?  Since a lot of work was clearly put into these drivers I'd like to make them work instead of having to write a custom driver on top of the lower level stuff if possible.

    I've written a barebones program to test this, copied below.  It's a modification of the PinInterrupt example.  No funny RTOS stuff, just two interrupts driving the GPIO low then high again.

    Thanks for your help!

     
    
    #include <xdc/std.h>
    #include <xdc/runtime/Types.h>
    
    
    /* BIOS Header files */
    #include <ti/sysbios/BIOS.h>
    #include <ti/drivers/Power.h>
    #include <ti/drivers/power/PowerCC26XX.h>
    
    #include "ti/drivers/timer/GPTimerCC26XX.h"
    
    
    /* TI-RTOS Header files */
    #include <ti/drivers/PIN.h>
    #include <ti/drivers/pin/PINCC26XX.h>
    
    /* Example/Board Header files */
    #include "Board.h"
    
    /* Pin driver handles */
    static PIN_Handle buttonPinHandle;
    static PIN_Handle ledPinHandle;
    
    /* Global memory storage for a PIN_Config table */
    static PIN_State buttonPinState;
    static PIN_State ledPinState;
    
    static GPTimerCC26XX_Handle hTimer;
    
    PIN_Config ledPinTable[] = {
        Board_LCD_MODE | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX,
        PIN_TERMINATE
    };
    
    GPTimerCC26XX_Value loadVal_1us;
    
    /*
     * Application button pin configuration table:
     *   - Buttons interrupts are configured to trigger on falling edge.
     */
    PIN_Config buttonPinTable[] = {
        Board_BUTTON0  | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE,
        PIN_TERMINATE
    };
    
    //Callback on button press, pulse GPIO low then start timer void buttonCallbackFxn(PIN_Handle handle, PIN_Id pinId) { uint32_t delay_ticks = 4800; /* Debounce logic, only toggle if the button is still pushed (low) */ CPUdelay(8000*50); if (!PIN_getInputValue(pinId)) { /* Toggle LED based on the button pressed */ switch (pinId) { case Board_BUTTON0: GPTimerCC26XX_setLoadValue(hTimer, delay_ticks); PINCC26XX_setOutputValue(Board_LCD_MODE,0); GPTimerCC26XX_start(hTimer); break; default: /* Do nothing */ break; } } } /* * ======== main ======== */ //Timer callback, set gpio high again void timerCallback(GPTimerCC26XX_Handle handle, GPTimerCC26XX_IntMask interruptMask) { // interrupt callback code goes here. Minimize processing in interrupt. PINCC26XX_setOutputValue(Board_LCD_MODE,1); } int main(void) { /* Call board init functions */ PIN_init(BoardGpioInitTable); /* Open LED pins */ ledPinHandle = PIN_open(&ledPinState, ledPinTable); if(!ledPinHandle) { } buttonPinHandle = PIN_open(&buttonPinState, buttonPinTable); if(!buttonPinHandle) { } /* Setup callback for button pins */ if (PIN_registerIntCb(buttonPinHandle, &buttonCallbackFxn) != 0) { } GPTimerCC26XX_Params params; GPTimerCC26XX_Params_init(&params); params.width = GPT_CONFIG_16BIT; params.mode = GPT_MODE_ONESHOT_UP; params.debugStallMode = GPTimerCC26XX_DEBUG_STALL_ON; hTimer = GPTimerCC26XX_open(CC2650_GPTIMER2B, &params); Power_setDependency(PowerCC26XX_XOSC_HF); GPTimerCC26XX_registerInterrupt(hTimer, timerCallback, GPT_INT_TIMEOUT); /* Start kernel. */ BIOS_start(); return (0); }

  • There will be some delay caused by SW when you use callback function to control the IO toggle. If you directly use PWM driver(directly output the EVENT to HW module) to checkout the timing accuracy, the error would be a lot smaller( less than 500ns)
  • Thanks for the reply Christin. Could you elaborate a bit on what you mean by "directly output the EVENT to the HW module"? I didn't realize how much overhead was involved in that callback mechanism. As an experiment, I tried implementing the above code using the Sys/Bios HAL timer instead, it's more precise but still off by ~5us.
    Thanks!
  • Hi,

    Im working on your adc example that is in this link

    e2e.ti.com/.../1433112

    Im trying to compile this code and it gives an error "identifier "INT_AUX_ADC" is undefined" which header file has the identifier? I cant find.

    Thanks..
  • Hey Svend,

    thanks so much for making this available. I managed to make the buzzer of the SensorTag beep with a fixed-frequency PWM.

    My next goal is to make a driver to play audio by changing the duty value every time a timeout occurs.

    I modified the driver code and set the GPT_TAMR_TAMRSU_TOUPDATE and GPT_TAMR_TAILD_TOUPDATE flags to make sure I get no glitches.

    I'm trying to get an interrupt every time the timeout occurs, this is the relevant code:

    void timeOutHandler(GPTimerCC26XX_Handle handle, GPTimerCC26XX_IntMask interruptMask) {
        timeOutCounter++;
        sineIdx = (sineIdx + 1) % SINE_LEN;
        HWREG(baseAddr + GPT_O_TAMATCHR) = sineTable[sineIdx];
    }

      GPTimerCC26XX_IntMask intMask = ????;
      GPTimerCC26XX_HwiFxn callback = &timeOutHandler;
      PWMCC26XX_Object* object  = hPWM->object;
      GPTimerCC26XX_Handle timerHandle = object->hTimer;
      GPTimerCC26XX_registerInterrupt(timerHandle, callback, intMask);
    

    The second block comes after PWM_Open.

    What is the right mask to get an interrupt on the timeout?

    I've found out that the interrupt is indeed fired at the right rate by letting the firmware run for 20 seconds and checking the timeOutCounter value.

    The problem is that the sine cannot be played back correctly.

    Is there anything I'm doing wrong? Please advise.

    Flogo

  • Hi LimeLight,

    Try to include hw_ints.h.
  • Hi,
    INT_AUX_ADC (renamed to INT_AUX_ADC_IRQ) this events name is changed so the problem solved.

    Thanks..
  • You are welcome and it's good to know the problem is solved.
  • Hi,

    i was able to include the drivers and board files to an empty sensortag project with no errors.
    I compiled it and flash it on the sensortag.
    Where are the output pins from the PWM?
    On the Board.h files i see those:
    #define Board_PWMPIN0 IOID_25 //7x7 LED1
    #define Board_PWMPIN1 IOID_27 //7x7 LED2

    But i couldnt measure any signal on them.

    When i include them o the pin table i get a crash as soon as the device runs:
    PIN_Config ledPinTable[] = {
    Board_LED0 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    Board_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    Board_PWMPIN0| PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX, /* Buzzer initially off */
    PIN_TERMINATE
    };

    On the main i dondt see this pin getting initiated, i only see this:

    hPWM = PWM_open(CC2650_PWM0, &pwmParams);

    and the CC2650_PWM0 is defined on the Board.h file like this:

    typedef enum CC2650_PWM {
    CC2650_PWM0 = 0,
    CC2650_PWM1,

    What about the PWMPIN0 Pins?
  • Hi Mike,

    I don't have the source code here but I remember I ran into this problem too.

    Take a look at the example that came with the driver: In the header file that corresponds to the .c file with the example code there's a definition somewhere that 'connects' the PWM output to the GPIO.

    Hope that helps.

    Flogo
  • Hi Flogo,

    do you mean inside the Board.c file example:

    // PWM configuration, one per PWM output
    PWMCC26XX_HwAttrs pwmCC26xxHWAttrs[CC2650_PWMCOUNT] = {
    { .pwmPin = Board_PWMPIN0, .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 } ,
    };

    Is this the connection that says PWM output to the BOARD_PWMPIN0?

    I have a problem by openning the PWM:

    hPWM = PWM_open(CC2650_PWM0, &pwmParams);


    if(hPWM == NULL)
        ;//test
    else
           PWM_start(hPWM);

    "PWM_open(..)" returns NULL and if i do "PWM_start(...)" it than crashes.

    Any idea?

  • can you attach your board.c, board.h files here?
    Which version of TI-RTOS you are using?
  • Hi Christin,

    i got it to work,
    i had to initiate the PWM Pins to the Board.c.

    Thanks

    Michael
  • Load Value Expected Delay Time (us) Actual Delay Time (us)
    1200 25 42

    Would this be the values that would get us close to a 20Khz timer?
  • This is possibly a dumb question, but one that I can't seem to find the answer to in the documentation, or on these forums: Which timers are in use by the RTOS/BLE-STACK, and which timers are available for me to use with the GPTimer/PWM Drivers? I'm having mixed results depending on which ones I use in my code.

    I tried using the ROV in the debugger to see what was going on, but I'm not sure I understand what I'm looking at.

    Does anyone have any ideas?
    Thanks,
    -Ben
  • Hi sven,
    I am trying to build this standalone project for buzzer. Seems like you have complete solution for this. But its all scattered across many blogs. i am not able to follow up what modifications to be done to make it working. Too many changes , so getting lost. considering i am new can you please post code which compiles on ccs6.1 with required header files/drivers to be placed. Request you to please do that, as i am getting compiler errors such as these :
    cannot open source file "ti/targets/arm/std.h".
    Request you to please do the needful.
  • You can use all timers in the GPTimer/PWM drivers
  • I would suggest you start from TI-RTOS empty_min example project and make sure it compiles.
  • Hi Christin,

          Thanks for your reply. I started working TIRTOS min project and got it working with sven's code of activating buzzer.
    Couple of things that i did not understand are :
    1) Pin Muxed between GPTA and Buzzer pin DIO21. why ?
    2) How do i set period of square wave that i generate for buzzer. 
               In code he used counter as ,

    if(direction)

    {

    counter += 30;

    }

    else {

    counter -= 30;

    }


    if(counter >= COUNTER_MAX)

    {

    counter = COUNTER_MAX;

    direction = 0;

    }

    else if(counter <= COUNTER_MIN)
    {

    counter = COUNTER_MIN;

    direction = 1;

    }

    Here in comment it says 1% of duty cycle , how ? As i understand the period is 300*Timefactor, hence 30 will be 10% of duty cycle, right ?
    Now how this works when i write this period value (copied from counter value) in Address. How should i generate 4kHz square wave with 50% duty cycle, as i want to see buzzer giving its highest spec.  
    And DIO pin 21 is always high and driving buzzer ?

    3) if i dont put sleep code, then buzzer does not work. why so ?

    Please let me know answers for these .

    Regards

    Vinay

     

  • Hi,

    I am not sure if I am following the questions you posted here :

    1.  If you want to driver buzzer, then you have to assign the pin, which physically connected to the buzzer, to output the PWM signal. Not sure about why you ask why...

    2. Here are the parameters you should use to set up the square wave, following shows how to do it for 50% duty cycle at all time

       
      pwmParams.periodUnit  = PWM_PERIOD_HZ;
      pwmParams.periodValue = 5000;
      pwmParams.dutyUnit    = PWM_DUTY_FRACTION; 
      pwmParams.dutyValue   = PWM_DUTY_FRACTION_MAX/2;

    I did not see the part of the code you posted in Svend's files. He was using period_us/100+period_us, which is exactly as he stated in the comment(change duty cycle with 1% of the original period)

        /* Change duty cycle with 1% of period */
        if(direction)
        {
          dutyValue += PERIOD_US / 100; 
        }
        else {
         dutyValue -= PERIOD_US / 100; 
        }

    3. It should still work, but the change might be too quick for you to notice.

  • Looks like the code i am using is different one :
    Can you please have look at below code , for clarity on my questions.

    Can you please let me know how the GPIO , DIO-21 is written high to drive buzzer , may be some code i have to refer ? or is there hardware module which is driving this pin high.

    And i want to drive buzzer with 4Khz square wave with 50% duty cycle , can use this below function to do that ?


    void taskFxn(UArg a0, UArg a1) {


    int baseAddr = 0x40010000;

    int offset = 0;


    // PWM_Params pwmParams;

    // PWM_Params_init(&pwmParams);

    // pwmParams.idleLevel = PWM_IDLE_LOW;

    //

    // /* PWM in microseconds with period in microseconds */

    //

    uint32_t period = COUNTER_MAX;


    // pwmParams.periodUnit = PWM_PERIOD_COUNTS;

    // pwmParams.periodValue = PERIOD_US * TIME_FACTOR;

    // pwmParams.dutyUnit = PWM_DUTY_COUNTS;

    // pwmParams.dutyValue = dutyValue * TIME_FACTOR;


    // /* PWM open should will set to pin to idle level */

    // hPWM = PWM_open(CC2650_PWM0, &pwmParams);

    //

    // if(hPWM == NULL) {

    // Log_error0("Opening PWM failed");

    // while(1);

    // }


    int powerMngrId = 0;


    Power_setDependency(powerMngrId);


    int GPT_MODE_PWM_down = GPT_TAMR_TAMR_PERIODIC | GPT_TAMR_TACDIR_DOWN |

    GPT_TAMR_TAAMS_PWM | GPT_TAMR_TACM_EDGCNT |

    GPT_TAMR_TAPLO_CCP_ON_TO | GPT_TAMR_TAPWMIE_EN |

    GPT_TAMR_TAMRSU_CYCLEUPDATE;


    HWREG(baseAddr + offset + GPT_O_CFG) = GPT_CFG_CFG_16BIT_TIMER;

    HWREG(baseAddr + offset + GPT_O_TAMR) = GPT_MODE_PWM_down;


    TimerStallControl(baseAddr, 0x000000FF, GPTimerCC26XX_DEBUG_STALL_OFF);


    PIN_Handle hPins = NULL;

    PIN_State pinState;


    int pinConfig = PIN_TERMINATE;

    hPins = PIN_open(&pinState, &pinConfig);


    uint32_t idleLevel = PIN_GPIO_HIGH;


    /* Generate pin config for PWM pin.

    * Output is inverted to make PWM duty calculations independent of period

    */

    int pwmPin = Board_BUZZER;

    pinConfig = pwmPin | PIN_INPUT_DIS | PIN_GPIO_OUTPUT_EN | idleLevel |

    PIN_INV_INOUT | PIN_PUSHPULL | PIN_DRVSTR_MAX;


    /* Fail if cannot add pin */

    if (PIN_add(hPins, pinConfig) != PIN_SUCCESS)

    {

    //Log_error2("PWM_open(%x): PIN (%d) already in use.", handle, hwAttrs->pwmPin);

    return;

    }


    // PWM_start(hPWM);


    GPTimerCC26XX_PinMux pinMux = GPT_PIN_0A;

    PINCC26XX_setMux(hPins, pwmPin, pinMux);


    HWREG(baseAddr + offset + GPT_O_TAILR) = period;

    HWREG(baseAddr + offset + GPT_O_TAMATCHR) = 20 * TIME_FACTOR;


    /* Enable timer */

    uint32_t ui32Timer = 255;

    HWREG(baseAddr + GPT_O_CTL) |= ui32Timer & (GPT_CTL_TAEN | GPT_CTL_TBEN);

    Power_setConstraint(PowerCC26XX_SB_DISALLOW);



    bool direction = 1; /* Initially increase duty */


    int counter = COUNTER_MIN;


    while (1) {


    /* Sleep */

    //Task_sleep(40 * PERIOD_US / Clock_tickPeriod);


    int c;

    for (c = 0; c < 100000; c++);


    /* Change duty cycle with 1% of period */

    if(direction)

    {

    counter += 30;

    }

    else {

    counter -= 30;

    }


    if(counter >= COUNTER_MAX)

    {

    counter = COUNTER_MAX;

    direction = 0;

    }

    else if(counter <= COUNTER_MIN) {

    counter = COUNTER_MIN;

    direction = 1;

    }


    period = counter;


    //PWM_setPeriod(hPWM, period);


    HWREG(baseAddr + offset + GPT_O_TAILR) = period;

    //HWREG(baseAddr + offset + GPT_O_TAILR) = 100000;


    }


    }
  • Hi,

    Have you consider to use the driver Svend made? It's recommended to use driver instead of low level register access. The code you have is a mixed with driverlib function and directly register access... It's a bit difficult to debug for you. You will have to look at the bitwise document if you want to do low level register access.

    If you use driver made from Svend, all you need is the following configuration, and the the driver will set it up for you.(Also you need to assign pins as PWM output which is stated in the original post)

      pwmParams.periodUnit  = PWM_PERIOD_HZ;
      pwmParams.periodValue = 4000;
      pwmParams.dutyUnit    = PWM_DUTY_FRACTION; 
      pwmParams.dutyValue   = PWM_DUTY_FRACTION_MAX/2;

    Give it a try and I will help you if there is anything you are not clear while using the driver

  • Hi Christian,

          Thanks a lot for reply.

    I created new project with emptyproject from TI RTOS example.

    and then copy pasted code of empty.c as below :


    /*
    * Example program showing how to output a PWM signal and sweep duty cycle
    * in 1% steps from 0us to 100us. When reaching 100us duty (100%) the duty
    * cycle will go towards 0us again.
    */
    #include <ti/sysbios/BIOS.h>
    #include <ti/sysbios/knl/Clock.h>
    #include <ti/sysbios/knl/Task.h>
    #include <xdc/runtime/Log.h>

    #include <ti/drivers/PIN.h>
    #include <ti/drivers/PWM2.h>

    #include "Board.h"

    // Task data
    Task_Struct pwmTask;
    Char pwmTaskStack[512];

    PWM_Handle hPWM;

    #define PERIOD_US 1000

    void taskFxn(UArg a0, UArg a1) {

    PWM_Params pwmParams;
    PWM_Params_init(&pwmParams);
    pwmParams.idleLevel = PWM_IDLE_LOW;

    /* PWM in microseconds with period in microseconds */

    uint32_t dutyValue = 0; /* 0us (0%) initial duty cycle */

    pwmParams.periodUnit = PWM_PERIOD_US;
    pwmParams.periodValue = PERIOD_US;
    pwmParams.dutyUnit = PWM_DUTY_US;
    pwmParams.dutyValue = dutyValue;

    /* PWM open should will set to pin to idle level */
    hPWM = PWM_open(CC2650_PWM0, &pwmParams);

    if(hPWM == NULL) {
    Log_error0("Opening PWM failed");
    while(1);
    }

    PWM_start(hPWM);

    bool direction = 1; /* Initially increase duty */

    while (1) {

    /* Sleep 10 PWM periods */
    Task_sleep(10 * PERIOD_US / Clock_tickPeriod);
    /* Change duty cycle with 1% of period */
    if(direction)
    {
    dutyValue += PERIOD_US / 100;
    }
    else {
    dutyValue -= PERIOD_US / 100;
    }

    if(dutyValue >= PERIOD_US)
    {
    dutyValue = PERIOD_US;
    direction = 0;
    }
    else if(dutyValue <= 0) {
    dutyValue = 0;
    direction = 1;
    }
    PWM_setDuty(hPWM, dutyValue);

    }

    }


    PIN_Config pinConfig[] = { PIN_ID(Board_PWMPIN0) | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX ,
    PIN_TERMINATE };

    int main(void) {

    PIN_init(pinConfig);

    Task_Params taskParams;

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

    /* Start TI-RTOS */
    BIOS_start();

    return 0;

    }

    Modified empty.cfg , to disable heartbeat_fxn . then i pulled new ti driver which was downloaded from your driver code and added/pulled the directory into project and then started build.

    Here are the below errors which i am finding it difficult to search and resolve.


    **** Build of configuration Debug for project PWM_buzzer ****

    "C:\\ti\\ccsv6\\utils\\bin\\gmake" -k all
    making ../src/sysbios/sysbios.aem3 ...
    gmake[1]: Entering directory `F:/TI_ST/PWM_buzzer/src/sysbios'
    Preprocessing library source files ...
    Library build complete.
    gmake[1]: Leaving directory `F:/TI_ST/PWM_buzzer/src/sysbios'
    'Building file: ../Board.c'
    'Invoking: ARM Compiler'
    "C:/ti/ccsv6/tools/compiler/ti-cgt-arm_5.2.5/bin/armcl" -mv7M3 --code_state=16 --abi=eabi -me --include_path="C:/ti/tirtos_simplelink_2_13_00_06/products/cc26xxware_2_21_01_15600" --include_path="F:/TI_ST/PWM_buzzer" --include_path="C:/ti/ccsv6/tools/compiler/ti-cgt-arm_5.2.5/include" -g --gcc --define=DEBUG --diag_wrap=off --diag_warning=225 --diag_warning=255 --display_error_number --gen_func_subsections=on --preproc_with_compile --preproc_dependency="Board.pp" --cmd_file="configPkg/compiler.opt" "../Board.c"
    "../Board.c", line 67: warning #1-D: last line of file ends without a newline
    'Finished building: ../Board.c'
    ' '
    'Building file: ../empty.c'
    'Invoking: ARM Compiler'
    "C:/ti/ccsv6/tools/compiler/ti-cgt-arm_5.2.5/bin/armcl" -mv7M3 --code_state=16 --abi=eabi -me --include_path="C:/ti/tirtos_simplelink_2_13_00_06/products/cc26xxware_2_21_01_15600" --include_path="F:/TI_ST/PWM_buzzer" --include_path="C:/ti/ccsv6/tools/compiler/ti-cgt-arm_5.2.5/include" -g --gcc --define=DEBUG --diag_wrap=off --diag_warning=225 --diag_warning=255 --display_error_number --gen_func_subsections=on --preproc_with_compile --preproc_dependency="empty.pp" --cmd_file="configPkg/compiler.opt" "../empty.c"
    'Finished building: ../empty.c'
    ' '
    'Building file: ../ti/drivers/pwm/PWMCC26XX.c'
    'Invoking: ARM Compiler'
    "C:/ti/ccsv6/tools/compiler/ti-cgt-arm_5.2.5/bin/armcl" -mv7M3 --code_state=16 --abi=eabi -me --include_path="C:/ti/tirtos_simplelink_2_13_00_06/products/cc26xxware_2_21_01_15600" --include_path="F:/TI_ST/PWM_buzzer" --include_path="C:/ti/ccsv6/tools/compiler/ti-cgt-arm_5.2.5/include" -g --gcc --define=DEBUG --diag_wrap=off --diag_warning=225 --diag_warning=255 --display_error_number --gen_func_subsections=on --preproc_with_compile --preproc_dependency="ti/drivers/pwm/PWMCC26XX.pp" --obj_directory="ti/drivers/pwm" --cmd_file="configPkg/compiler.opt" "../ti/drivers/pwm/PWMCC26XX.c"
    "../ti/drivers/pwm/PWMCC26XX.c", line 137: warning #169-D: argument of type "PWM_Handle" is incompatible with parameter of type "xdc_IArg"
    "../ti/drivers/pwm/PWMCC26XX.c", line 146: warning #169-D: argument of type "PWM_Handle" is incompatible with parameter of type "xdc_IArg"
    "../ti/drivers/pwm/PWMCC26XX.c", line 162: warning #169-D: argument of type "PWM_Handle" is incompatible with parameter of type "xdc_IArg"
    "../ti/drivers/pwm/PWMCC26XX.c", line 193: warning #169-D: argument of type "PWM_Handle" is incompatible with parameter of type "xdc_IArg"
    "../ti/drivers/pwm/PWMCC26XX.c", line 212: warning #169-D: argument of type "PWM_Handle" is incompatible with parameter of type "xdc_IArg"
    "../ti/drivers/pwm/PWMCC26XX.c", line 225: warning #169-D: argument of type "PWM_Handle" is incompatible with parameter of type "xdc_IArg"
    "../ti/drivers/pwm/PWMCC26XX.c", line 230: warning #169-D: argument of type "PWM_Handle" is incompatible with parameter of type "xdc_IArg"
    "../ti/drivers/pwm/PWMCC26XX.c", line 248: error #20: identifier "period" is undefined
    "../ti/drivers/pwm/PWMCC26XX.c", line 248: warning #169-D: argument of type "PWM_Handle" is incompatible with parameter of type "xdc_IArg"
    "../ti/drivers/pwm/PWMCC26XX.c", line 268: error #20: identifier "period" is undefined
    "../ti/drivers/pwm/PWMCC26XX.c", line 268: warning #169-D: argument of type "PWM_Handle" is incompatible with parameter of type "xdc_IArg"
    "../ti/drivers/pwm/PWMCC26XX.c", line 278: warning #169-D: argument of type "PWM_Handle" is incompatible with parameter of type "xdc_IArg"
    "../ti/drivers/pwm/PWMCC26XX.c", line 296: error #20: identifier "duty" is undefined
    "../ti/drivers/pwm/PWMCC26XX.c", line 296: warning #169-D: argument of type "PWM_Handle" is incompatible with parameter of type "xdc_IArg"
    "../ti/drivers/pwm/PWMCC26XX.c", line 310: error #20: identifier "duty" is undefined

    "../ti/drivers/pwm/PWMCC26XX.c", line 310: warning #169-D: argument of type "PWM_Handle" is incompatible with parameter of type "xdc_IArg"
    >> Compilation failure
    "../ti/drivers/pwm/PWMCC26XX.c", line 319: warning #169-D: argument of type "PWM_Handle" is incompatible with parameter of type "xdc_IArg"
    4 errors detected in the compilation of "../ti/drivers/pwm/PWMCC26XX.c".
    gmake: *** [ti/drivers/pwm/PWMCC26XX.obj] Error 1
    gmake: Target `all' not remade because of errors.

    **** Build Finished ****

    Do you also have code to add Buzzer in this ?

    I am actually fully lost in this thread as too many codes and suggestions. Can you please clarify.

    Regards

    Vinay

  • I also tried copying the main_pwm_sine.c and tried building, getting same errors as pointed in above thread.
    please help.
  • Just for clarity, attaching my project.
    Please give it try and let me know.

    PWM_buzzer.rar

  • Have you done the following part as stated in the original post?
    1. Add the necessary hardware descriptors, objects and configuration to your board files as shown in Board.c/h.
  • Yes Christin. I added following files in include :
    "${COM_TI_RTSC_TIRTOSSIMPLELINK_INSTALL_DIR}/products/cc26xxware_2_21_01_15600"
    "C:\ti\tirtos_simplelink_2_13_00_06\packages\ti\boards\SensorTag\CC26XXST_0110"
    "C:\ti\tirtos_simplelink_2_13_00_06\packages\ti\boards\SensorTag"
    "C:\ti\tirtos_simplelink_2_13_00_06\products\bios_6_42_00_08\packages"
    "C:\ti\tirtos_simplelink_2_13_00_06\packages"
    "F:\TI_ST\PWM_buzzer"
    "${CG_TOOL_ROOT}/include"

    What are other descriptors i need to add. I can run empty project smoothly and also yesterday's one which had buzzer code with improper duty cycle is running smoothly. Please let me know if i have missed any or any part which may need re-look?.
    I am using CC2650 sensortag . I also was able to run sensortag with BLE smoothly.
  • I Resolved this issue by following the step listed in original post:

    PWMCC26XX.c, you can either comment out the log_error or just change the duty/period to dutyValue/periodValue if you got compile error.

         Actually original post said if we are using RTOS ver 2.15>= , then only make few changes which also includes above step. whereas this step is required even for TI-RTOS 2.13 also. This actually mislead me .

    Now Using this how can i add Buzzer to this ?, Do i need to multiplex this PWM pin with Buzzer pin which DIO-21 ? Is my understanding correct.

    Please let me know.

    Regards
    Vinay

  • You can just map DIO21 as PWM pin in the board file. You can see the example Svend has attached.
  • Thanks Christin.
    Just for completeness, here is change that i made to work:
    In Board.h in the project that i am working , made change of this
    #define Board_PWMPIN0 IOID_21 //7x7 LED1
    which was previously IOID_25.
  • Yes, that's the only thing you need to do.
  • Hi,

    i am using an external Hall sensor to give an INT on the CC2650 with an magnet.

    I receive the interrupts and my question is what is the unit of the returned value from the GPTimerCC26XX_getValue:

    void timerCallback(GPTimerCC26XX_Handle handle, GPTimerCC26XX_IntMask interruptMask)
    {
    timerBuf[timerBufIndex++] = GPTimerCC26XX_getValue(handle);

    if(timerBufIndex == 10)
    {
    //GPTimerCC26XX_stop(handle);
    timerBufIndex=0;
    }
    }

    The Timer is triggert for a positive edge: GPTimerCC26XX_POS_EDGE

    So if i understood it right, the Timer will give an Interrupt for every positive Edge.

    And the time between them would be given in return on the callback function.

    Is this right?

    Is the returned value in seconds, mili seconds or micro seconds?
    Which clock source is it using RTC or 48MHz?
  • Hi,

    Your question has been answer here :
    e2e.ti.com/.../509701
  • Is this now included in the official drivers (version 2.16.01.14)? Are there any basic usage examples? (I want to output an analog voltage from 0-5V).
  • No, it has been included in TI-RTOS official release yet. You can include the driver manually according to the guide(usage) from the original post. The zip file contains 3 basic examples which you can take a look at it.
  • We are using this prerelease pwm driver and its working fine.

    I would like to temporarily stop/deinit/close the pwm driver and open the pwm pins as input for some other purpose, then close that handle and restart/reinit/open the pwm driver again.

    Can someone please post an example of how to do this? Thank you.
  • I use this library implements the PWM output。

    when I  start  1 second and stop 1 second, again and again.

    At the beginning of the work is normal , but,  After a period of time, I can't stop PWM output.

    I don't no , why???