PWM Red LED on Stellaris Launchpad

launchpadstellaristroubleshooting

I am trying to PWM the red LED on a Stellaris LaunchPad, following the recipe in the datasheet p.706, taking a couple shortcuts from the Stellaris® Peripheral Driver Library. I cannot figure out why the LED doesn't glow. I expect a 50% duty cycle @ 5 kHz PWM. Does anybody see what I'm overlooking here?

EDIT:
In the mean while I narrowed the problem. If I include an extra line:

ROM_TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_PWM);

at bullet 2 the program starts working. This is what I mean in my comments that the ROM calls are not very well documented as it is hard to identify which registers it actually writes.

#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/cpu.c"
#include "driverlib/gpio.h"
#include "driverlib/rom.h"
#include "inc/hw_sysctl.h"
#include "driverlib/sysctl.h"
#include "driverlib/timer.c"

#include <stdint.h>

#define LED_RED GPIO_PIN_1

int main() {
    // Set system clock to 80 MHz using PLL and external 16 MHz crystal.
    ROM_SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);

    // Enable GPIO for LED.
    ROM_SysCtlPeripheralEnable( SYSCTL_PERIPH_GPIOF );
    ROM_GPIOPinWrite( GPIO_PORTF_BASE , LED_RED , 0x00 );
    ROM_GPIOPinTypeGPIOOutput( GPIO_PORTF_BASE , LED_RED );

    // Enable timer peripheral.
    ROM_SysCtlPeripheralEnable( SYSCTL_PERIPH_TIMER0 );
    // the GP Timer module clock must be enabled before the registers can be programmed (see page 313 or page 330).
    HWREG( SYSCTL_RCGCTIMER ) |= SYSCTL_RCGCTIMER_R0;
    // There must be a delay of 3 system clocks after the Timer module clock is enabled before any Timer module registers are accessed.
    ROM_SysCtlDelay( 1 );

    // Configure output pin for PWM use.
    ROM_GPIOPinConfigure( GPIO_PF1_T0CCP1 );
    ROM_GPIOPinTypeTimer( GPIO_PORTF_BASE , LED_RED );  //ROM_GPIOPinTypePWM( GPIO_PORTF_BASE , LED_RED ); => No PWM units on board, the timers are used for PWM.

    // LED_RED on PF1 is convenient.
    // PF1: T0CCP1 => 16/32-Bit Timer 0 Capture/Compare/PWM 1. (muxed with: PB7)
    // PWM 1 implies use of Timer B

    // PWM Mode
    // A timer is configured to PWM mode using the following sequence:
    // 1. Ensure the timer is disabled (the TnEN bit is cleared) before making any changes.
    ROM_TimerDisable( TIMER0_BASE , TIMER_B );

    // 2. Write the GPTM Configuration (GPTMCFG) register with a value of 0x0000.0004.
    HWREG( TIMER0_BASE + 0x000 ) = 0x00000004;

    // 3.In the GPTM Timer Mode (GPTMTnMR) register, set the TnAMS bit to 0x1, the TnCMR bit to 0x0, and the TnMR field to 0x2.
    HWREG( TIMER0_BASE + 0x004 ) |= TIMER_TBMR_TBAMS | TIMER_TBMR_TBMR_PERIOD;

    // 4. Configure the output state of the PWM signal (whether or not it is inverted) in the TnPWML field of the GPTM Control (GPTMCTL) register.
    HWREG( TIMER0_BASE + 0x00c ) |= 0;  // TIMER_CTL_TAPWML

    // 5. If a prescaler is to be used, write the prescale value to the GPTM Timer n Prescale Register (GPTMTnPR).
    //HWREG( TIMER0_BASE + 0x038 ) = ...

    // 6. If PWM interrupts are used, configure the interrupt condition in the TnEVENT field in the
    // GPTMCTL register and enable the interrupts by setting the TnPWMIE bit in the GPTMTnMR
    // register. Note that edge detect interrupt behavior is reversed when the PWM output is inverted
    //HWREG( TIMER0_BASE + 0x00c ) |= ...

    // 7. Load the timer start value into the GPTM Timer n Interval Load (GPTMTnILR) register.
    HWREG( TIMER0_BASE + 0x02c ) = 16000 - 1;   // 16000 @ 80MHz system clock makes 5 kHz PWM

    // 8. Load the GPTM Timer n Match (GPTMTnMATCHR) register with the match value.
    HWREG( TIMER0_BASE + 0x034 ) = 8000 - 1;    // Defines duty cycle

    // 9. Set the TnEN bit in the GPTM Control (GPTMCTL) register to enable the timer and begin generation of the output PWM signal.
    HWREG( TIMER0_BASE + 0x00c ) |= TIMER_CTL_TBEN; 

    // In PWM Timing mode, the timer continues running after the PWM signal has been generated. The
    // PWM period can be adjusted at any time by writing the GPTMTnILR register, and the change takes
    // effect at the next cycle after the write.

    while ( 1 ) {
    }
}

Best Answer

I was writing to GPTMTAMR where I should be writing to GPTMTBMR (bullet #3 in the comments). In other words I was writing a value to a timerA register where I should be using timerB. Added the final code as a simple proof of concept for PWM the red LED on Stellaris LaunchPad.

#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/rom.h"
#include "inc/hw_sysctl.h"
#include "driverlib/sysctl.h"
#include "driverlib/timer.c"

#include <stdint.h>

#define LED_RED GPIO_PIN_1

uint16_t period = 5000;
uint16_t dutyCycle = 0;

int main() {
    // Set system clock to 80 MHz using PLL and external 16 MHz crystal.
    ROM_SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);

    // Enable GPIO for LED.
    ROM_SysCtlPeripheralEnable( SYSCTL_PERIPH_GPIOF );

    // Enable timer peripheral.
    ROM_SysCtlPeripheralEnable( SYSCTL_PERIPH_TIMER0 );

    // the GP Timer module clock must be enabled before the registers can be programmed (see page 313 or page 330).
    HWREG( SYSCTL_RCGCTIMER ) |= SYSCTL_RCGCTIMER_R0;
    // There must be a delay of 3 system clocks after the Timer module clock is enabled before any Timer module registers are accessed.
    ROM_SysCtlDelay( 1 );

    // Configure output pin for PWM use.
    ROM_GPIOPinConfigure( GPIO_PF1_T0CCP1 );
    ROM_GPIOPinTypeTimer( GPIO_PORTF_BASE , LED_RED );  //ROM_GPIOPinTypePWM( GPIO_PORTF_BASE , LED_RED ); => No PWM units on board, the timers are used for PWM.

    // LED_RED on PF1 is convenient.
    // PF1: T0CCP1 => 16/32-Bit Timer 0 Capture/Compare/PWM 1. (muxed with: PB7)
    // PWM 1 implies use of Timer B

    // PWM Mode
    // A timer is configured to PWM mode using the following sequence:
    // 1. Ensure the timer is disabled (the TnEN bit is cleared) before making any changes.
    ROM_TimerDisable( TIMER0_BASE , TIMER_B );

    // 2. Write the GPTM Configuration (GPTMCFG) register with a value of 0x0000.0004.
    // ROM_TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_PWM);
    HWREG( TIMER0_BASE + 0x000 ) = 0x00000004;

    // 3.In the GPTM Timer Mode (GPTMTnMR) register, set the TnAMS bit to 0x1, the TnCMR bit to 0x0, and the TnMR field to 0x2.
    HWREG( TIMER0_BASE + 0x008 ) |= TIMER_TBMR_TBAMS | TIMER_TBMR_TBMR_PERIOD;

    // 4. Configure the output state of the PWM signal (whether or not it is inverted) in the TnPWML field of the GPTM Control (GPTMCTL) register.
    HWREG( TIMER0_BASE + 0x00c ) |= 0;  // TIMER_CTL_TAPWML

    // 5. If a prescaler is to be used, write the prescale value to the GPTM Timer n Prescale Register (GPTMTnPR).
    //HWREG( TIMER0_BASE + 0x038 ) = ...

    // 6. If PWM interrupts are used, configure the interrupt condition in the TnEVENT field in the
    // GPTMCTL register and enable the interrupts by setting the TnPWMIE bit in the GPTMTnMR
    // register. Note that edge detect interrupt behavior is reversed when the PWM output is inverted
    //HWREG( TIMER0_BASE + 0x00c ) |= ...

    // 7. Load the timer start value into the GPTM Timer n Interval Load (GPTMTnILR) register.
    // ROM_TimerLoadSet(  TIMER0_BASE, TIMER_B, period - 1 );
    HWREG( TIMER0_BASE + 0x02c ) = period - 1;  // 16000 @ 80MHz system clock makes 5 kHz PWM

    // 8. Load the GPTM Timer n Match (GPTMTnMATCHR) register with the match value.
    // ROM_TimerMatchSet( TIMER0_BASE, TIMER_B, dutyCycle );
    HWREG( TIMER0_BASE + 0x034 ) = dutyCycle - 1;   // Defines duty cycle

    // 9. Set the TnEN bit in the GPTM Control (GPTMCTL) register to enable the timer and begin generation of the output PWM signal.
    // ROM_TimerEnable(   TIMER0_BASE, TIMER_B );
    HWREG( TIMER0_BASE + 0x00c ) |= TIMER_CTL_TBEN; 

    // In PWM Timing mode, the timer continues running after the PWM signal has been generated. The
    // PWM period can be adjusted at any time by writing the GPTMTnILR register, and the change takes
    // effect at the next cycle after the write.


    while ( 1 ) {
        //ROM_TimerMatchSet(TIMER0_BASE, TIMER_B, dutyCycle );
        HWREG( TIMER0_BASE + 0x034 ) = ( dutyCycle++ - 1 ); // Defines duty cycle
        if ( dutyCycle >= period - 1) {
            dutyCycle = 0;
        }
        ROM_SysCtlDelay(9000 / 3 );
    }
}