0
\$\begingroup\$

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 ) {
    }
}
\$\endgroup\$
  • \$\begingroup\$ You are writing the port before declaring it as an output. It might be the problem. \$\endgroup\$ – Eugene Sh. Jan 20 '15 at 21:24
  • \$\begingroup\$ @EugeneSh. refering to PinWrite vs PinTypeGPIOOutput? Tried it, didn't solve it. I think it is unlikely the problem anyway because couple lines down I reconfigure the pin anyway using PinTypeTimer. \$\endgroup\$ – jippie Jan 20 '15 at 21:38
  • \$\begingroup\$ if you just turn it on it turns on right? \$\endgroup\$ – Vladimir Cravero Jan 20 '15 at 21:40
  • \$\begingroup\$ Just in case, put a breakpoint in your HardFault handler, and make sure you are not ending up there... \$\endgroup\$ – Eugene Sh. Jan 20 '15 at 21:40
  • 1
    \$\begingroup\$ @VladimirCravero ROM_ calls are good for calling a hard-coded library functionality directly in the MC's ROM (which are supporting it). It is good for reducing executable size (no need to link the soft implemented libraries). \$\endgroup\$ – Eugene Sh. Jan 20 '15 at 22:35
1
\$\begingroup\$

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 );
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.