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.

CC2650 PWM runnig in background

Other Parts Discussed in Thread: CC2650, CC2650STK

Hi,

is there an example showing the PWM on CC2650 running in the background without the CPU?

Is there any  CC2650 RTOS PWM drivers available?

Thanks,

Michael

  • Hi Chen,

    i looked up those files and main.c examples but does the PWM run in the background or the CPU has to handle the timers interrupt?

    Thanks,

    Michael
  • PWM should run in the background.
  • Hi Chen,

    i was able to get the drivers and board files on an empty project for the sensortag.
    I compile it and flash it with no problem.

    But i dont see any PWM signal at the PINS:

    #define Board_PWMPIN0 IOID_25 //7x7 LED1
    #define Board_PWMPIN1 IOID_27 //7x7 LED2

    When i look up inside the main.c example i dont see any PIN configuration for the board:

    PWM_Params pwmParams;
    PWM_Params_init(&pwmParams);

    pwmParams.periodUnit = PWM_PERIOD_HZ;
    pwmParams.periodValue = 100e3;
    pwmParams.dutyUnit = PWM_DUTY_FRACTION; // Fractional duty cycle
    pwmParams.dutyValue = PWM_DUTY_FRACTION_MAX/2; //50% duty
    pwmParams.idleLevel = PWM_IDLE_LOW;

    hPWM = PWM_open(CC2650_PWM0, &pwmParams); <-------------------------------------------------Is it Here the CC2650_PWM0 the PWM PIN ?
    PWM_start(hPWM);

    When i look inside the board.h i see the CC2650_PWM0 configured as:
    typedef enum CC2650_PWM {
    CC2650_PWM0 = 0,
    CC2650_PWM1,
  • I got it to work,

    had to initiate the PWM Pins in the Board.c files.

    Thanks

    Michael
  • can you post your Board.c file? I'm having trouble with this. It would be nice to see a working example.

  • Hi Bruce,

    see a .rar file attached.

    Regards,

    MichaelPWM.rar

  • Michael,

    Many thanks for posting that. I looked through it and have a question about the CC2650_PWM0 parameter: (as in your original question)

    Here we have the configuration

    // PWM configuration, one per PWM output

    PWMCC26XX_HwAttrs pwmCC26xxHWAttrs[CC2650_PWMCOUNT] = {

        { .pwmPin = Board_PWMPIN0, .gpTimerUnit = CC2650_GPTIMER0A } ,

        { .pwmPin = Board_PWMPIN1, .gpTimerUnit = CC2650_GPTIMER0B } ,

    };

    I'm assuming this is where the PWM Pins are initialized.

    However, in the sample code for main, I don't see any reference to these pins?

    I also don't see any other reference to CC2650_PWM0 so its not clear how this maps to Board_PWMPIN0 if that is what is supposed to be happening.

    void SimpleBLEPeripheralPWM_init(void)

    {

    PWM_Params pwmParams;

    // Initialize the PWM driver.

    PWM_init();

    PWM_Params_init(&pwmParams);

    pwmParams.idleLevel = PWM_IDLE_LOW; // Output low when PWM is not running

    /* PWM in US with fractional duty cycle */

    pwmParams.periodUnit = PWM_PERIOD_US;

    pwmParams.periodValue = PERIOD_US;

    pwmParams.dutyUnit = PWM_DUTY_FRACTION;

    pwmParams.dutyValue = PWM_DUTY_FRACTION_MAX/2;

    //handle = PWM_open(IOID_6, &params);

    PWM_Handle hPWM0 = PWM_open(CC2650_PWM0, &pwmParams);

    if(hPWM0 == NULL) {

    while(1);

    }

    PWM_start(hPWM0);

    }

    this is the example that I've seen in reference to this problem. However, I can't find the line of code that actually references the output pin.

    There is this line:

    PWM_Handle hPWM0 = PWM_open(CC2650_PWM0, &pwmParams);

    However, CC2650_PWM0 is initialized , but never referenced in any of the files.

    am I to assume that CC2650_PWM0 is simply supposed to be the first initialized PWM pin in the initialization list?

    i.e does CC2650_PWM0 actually refer to 

        { .pwmPin = Board_PWMPIN0, .gpTimerUnit = CC2650_GPTIMER0A } ,

    which is the first line in the PWMCC26XX_HwAttrs structure

    // PWM configuration, one per PWM output

    PWMCC26XX_HwAttrs pwmCC26xxHWAttrs[CC2650_PWMCOUNT] = {

        { .pwmPin = Board_PWMPIN0, .gpTimerUnit = CC2650_GPTIMER0A } ,

        { .pwmPin = Board_PWMPIN1, .gpTimerUnit = CC2650_GPTIMER0B } ,

    };

    also, if I want to vary the period or duty while its running, where do I do that?

    shouldn't there be a callback for that? I can't see where that would be set up in this example.

  • Hi Bruce,

    in my project i didt the same but i open the PIN i want for the PWM.

    First you have to declare  "Board_PWMPIN1" in your Board.h file which GPIO it should be used:

    #define Board_PWMPIN0  				IOID_24

    On Board.c you add it to the "BoardGpioInitTable[]":

    Board_PWMPIN0 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL ,

    In main it will initiate the "BoardGpioInitTable":

    PIN_init(BoardGpioInitTable);
    
    
    By initialising your sensortag task or init function you add this:

    hGpioPin = PIN_open(&pinGpioState, SensortagAppPinTable);
    PWM_Params pwmParams;
    PWM_Params_init(&pwmParams);
    pwmParams.periodUnit = PWM_PERIOD_HZ;
    pwmParams.periodValue = 614400;//100e3; <----------Here is the frequency you want
    pwmParams.dutyUnit = PWM_DUTY_FRACTION; // Fractional duty cycle
    pwmParams.dutyValue = PWM_DUTY_FRACTION_MAX/2; //50% duty
    pwmParams.idleLevel = PWM_IDLE_LOW;
    hPWM = PWM_open(CC2650_PWM0, &pwmParams);
    if(hPWM == NULL ) { }
    else PWM _start(hPWM);
    If you want to change the Dulty Cycle or anything on the PWM you will have to do this on a semaphore with an event on your task function.

    Something like this:

     if (events & PWM_Dulty_Change_EVT){
    PWM_stop(hPWM);
    PWM_Params pwmParams;
    PWM_Params_init(&pwmParams);
    pwmParams.periodUnit = PWM_PERIOD_HZ;
    pwmParams.periodValue = 614400;//100e3;
    pwmParams.dutyUnit = PWM_DUTY_FRACTION; // Fractional duty cycle
    pwmParams.dutyValue = PWM_DUTY_FRACTION_MAX/2; //50% duty<-------------------------Change Dulty
    pwmParams.idleLevel = PWM_IDLE_LOW;
    hPWM = PWM_open(CC2650_PWM0, &pwmParams);
    if(hPWM == NULL ) {}e
    else
    PWM_start(hPWM);}

     Use it as reference i dont know if it will work. But any change you do, use a smeaphore to wake up the task function and set an event to change your dulty.

    Regards,

    Michael

    
    

  • Thanks again for your help Michael.

    can you point out in this code where the link is made between Board_PWMPIN0 and CC2650_PMW0 Is that done here:

    // PWM configuration, one per PWM output

    PWMCC26XX_HwAttrs pwmCC26xxHWAttrs[CC2650_PWMCOUNT] = {

    { .pwmPin = Board_SENS_LED1_EN, .gpTimerUnit = CC2650_GPTIMER0A } ,

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

    };

    If this is true then, where is CC2650_GPTIMER0A associated with CC2650_PMW0?

    Or with the value 0 (which is the enumeration value)?

    I need to known how to associate more then one LED

    I am also confused about how the PWM actually works. Does it toggle the PIN for me?

    I've run this example as is and I can't see any change in the LED output.

    In other examples I've seen a function like this

    /* * ======== heartBeatFxn ======== * Toggle the Board_LED0. The Task_sleep is determined by arg0 which * is configured for the heartBeat Task instance. */

    Void heartBeatFxn(UArg arg0, UArg arg1)

    { while (1) { Task_sleep((UInt)arg0);

    PIN_setOutputValue(ledPinHandle, Board_LED1, !PIN_getOutputValue(Board_LED1)); }

    }

    But in this particular example at e2e.ti.com/.../1729748 It does not show how to use the heartbeat function. Is this old code and no longer required with the new PWM driver?

    This example seems to indicate that I have to toggle the LED myself. Yet there is no code in the example that actually calls this function.

  • I guess another possibility is that PIN_init(BoardGpioInitTable); does the association.
    If the only element in the list is Board_PWMPIN0 then would have an index of 0 which would match up with CC2650_PMW0
    Otherwise, I can't see where this association is made.
  • Hi Bruce,

    in the Board.c file you see at the bottom this:

    /*
    * ========================== PWM =========================================
    */
    
    
    // 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, },
    };
    // 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},
    };
    // PWM configuration, one per PWM output
    PWMCC26XX_HwAttrs pwmCC26xxHWAttrs[CC2650_PWMCOUNT] = {
    { .pwmPin = Board_PWMPIN0, .gpTimerUnit = CC2650_GPTIMER0A } , <---------------------------------------Here is where the GPIO gets to the peripheral PWM Timer
    { .pwmPin = Board_PWMPIN1, .gpTimerUnit = CC2650_GPTIMER0B } ,
    };
    // 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] },
    { NULL, NULL, NULL }
    };

    You dont have to do anything except on init() start the PWM with the parameters. If you want to see a LED blink you will have to have a very low frequency like 1 Hz or so.
    If you are using high frequencies your eyes wont notice the change.

    I will recommend you to get a project example like Sensortag or SimpleBLEPeripheral and change/add the Board.c and Board.h according to the PWM.
    Add the PWM files to the project as well.
    On Sensortag.c file where it init all GPIOs relays/leds and so on you add the PWM parameter stuff and start the PWM.
    Once that is up and running you could use an osciloscope or your LED with 1Hz Frequency and 50% dulty on the port you are using to see if it is something there.
    Notice that the LED has to be in the right polrity and make sure you have like 680OHm resistor so that the current limit at 2-4mA.

    Its not complicated, first chane Board Files, add the Stuff into the project and add PWM Files inside "Application" Folder.
    Build and check for errors and when not flash your device. PWM is easy and runs on the background so that the processor doesnt have to deal with toogling the GPIO output.

    Regards,

    Michael
  • Hi Bruce,

    yes PIN_init(BoardGpioInitTable); will do the job.
    Dont worry about the rest. Its same thing on I2C and SPI.
  • I implemented everything as outlined.
    However,
    PWM_Handle hPWM0 = PWM_open(CC2650_PWM0, &pwmParams);

    is returning null. so the PWM won't start.

    when I trace through, it looks like the call to PIN_add is failing.

    what should I do next?
  • Hi Bruce,

    looks like the PIN board files are not right.

    Make sure to use the board.c and board.h that is declaired under project propreties.

    If you use sensortag project for example, this is on the folder c;/ti/simplelink/src/boards/cc2650stk/

    The project link those on the configuration and if you declaire any GPIO to use make sure you write it in those ones.

    Go ahead and add this to where you are trying to open the PWM:

    PIN_Config pwmPin[] =  { Board_PWMPIN1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    PIN_TERMINATE };

    Change "Board_PWMPIN1" here to what you declaired.

    Now if it gives you an error than you didnt write on the correct board files. If no errors than look if you have any warning about pwm or double check board.c PWM part if you wrote the write name. 

  • You can use the RTOS example on CCS. They have a PWM LED example!
    "TI_RTOS for CC13XX and CC26XX - v:2.21.00.06"-> "CC2650DK_7ID"->"Drivers Examples"->"PWM Examples"-> PWM LED
  • When I step through the code, I can see that the failure is happening because my pins look like they are already added.
    However, I have no other code that does an "add" so I have no idea why they would be in this state.

    What I finally tried, which worked, was to comment out the section of code in PIN_add that checked.

    Here is the modified PIN_add call that seems to work:

    PIN_Status PIN_add(PIN_Handle handle, PIN_Config pinCfg) {
    PIN_Status ret;
    PIN_Id pinId = PIN_ID(pinCfg);

    // Check that handle and pinId is valid
    if (!handle || pinId>=PIN_NumPins) {
    return PIN_NO_ACCESS;
    }

    // Ensure that only one client at a time can call PIN_open() or PIN_add()
    Semaphore_pend(Semaphore_handle(&PinSem), BIOS_WAIT_FOREVER);

    // Check whether pin is available
    //if (PIN_HandleTable[pinId]) {
    // Pin already allocated -> do nothing
    // ret = PIN_ALREADY_ALLOCATED;
    //} else {
    // Allocate pin
    PIN_HandleTable[pinId] = handle;
    handle->bmPort |= (1<<pinId);
    PIN_setConfig(handle, PIN_BM_ALL, pinCfg);
    ret = PIN_SUCCESS;
    //}

    Semaphore_post(Semaphore_handle(&PinSem));
    return ret;
    }
  • Hi Michael,

    Do you have any idea how to do ADC sampling with PWM?
    Here is my adc sample code. Unfortunately it hangs on the idle semaphore - BIOS_WAIT_FOREVER

    void adcSampleAllChannels(void)
    {
    uint8_t bChannel;
    uint8_t bChannelValid = 1;

    for (bChannel = 0; bChannel < SALU_ADC_CHAN_NUM_TOTAL; bChannel++)
    {
    // Connect AUX IO4 (DIO26) as analog input (battery voltage)
    // AUX IO7 corresponds to DIO23, AUX IO0 corresponds to DIO30
    switch (bChannel)
    {
    case SALU_ADC_CHAN_BATTERY: { AUXADCSelectInput(ADC_COMPB_IN_AUXIO4); break; } // Battery Voltage
    case SALU_ADC_CHAN_PHOTO: { AUXADCSelectInput(ADC_COMPB_IN_AUXIO5); break; } // Photo Voltage
    case SALU_ADC_CHAN_ECG_REF1: { AUXADCSelectInput(ADC_COMPB_IN_AUXIO6); break; } // ECG Reference 1 Voltage
    case SALU_ADC_CHAN_ECG_SIG1: { AUXADCSelectInput(ADC_COMPB_IN_AUXIO7); break; } // ECG Signal 1 Voltage
    default: { bChannelValid = 0; break; }
    }

    if (bChannelValid)
    {
    // Trigger ADC sampling
    AUXADCGenManualTrigger();

    // Wait in IDLE until done
    Semaphore_pend(hAdcSem, BIOS_WAIT_FOREVER );

    // When we return from the above line, the ISR will have populated dwAdcSingleSample with the latest ADC value
    dwAdcSingleSampleArray[bChannel] = dwAdcSingleSample; // Get the value from the ISR
    }
    }
    }