Using PWMs in Stellaris Launchpad

The Stellaris launchpad PWMs will be deceiving if you start the design by reading ‘Stellaris® Peripheral Driver Library’ instead of the “LM4F120H5QR” datasheet. You will assume that it has hardware PWMs and you will directly use the PWM APIs available in the driver library. Unfortunately this is not the case with the launchpad. It does not have seperate hardware PWM modules but it has timers which can be configured as PWMs. I will be explaining how to configure the PWM modules available in timer0 andtimer1 to drive the R,G,B LEDs available in the launchpad.

med_ek-lm4f120xl_stellarislaunchpad_tool

First thing you need to understand is the architecture of the Timer modules. These timer modules contain six 16/32 bit timers and six 32/64 bit timers. Each of these twelve timers can be further divided into two independent timers named as TimerA and TimerB. These can be configured to operate separately or can combine.

For example,
a 32 bit Timer0 has 16 bit Timer_A and 16 bit Timer_B,
a 64 bit Timer1 has 32 bit Timer_A and 32 bit Timer_B.
Refer to the Chapter 11 of the datasheet for more information.

Then you need to know what are the pins connected with the Launchpads LEDs and whether we can drive them using the PWM signals. If you refer to the Stellaris Launchpad Pinmaps, you will understand that PF_1, PF_2 and PF_3 will be respectively connected to RED, BLUE and GREEN LEDs.

Also if you refer to Table 11-1, Table 11-2 and Table 10-2 in the datasheet you will understand that
PF_1 can be driven through T0CCP1 (Timer 0 Timer B),
PF_2 can be driven through T1CCP0 (Timer 1 Timer A) and
PF_3 can be driven through T1CCP1 (Timre 1 Timer B).

That is all the hardware information you need to know. After that you need to go through the ‘Stellaris® Peripheral Driver Library’ to understand how to configure the Clocks, Timers and GPIO pins.

Configuring the GPIOs

// Enable clocks for peripherals
// GPIOF > LED
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);

// Configure GPIO
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3,0x00);
GPIOPinConfigure(GPIO_PF1_T0CCP1); // R
GPIOPinConfigure(GPIO_PF2_T1CCP0); // B
GPIOPinConfigure(GPIO_PF3_T1CCP1); // G
GPIOPinTypeTimer(GPIO_PORTF_BASE, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);

Above commands will configure the GPIO_PF1,2 and 3 as Timer CCP (Capture Compare PWM) outputs. First, the peripheral clocks were enabled. Then the initial pin values were set to zero. Finally they were configured as Timer CCP outputs.

Configuring the Timers

// Timer0
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);

// Timer1
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);

// Congiure timer0
ulPeriod = 1000;


// Red LED > PF1 > T0CCP1
TimerConfigure(TIMER0_BASE,TIMER_CFG_SPLIT_PAIR|TIMER_CFG_B_PWM);
ulDutyR = 0;
TimerLoadSet(TIMER0_BASE,TIMER_B,ulPeriod - 1);
TimerMatchSet(TIMER0_BASE,TIMER_B,ulDutyR);
TimerEnable(TIMER0_BASE,TIMER_B);

TimerConfigure(TIMER1_BASE,TIMER_CFG_SPLIT_PAIR|TIMER_CFG_A_PWM|TIMER_CFG_B_PWM);
// Blue LED > PF2 > T1CCP0
ulDutyB = 0;
TimerLoadSet(TIMER1_BASE,TIMER_A,ulPeriod-1);
TimerMatchSet(TIMER1_BASE,TIMER_A,ulDutyB);
TimerEnable(TIMER1_BASE,TIMER_A);

// Green LED > PF3 > T1CCP1
ulDutyG = 0;
TimerLoadSet(TIMER1_BASE,TIMER_B,ulPeriod-1);
TimerMatchSet(TIMER1_BASE,TIMER_B,ulDutyG);
TimerEnable(TIMER1_BASE,TIMER_B);

Using the “TimerConfigure” API the Timer0 module is configured to drive Timer_A and Timer_B independently. (TIMER_CFG_SPLIT_PAIR) Using the “TimerLoadSet” API the initial timer value is configured. Using the “TimerMatchSet” API the PWM duty cycle can be configured. In the Stellaris PWM mechanism, the timer module will start at “ulPeriod – 1” value and count down until “ulDutyX” value. Until that time the PWM output will be high. As soon as the timer reached “ulDutyX” value the PWM output will go low and keep on counting until ‘Zero’ achieved. Then the timer will be reset to the “ulPeriod – 1” value.

In order to make this example working you must add all the include paths, linker paths and pre-processor definitions mentioned in my previous post https://unboxnbeyond.wordpress.com/2013/04/14/using-button-h-for-inbuilt-buttons/.

Following is the total code with comments where necessary.


/*
* PWM example
*/

#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/timer.h"
#include "driverlib/pin_map.h"

#define RED 0
#define BLUE 1
#define GREEN 2

int main(void)
{
unsigned long ulPeriod, ulDutyR, ulDutyG, ulDutyB;
unsigned int uiOn = RED;

// Set the clock to 40 MHz
SysCtlClockSet(SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ | SYSCTL_SYSDIV_5);

// Enable clocks for peripherals
// GPIOF > LED
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);

// Timer0
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);

// Timer1
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);

// Configure GPIO
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3,0x00);
GPIOPinConfigure(GPIO_PF1_T0CCP1); // R
GPIOPinConfigure(GPIO_PF2_T1CCP0); // B
GPIOPinConfigure(GPIO_PF3_T1CCP1); // G
GPIOPinTypeTimer(GPIO_PORTF_BASE, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);

// Congiure timer0
ulPeriod = 1000;

// Red LED > PF1 > T0CCP1
TimerConfigure(TIMER0_BASE,TIMER_CFG_SPLIT_PAIR|TIMER_CFG_B_PWM);
ulDutyR = 0;
TimerLoadSet(TIMER0_BASE,TIMER_B,ulPeriod - 1);
TimerMatchSet(TIMER0_BASE,TIMER_B,ulDutyR);
TimerEnable(TIMER0_BASE,TIMER_B);

TimerConfigure(TIMER1_BASE,TIMER_CFG_SPLIT_PAIR|TIMER_CFG_A_PWM|TIMER_CFG_B_PWM);
// Blue LED > PF2 > T1CCP0
ulDutyB = 0;
TimerLoadSet(TIMER1_BASE,TIMER_A,ulPeriod-1);
TimerMatchSet(TIMER1_BASE,TIMER_A,ulDutyB);
TimerEnable(TIMER1_BASE,TIMER_A);

// Green LED > PF3 > T1CCP1
ulDutyG = 0;
TimerLoadSet(TIMER1_BASE,TIMER_B,ulPeriod-1);
TimerMatchSet(TIMER1_BASE,TIMER_B,ulDutyG);
TimerEnable(TIMER1_BASE,TIMER_B);

while(1)
{
if(uiOn == RED)
{
TimerMatchSet(TIMER0_BASE,TIMER_B,ulDutyR++);
if(ulDutyR == ulPeriod - 1)
{
ulDutyR = 900;
TimerMatchSet(TIMER0_BASE,TIMER_B,ulDutyR);
ulDutyB = 0;
uiOn = BLUE;
}
}else if(uiOn == BLUE)
{
TimerMatchSet(TIMER1_BASE,TIMER_A,ulDutyB++);
if(ulDutyB == ulPeriod - 1)
{
ulDutyB = 900;
TimerMatchSet(TIMER1_BASE,TIMER_A,ulDutyB);
ulDutyG = 0;
uiOn = GREEN;
}
}else if(uiOn == GREEN)
{
TimerMatchSet(TIMER1_BASE,TIMER_B,ulDutyG++);
if(ulDutyG == ulPeriod - 1)
{
ulDutyG = 900;
TimerMatchSet(TIMER1_BASE,TIMER_B,ulDutyG);
ulDutyR= 0;
uiOn = RED;
}
}

SysCtlDelay(10000);
}
}

I hope that you learned something. Thank you for reading.

4 comments

  1. this essay gives me some good advices about lm4f120 and i tried them but i need to ask something. I wanted to make some sine waves but i dont know how can i do it with pwm signals. do i need to use prto b instead of port f?

    1. Hello,

      Can you please elaborate a little bit? You can use PWM to create sine waves, but you will need some additional circuitry too. If you vary the PWM duty cycle in a sinusoidal pattern you can convert the output signal to analogue and get a sine wave.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s