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.

M4 PWM initialization (no driverlib)



I am brand new to the launchpad (LM4F120) and trying to get PWM working.  I am not using any of the functions defined in driverlib, I figured that I could simply follow the datasheet for the MCU and get the PWM with CCP working.  However, that seems to not be the case.  Any help getting my PWM working would be much appreciated.

#include <inc/lm4f120h5qr.h>

int main(void){

    //Enable clock gating for GPIOF
    SYSCTL_RCGC2_R = SYSCTL_RCGC2_GPIOF;

    int pin = 0x02; //0x02 is PF1, Red LED
    //GPIO_PORTF_DIR_R = pin; //Don't care condition according to Table 10-3
    GPIO_PORTF_DEN_R = pin;

    //Set the alternate function of the selected GPIO and set the MUX
    GPIO_PORTF_AFSEL_R = pin;
    GPIO_PORTF_PCTL_R = GPIO_PCTL_PF1_T0CCP1;

    //16/32-bit timer register clock gating control register
    SYSCTL_RCGCTIMER_R = SYSCTL_RCGCTIMER_R0;

    //Disable the timer
    TIMER0_CTL_R = 0x0000;

    //Set Timer 0 as a 16-bit timer
    TIMER0_CFG_R = TIMER_CFG_16_BIT;

    //Configure GPTM Timer Mode
    TIMER0_TAMR_R = TIMER_TAMR_TAAMS | TIMER_TAMR_TAMR_PERIOD;

    //Load the timer start value, the timer counts down
    TIMER0_TAILR_R = 0xFFFF;

    //Load the match value
    TIMER0_TAMATCHR_R = 0x7FFF;

    //Enable the timer
    TIMER0_CTL_R = TIMER_CTL_TAEN;

    //Loop forever
    while(1){

    }
}

  • ryan mayer said:
    I am not using any of the functions defined in driverlib

    The driverlib was created - and maintained at great cost/effort by first LMI and now TI.  Driverlib both shields and protects you from having to gain, "complete understanding" of the many facets and interactions which occur within these complex, ARM MCUs.

    Suspect that you'd progress far faster by revisiting your, "Non driverlib" decision!"   Drivelib enables you to quickly/easily exploit 100's of examples w/in StellarisWare - gives you a known base of well documented success - and provides a great framework should you later seek to, "roll your own" non-driverlib implementations...

  • cb1 make heap good sense. Preserve your foot for later usage.

  • Thanks.  I tend to want to understand what's going on under the hood at times but I do agree, driverlib was developed by professionals and it is good to go.  I will scrap my approach and give driverlib a try.

  • Hi,

    maybe this helps to get you started. Found this on the internet; so don't blame me since it's not my code (http://www.multiwii.com/forum/viewtopic.php?f=22&t=2376&start=30)!

    aBUGSworstnightmare

    #include "inc/hw_types.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_timer.h"

    #include "driverlib/sysctl.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/gpio.h"
    #include "driverlib/timer.h"

    #include <stdint.h>

    #define tics_us                       SysCtlClockGet() / 1000000
    #define default_pulse                  0 //µs


    void config_timer(uint8_t timer, uint16_t refresh_rate)
    {
       // set refresh rate
       uint32_t period1 = SysCtlClockGet() / refresh_rate; /*Hz*/
       uint8_t extender1 = period1 >> 16;
       period1 &= 0xFFFF;

       //set default
       uint32_t period2 = tics_us*default_pulse;
       uint8_t extender2 = period2 >> 16;
       period2 &= 0xFFFF;

       switch (timer){

       case 0: {// Configure output, PB6/7 as T0CCP0/1
          SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
          GPIOPinConfigure(GPIO_PB6_T0CCP0);
          GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_6);
          GPIOPinConfigure(GPIO_PB7_T0CCP1);
          GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_7);
          // Configure timer
          SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
          TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR|TIMER_CFG_A_PWM|TIMER_CFG_B_PWM);
          HWREG(TIMER0_BASE + TIMER_O_CTL) |= (TIMER_CTL_TAPWML|TIMER_CTL_TBPWML);
          //set period
          TimerPrescaleSet(TIMER0_BASE, TIMER_A, extender1);
          TimerLoadSet(TIMER0_BASE, TIMER_A, period1);
          TimerPrescaleSet(TIMER0_BASE, TIMER_B, extender1);
          TimerLoadSet(TIMER0_BASE, TIMER_B, period1);
          //set default value A
          TimerPrescaleMatchSet(TIMER0_BASE, TIMER_A, extender2);
          TimerMatchSet(TIMER0_BASE, TIMER_A, period2);
          //set default value B
          TimerPrescaleMatchSet(TIMER0_BASE, TIMER_B, extender2);
          TimerMatchSet(TIMER0_BASE, TIMER_B, period2);
          TimerEnable(TIMER0_BASE, TIMER_A|TIMER_B);
          break;}

       case 1: {// Configure output, PB4/5 as T1CCP0/1
          SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
          GPIOPinConfigure(GPIO_PB4_T1CCP0);
          GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_4);
          GPIOPinConfigure(GPIO_PB5_T1CCP1);
          GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_5);
          // Configure timer
          SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
          TimerConfigure(TIMER1_BASE, TIMER_CFG_SPLIT_PAIR|TIMER_CFG_A_PWM|TIMER_CFG_B_PWM);
          HWREG(TIMER1_BASE + TIMER_O_CTL) |= (TIMER_CTL_TAPWML|TIMER_CTL_TBPWML);
          //set period
          TimerPrescaleSet(TIMER1_BASE, TIMER_A, extender1);
          TimerLoadSet(TIMER1_BASE, TIMER_A, period1);
          TimerPrescaleSet(TIMER1_BASE, TIMER_B, extender1);
          TimerLoadSet(TIMER1_BASE, TIMER_B, period1);
          //set default value A
          TimerPrescaleMatchSet(TIMER1_BASE, TIMER_A, extender2);
          TimerMatchSet(TIMER1_BASE, TIMER_A, period2);
          //set default value B
          TimerPrescaleMatchSet(TIMER1_BASE, TIMER_B, extender2);
          TimerMatchSet(TIMER1_BASE, TIMER_B, period2);
          TimerEnable(TIMER1_BASE, TIMER_A|TIMER_B);
          break;}

       case 2: {// Configure output, PB0/1 as T2CCP0/1
          SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
          GPIOPinConfigure(GPIO_PB0_T2CCP0);
          GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_0);
          GPIOPinConfigure(GPIO_PB1_T2CCP1);
          GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_1);
          // Configure timer
          SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER2);
          TimerConfigure(TIMER2_BASE, TIMER_CFG_SPLIT_PAIR|TIMER_CFG_A_PWM|TIMER_CFG_B_PWM);
          HWREG(TIMER2_BASE + TIMER_O_CTL) |= (TIMER_CTL_TAPWML|TIMER_CTL_TBPWML);
          //set period
          TimerPrescaleSet(TIMER2_BASE, TIMER_A, extender1);
          TimerLoadSet(TIMER2_BASE, TIMER_A, period1);
          TimerPrescaleSet(TIMER2_BASE, TIMER_B, extender1);
          TimerLoadSet(TIMER2_BASE, TIMER_B, period1);
          //set default value A
          TimerPrescaleMatchSet(TIMER2_BASE, TIMER_A, extender2);
          TimerMatchSet(TIMER2_BASE, TIMER_A, period2);
          //set default value B
          TimerPrescaleMatchSet(TIMER2_BASE, TIMER_B, extender2);
          TimerMatchSet(TIMER2_BASE, TIMER_B, period2);
          TimerEnable(TIMER2_BASE, TIMER_A|TIMER_B);
          break;}

       case 3: {// Configure output, PB2/3 as T3CCP0/1
          SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
          GPIOPinConfigure(GPIO_PB2_T3CCP0);
          GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_2);
          GPIOPinConfigure(GPIO_PB3_T3CCP1);
          GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_3);
          // Configure timer
          SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER3);
          TimerConfigure(TIMER3_BASE, TIMER_CFG_SPLIT_PAIR|TIMER_CFG_A_PWM|TIMER_CFG_B_PWM);
          HWREG(TIMER3_BASE + TIMER_O_CTL) |= (TIMER_CTL_TAPWML|TIMER_CTL_TBPWML);
          //set period
          TimerPrescaleSet(TIMER3_BASE, TIMER_A, extender1);
          TimerLoadSet(TIMER3_BASE, TIMER_A, period1);
          TimerPrescaleSet(TIMER3_BASE, TIMER_B, extender1);
          TimerLoadSet(TIMER3_BASE, TIMER_B, period1);
          //set default value A
          TimerPrescaleMatchSet(TIMER3_BASE, TIMER_A, extender2);
          TimerMatchSet(TIMER3_BASE, TIMER_A, period2);
          //set default value B
          TimerPrescaleMatchSet(TIMER3_BASE, TIMER_B, extender2);
          TimerMatchSet(TIMER3_BASE, TIMER_B, period2);
          TimerEnable(TIMER3_BASE, TIMER_A|TIMER_B);
          break;}
       default: {break;}
       }
    }

    void set_pwm (uint8_t chan, uint16_t pulse)    //sets 24-bit pwm values to timer0A - timer3B registers -> channels 0-7
    {
       //calc and set pwm timer value, no deadtime interlock!
       uint32_t pwm_period = tics_us*pulse;
       uint8_t pwm_extender = pwm_period >> 16;
       pwm_period &= 0xFFFF;
       switch (chan) {
       case 0: {
          TimerPrescaleMatchSet(TIMER0_BASE, TIMER_A, pwm_extender);
          TimerMatchSet(TIMER0_BASE, TIMER_A, pwm_period);
          break;   }
       case 1: {
          TimerPrescaleMatchSet(TIMER0_BASE, TIMER_B, pwm_extender);
          TimerMatchSet(TIMER0_BASE, TIMER_B, pwm_period);
          break;   }
       case 2: {
          TimerPrescaleMatchSet(TIMER1_BASE, TIMER_A, pwm_extender);
          TimerMatchSet(TIMER1_BASE, TIMER_A, pwm_period);
          break;   }
       case 3: {
          TimerPrescaleMatchSet(TIMER1_BASE, TIMER_B, pwm_extender);
          TimerMatchSet(TIMER1_BASE, TIMER_B, pwm_period);
          break;   }
       case 4: {
          TimerPrescaleMatchSet(TIMER2_BASE, TIMER_A, pwm_extender);
          TimerMatchSet(TIMER2_BASE, TIMER_A, pwm_period);
          break;   }
       case 5: {
          TimerPrescaleMatchSet(TIMER2_BASE, TIMER_B, pwm_extender);
          TimerMatchSet(TIMER2_BASE, TIMER_B, pwm_period);
          break;   }
       case 6: {
          TimerPrescaleMatchSet(TIMER3_BASE, TIMER_A, pwm_extender);
          TimerMatchSet(TIMER3_BASE, TIMER_A, pwm_period);
          break;   }
       case 7: {
          TimerPrescaleMatchSet(TIMER3_BASE, TIMER_B, pwm_extender);
          TimerMatchSet(TIMER3_BASE, TIMER_B, pwm_period);
          break;   }
       default: { break; }
       }
    }

    int main(void)
    {
       uint16_t pulse;

       // Configure system clock
       SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN); //80MHZ

       // init timer(number, frequency)
       config_timer(0,100);
       config_timer(1,200);
       config_timer(2,300);
       config_timer(3,400);

       //generate sweeps [1000-1500µs]-[1700-2200µs]
       while(1)
       {
          set_pwm(0,pulse);
          set_pwm(1,(pulse+100));
          set_pwm(2,(pulse+200));
          set_pwm(3,(pulse+300));
          set_pwm(4,(pulse+400));
          set_pwm(5,(pulse+500));
          set_pwm(6,(pulse+600));
          set_pwm(7,(pulse+700));

          if (pulse<1500) pulse++; else pulse=1000;
          SysCtlDelay(50000);
       }

    }
  • ryan mayer said:
    I tend to want to understand what's going on under the hood

    Glad that you're agreeable - know that your employer will appreciate the efficiency which the driverlib brings...

    There's nothing wrong with wanting a greater understanding of the, "nuts & bolts."  However that may be best mastered by careful review of the pertinent functions w/in StellarisWare.  You can drill down as far as you like - you'll see "asserts" and other safety mechanisms - all designed to shield you from the "horrors" which a single, misplaced bit may cause!  (do not ask - how I know)

    Once you have one section/piece of your code test/verified under StellarisWare - you can then examine the various registers - and by review of the specific Stellaris functions - see how the numerous constructs & interactions are properly managed.  Its unlikely a novice user would put in the time to exhaustively read (and understand) the 1400+ pg datasheets - StellarisWare greatly shields the user from such trial/tribulations...

  • This is all a hobby for me.  I am active duty Navy so I do not get the time that I really want to work on my MCU projects.  My other projects rely on AVR MCUs and I was craving more horsepower.  The LM4F120 eval kit seems to be the way to go for a cost effective solution.  I will take your advice and focus on learning driverlib and get familiar with it before I dive down deeper.  I am not afraid to get into a datasheet, in fact I prefer it because I get a great deal of understanding from them that helps with development.  I am looking forward to Stellarisware.  Thanks again.

  • I figured out what I was doing wrong, it's kind of embarassing.  If you notice I am trying to use T0CPP1 to control PF1 GPIO.  However, when I am configuring the GPTM modes and registers, I am using Timer A rather than the correct Timer B.  Changing the approproate A's to B's works like a charm.  I hope this helps out anyone else who is just playing around with their launchpad.