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.

CC2538 Multiple PWM Signals Z-Stack

Other Parts Discussed in Thread: CC2538, CC2530, Z-STACK

Hi All, 

I would like to generate 4 PWM signals with one maybe two timers. I see Nalves from "https://e2e.ti.com/support/wireless_connectivity/f/155/p/454176/1692318#1692318"  has a similar problem that didn't get resolved. 

Im using the CC2538 with ZStack and Smart Energy. Timer A seems to be free for uses not related to the RTOS, possibly timer C too.

I successfully used code from "https://e2e.ti.com/support/wireless_connectivity/f/155/t/279509"  to create one PWM signal, 300KHz, 0.9%DC step. 

Does anyone know how to get four separately controllable signals from one timer? Comments in the ZStack code seem to indicate that it is possible.

I was going to try using a timer and a linked ISR, so that the match-set parameter of the function TimerMatchSet() is updated four times, and four GPIO are manually updated accordingly, all within the ISR. Would this approach work? 

Thanks in advance.

  • I write an example based on SampleLight using 4 PWM to change different level on LED1/LED2/LED3/LED4 of SmartRF06EB. You can refer to PWM_EVT in zcl_sampleLight.c. Remember to define HAL_PWM in your project  toenable PWM capability.

    PWMonSampleLight.zip

  • Thanks for sending the files YiKai,

    I see that the files sent require other Z-Stack Light files such as hal_timer.c When I added it to my ZStack Energy project for CC2538, it has many errors when I compile, and I think this is because hal_timer is written for the CC2530, and so many of the definitions do not match.

    Is there a cc2538 version of hal_timer.c that you know of?
  • Actually, those files are for CC2538. You can download Z-Stack Home 1.2.2a and try it. I just test it and it works fine.
  • Ok thanks YiKai, I was looking in ZStack Light, I didn't think to try ZStack Home. My ZStack Energy now compiles with no errors with hal_timer.c included. I'll experiment with the code and see what happens.
  • You are welcome.
  • Hi YiKai, 

    Thanks for the past help on this PWM issue. We are trying to use it in our project again and have a few issues:

    We were hoping to generate 4 separate PWM signals. The firmware seems to have everything in place for this, except for one: It seems that the function halTimer1SetChannelDuty (found in Z-Stack home & Z-Stack light, and your example code files) has only been written for one PWM channel. Or did I miss something?

    All other posts regarding the use of 4 PWMs and CC2538 seem to have the same issue.

    Was the intention that one edits this function so that it can control 3 more PWMs if desired, by using the other timers and their related setup & control functions?  If this is the case, then my concern is that I interfere with the SoC's timer control.

    Thanks for your time!

  • I don't understand your question well. Can you elaborate?
  • Yes of course: The function ...

    void halTimer1SetChannelDuty (uint8 channel, uint16 promill)
    {
    if(channel == 0)
    {
    uint32 timerAMatch = (PWM_PERIOD * (100-promill)) / 100;

    if(timerAMatch == PWM_PERIOD)
    {
    timerAMatch--;
    }

    //
    // The PWM counter counts down thus calculate actual match count based
    // on duty cycle as: period*(100-DucyCycle)/100
    //
    TimerMatchSet(GPTIMER0_BASE, GPTIMER_A, timerAMatch);
    }
    }

    ... seems to be written for only one PWM channel. If we wanted to add in 3 other PWM channels, would we have to add extra "if" statements? ( if(channel == 1) ... if(channel == 2) ... if(channel == 3) ) ?
  • It looks you have to do this. I don't have CC2538DK to test it right now. You can try it and let me know the result.
  • Ok thanks I'll try it.

    Do I have access to timer-B, timer-C, and timer-D ??

    I'm concerned that I will break the SoC system, by taking control of one of the timers that the SoC system uses.
  • As I know, all timer A, B, C, and D are for general usage and application should be no problem to use them.
  • Thanks YiKai, everything looks good, I have made all four general-purpose timers into separately controllable PWM's, and they work well. 

    The function below shows how I set it up:

    /************************************************************************************
    * @fn pwmTimersInit
    *
    * @brief Configures the 4 general purpose timers for use as 4 separate PWMs
    *
    * @param n/a
    *
    * @return n/a
    **********************************************************************/

    void pwmTimersInit(void)
    {
    //CLOCK SETUP
    SysCtrlClockSet(false, false, SYS_CTRL_SYSDIV_32MHZ); //Set the clocking to run directly from the external crystal/oscillator. (no ext 32k osc, no internal osc)
    SysCtrlIOClockSet(SYS_CTRL_SYSDIV_32MHZ); //Set IO clock to the same as system clock. */
    SysCtrlPeripheralEnable(SYS_CTRL_PERIPH_GPT0); //The Timer peripheral must be enabled for use.


    //TIMER SETUP
    TimerConfigure(GPTIMER0_BASE, GPTIMER_CFG_SPLIT_PAIR | GPTIMER_CFG_A_PWM | GPTIMER_CFG_B_PWM); // Configure GPTimer0A as a 16-bit PWM Timer.
    TimerLoadSet(GPTIMER0_BASE, GPTIMER_A, PWM_PERIOD );
    TimerControlLevel(GPTIMER0_BASE, GPTIMER_A, true);

    TimerConfigure(GPTIMER1_BASE, GPTIMER_CFG_SPLIT_PAIR | GPTIMER_CFG_A_PWM | GPTIMER_CFG_B_PWM); // Configure GPTimer1A as a 16-bit PWM Timer.
    TimerLoadSet(GPTIMER1_BASE, GPTIMER_A, PWM_PERIOD );
    TimerControlLevel(GPTIMER1_BASE, GPTIMER_A, true);

    TimerConfigure(GPTIMER2_BASE, GPTIMER_CFG_SPLIT_PAIR | GPTIMER_CFG_A_PWM | GPTIMER_CFG_B_PWM); // Configure GPTimer2A as a 16-bit PWM Timer.
    TimerLoadSet(GPTIMER2_BASE, GPTIMER_A, PWM_PERIOD );
    TimerControlLevel(GPTIMER2_BASE, GPTIMER_A, true);

    TimerConfigure(GPTIMER3_BASE, GPTIMER_CFG_SPLIT_PAIR | GPTIMER_CFG_A_PWM | GPTIMER_CFG_B_PWM); // Configure GPTimer3A as a 16-bit PWM Timer.
    TimerLoadSet(GPTIMER3_BASE, GPTIMER_A, PWM_PERIOD );
    TimerControlLevel(GPTIMER3_BASE, GPTIMER_A, true);


    //PIN SETUP PB3 ( Done under configIOStartup() )
    IOCPinConfigPeriphOutput( GPIO_B_BASE, GPIO_PIN_3, IOC_MUX_OUT_SEL_GPT0_ICP1 ); // Configure GPIOPortB3 as the Timer0 output
    GPIOPinTypeTimer( GPIO_B_BASE, GPIO_PIN_3 ); // Tell timer to use GPIOPortB3, Direction Selection and PAD Selection
    IOCPadConfigSet( GPIO_B_BASE, GPIO_PIN_3, IOC_OVERRIDE_OE );

    IOCPinConfigPeriphOutput( GPIO_B_BASE, GPIO_PIN_4, IOC_MUX_OUT_SEL_GPT1_ICP1 ); // Configure GPIOPortB4 as the Timer1 output
    GPIOPinTypeTimer( GPIO_B_BASE, GPIO_PIN_4 ); // Tell timer to use GPIOPortB4, Direction Selection and PAD Selection
    IOCPadConfigSet( GPIO_B_BASE, GPIO_PIN_4, IOC_OVERRIDE_OE );

    IOCPinConfigPeriphOutput( GPIO_B_BASE, GPIO_PIN_1, IOC_MUX_OUT_SEL_GPT2_ICP1 ); // Configure GPIOPortB1 as the Timer2 output
    GPIOPinTypeTimer( GPIO_B_BASE, GPIO_PIN_1 ); // Tell timer to use GPIOPortB1, Direction Selection and PAD Selection
    IOCPadConfigSet( GPIO_B_BASE, GPIO_PIN_1, IOC_OVERRIDE_OE );

    IOCPinConfigPeriphOutput( GPIO_B_BASE, GPIO_PIN_2, IOC_MUX_OUT_SEL_GPT3_ICP1 ); // Configure GPIOPortB2 as the Timer3 output
    GPIOPinTypeTimer( GPIO_B_BASE, GPIO_PIN_2 ); // Tell timer to use GPIOPortB2, Direction Selection and PAD Selection
    IOCPadConfigSet( GPIO_B_BASE, GPIO_PIN_2, IOC_OVERRIDE_OE );

    //INTERRUPT SETUP
    IntMasterEnable(); // Enable processor interrupts.

    // Enable GPTIMER0_BASE, GPTIMER1_BASE, GPTIMER2_BASE, GPTIMER3_BASE, by referencing GPTIMER_A each time.
    TimerMatchSet( GPTIMER0_BASE, GPTIMER_A, 0 ); //0%, Default to load-OFF
    TimerEnable(GPTIMER0_BASE, GPTIMER_A);

    TimerMatchSet( GPTIMER1_BASE, GPTIMER_A, 0 ); //0%, Default to load-OFF
    TimerEnable(GPTIMER1_BASE, GPTIMER_A);

    TimerMatchSet( GPTIMER2_BASE, GPTIMER_A, 0 ); //0%, Default to load-OFF
    TimerEnable(GPTIMER2_BASE, GPTIMER_A);

    TimerMatchSet( GPTIMER3_BASE, GPTIMER_A, 0 ); //0%, Default to load-OFF
    TimerEnable(GPTIMER3_BASE, GPTIMER_A);

    }

    Nominal control of the PWM signals are done by using:

    TimerMatchSet( GPTIMERx_BASE, GPTIMER_A, z );   

    ... where x is the timer base 0-3, and z is the desired duty cycle from 0-100.

    and ..

    TimerLoadSet( GPTIMER0_BASE, 0x68 ); 

    ...controls the period. I found that using 0x68 allows a duty cycle step increase of approximately 1%.

    I did not have a chance to measure the frequency with 0x68 set, but it is somewhere between 200 and 600 kHz.

  • Cool and Thanks for sharing this back.