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.

TM4C123GH6PM: PWMGenIntRegister() fails for INT_PWM0_1_TM4C123

Part Number: TM4C123GH6PM

Hello,

I'm trying to generate a zero interrupt with the PWM module and noticed something odd that I can't solve.

When I use PWMGenIntRegister, the callback always ends in the FaultIsr because the _PWMGenIntNumberGet call inside the register function returns zero when PWM0_BASE and PWM_GEN_1 are used.

Looking at the hw_ints.h file, there is an INT_PWM0_1_TM4C123 interrupt. Shouldn't _PWMGenIntNumberGet return this value?

// This doesn't work as _PWMGenIntNumberGet returns 0
PWMGenIntRegister(PWM0_BASE, PWM_GEN_1, &IsrPwmZero);

// This works just fine
IntRegister(INT_PWM0_1, &IsrPwmZero);

Am I missing something?

  • Hi,

     I think you should use IsrPwmZero, not &IsrPwmZero as in PWMGenIntRegister(PWM0_BASE, PWM_GEN_1, IsrPwmZero) if IsrPwmZero is your ISR handler. If this is not the problem then does it fault if you use PWM_GEN_0 or all will fault? I want to know if the problem is with the second argument or the third argument. 

  • Hi,
    I tried changing &IsrPwmZero to IsrPwmZero and the problem persists.

    The problem doesn't occur if I use the PWM_GEN_0. The _PWMGenIntNumberGet function of driverlib/pwm.c is like this:

    switch(ui32Base + ui32Gen)
    {
        //
        // The first PWM generator in the first PWM module.
        //
        case PWM0_BASE + PWM_GEN_0:
        {
            if(CLASS_IS_TM4C123)
            {
                return(INT_PWM0_0_TM4C123);
            }
            else if(CLASS_IS_TM4C129)
            {
                return(INT_PWM0_0_TM4C129);
            }
            else
            {
                return(0);
            }
        }
    
        //
        // The second PWM generator in the first PWM module.
        //
        case PWM0_BASE + PWM_GEN_1:
        {
            if(CLASS_IS_TM4C129)
            {
                return(INT_PWM0_1_TM4C129);
            }
            else
            {
                return(0);
            }
        }
    }

    There is no condition for TM4C123 and PWM_GEN_1 to return INT_PWM0_1_TM4C123.

  • Hi,

      I cannot reproduce your problem when I use PWM_GEN_0. I don't get any fault. Below is my example code. Look at line 250. 

    #include <stdbool.h>
    #include <stdint.h>
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "driverlib/debug.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/pwm.h"
    #include "driverlib/rom.h"
    #include "driverlib/rom_map.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "utils/uartstdio.h"
    
    //*****************************************************************************
    //
    //! \addtogroup example_list
    //! <h1>PWM Reload Interrupt Demo (pwm_interrupt)</h1>
    //!
    //! This example shows how to configure PWM0 for a load interrupt.  The PWM
    //! interrupt will trigger every time the PWM0 counter gets reloaded.  In the
    //! interrupt, 0.1% will be added to the current duty cycle.  This will
    //! continue until a duty cycle of 75% is received, then the duty cycle will
    //! get reset to 0.1%.
    //!
    //! This example uses the following peripherals and I/O signals.
    //! - GPIO Port B peripheral (for PWM0 pin)
    //! - PWM0 - PB6
    //!
    //! UART0, connected to the Virtual Serial Port and running at 115,200, 8-N-1,
    //! is used to display messages from this application.
    //
    //*****************************************************************************
    
    //*****************************************************************************
    //
    // The variable g_ui32PWMIncrement contains the value needed to increment the
    // PWM duty cycle by 0.1% cycles based on the System Clock speed.
    //
    //*****************************************************************************
    uint32_t g_ui32PWMIncrement;
    
    //*****************************************************************************
    //
    // The error routine that is called if the driver library encounters an error.
    //
    //*****************************************************************************
    #ifdef DEBUG
    void
    __error__(char *pcFilename, uint32_t ui32Line)
    {
    }
    #endif
    
    //*****************************************************************************
    //
    // The interrupt handler for the for PWM0 interrupts.
    //
    //*****************************************************************************
    void
    PWM0Gen0IntHandler(void)
    {
        //
        // Clear the PWM0 LOAD interrupt flag.  This flag gets set when the PWM
        // counter gets reloaded.
        //
        MAP_PWMGenIntClear(PWM0_BASE, PWM_GEN_0, PWM_INT_CNT_LOAD);
    
        //
        // If the duty cycle is less or equal to 75% then add 0.1% to the duty
        // cycle.  Else, reset the duty cycle to 0.1% cycles.  Note that 64 is
        // 0.1% of the period (64000 cycles).
        //
        if((MAP_PWMPulseWidthGet(PWM0_BASE, PWM_OUT_0) + g_ui32PWMIncrement) <=
           ((MAP_PWMGenPeriodGet(PWM0_BASE, PWM_GEN_0) * 3) / 4))
        {
            MAP_PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0,
                                 MAP_PWMPulseWidthGet(PWM0_BASE, PWM_GEN_0) +
                                 g_ui32PWMIncrement);
        }
        else
        {
            PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, g_ui32PWMIncrement);
        }
    }
    
    //*****************************************************************************
    //
    // Configure the UART and its pins.  This must be called before UARTprintf().
    //
    //*****************************************************************************
    void
    ConfigureUART(void)
    {
        //
        // Enable the GPIO Peripheral used by the UART.
        //
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    
        //
        // Enable UART0.
        //
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    
        //
        // Configure GPIO Pins for UART mode.
        //
        MAP_GPIOPinConfigure(GPIO_PA0_U0RX);
        MAP_GPIOPinConfigure(GPIO_PA1_U0TX);
        MAP_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
        //
        // Use the internal 16MHz oscillator as the UART clock source.
        //
        UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
    
        //
        // Initialize the UART for console I/O.
        //
        UARTStdioConfig(0, 115200, 16000000);
    }
    
    //*****************************************************************************
    //
    // Configure PWM for a load interrupt.
    //
    //*****************************************************************************
    int
    main(void)
    {
        volatile uint32_t ui32Loop;
    
        //
        // Set the clocking to run directly from the external crystal/oscillator.
        //
        MAP_SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
                           SYSCTL_XTAL_16MHZ);
    
        //
        // Set the PWM clock to be equal to the system clock.
        //
        MAP_SysCtlPWMClockSet(SYSCTL_PWMDIV_1);
    
        //
        // Initialize the UART.
        //
        ConfigureUART();
    
        //
        // Display the setup on the console.
        //
        UARTprintf("PWM ->\n");
        UARTprintf("  Module: PWM0\n");
        UARTprintf("  Pin: PB6\n");
        UARTprintf("  Duty Cycle: Variable -> ");
        UARTprintf("0.1%% to 75%% in 0.1%% increments.\n");
        UARTprintf("  Features: ");
        UARTprintf("Variable pulse-width done using a reload interrupt.\n\n");
        
        //
        // Enable the GPIO port that is used for the on-board LED.
        //
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    
        //
        // Enable the GPIO pin for the LED (PF3) as an output.
        //
        MAP_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_3);
    
        //
        // The PWM peripheral must be enabled for use.
        //
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);
    
        //
        // Enable the GPIO port that is used for the PWM output.
        //
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    
        //
        // Configure the PWM function for this pin.
        //
        MAP_GPIOPinConfigure(GPIO_PB6_M0PWM0);
        MAP_GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_6);
    
        //
        // Configure PWM0 to count down without synchronization.
        //
        MAP_PWMGenConfigure(PWM0_BASE, PWM_GEN_0,
                            PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);
    
        //
        // Set the PWM period to 250Hz.  To calculate the appropriate parameter
        // use the following equation: N = (1 / f) * SysClk.  Where N is the
        // function parameter, f is the desired frequency, and SysClk is the
        // system clock frequency.
        //
        MAP_PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, (SysCtlClockGet() / 250));
    
        //
        // For this example the PWM0 duty cycle will be variable.  The duty cycle
        // will start at 0.1% and will increase to 75%. After a duty cycle of 75%
        // is reached, it is reset back to 0.1%.  This dynamic adjustment of the
        // pulse width is done in the PWM0 load interrupt, which increases the
        // duty cycle by 0.1% every time the reload interrupt is received.
        //
    
        //
        // Set the PWM increment variable based on the System Clock. Since this
        // is a 250 Hz PWM, continue to use the equation N = (1 / f) * SysClk.
        // Then to set the initial period to 0.1% by dividing (N / 1000).
        // This variable will be used to increment PWM0 by 0.1% on each
        // interrupt.
        //
        g_ui32PWMIncrement = ((SysCtlClockGet() / 250) / 1000);
    
        //
        // Set the initial PWM0 Pulse Width with the calculated increment variable
        // to start at 0.1% duty cycle.
        //
        MAP_PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, g_ui32PWMIncrement);
    
        //
        // Enable processor interrupts.
        //
        MAP_IntMasterEnable();
    
        //
        // Allow PWM0 generated interrupts.  This configuration is done to
        // differentiate fault interrupts from other PWM0 related interrupts.
        //
        MAP_PWMIntEnable(PWM0_BASE, PWM_INT_GEN_0);
    
        //
        // Enable the PWM0 LOAD interrupt on PWM Gen 0.
        //
        MAP_PWMGenIntTrigEnable(PWM0_BASE, PWM_GEN_0, PWM_INT_CNT_LOAD);
    
        //
        // Enable the interrupt for PWM Gen 0 on the processor (NVIC).
        //
        MAP_IntEnable(INT_PWM0_0);
    
        //
        // Enable the PWM Out Bit 0 (PB6) output signal.
        //
        MAP_PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT, true);
    
        PWMGenIntRegister(PWM0_BASE, PWM_GEN_0, PWM0Gen0IntHandler);
    
        //
        // Enable the PWM generator block.
        //
        MAP_PWMGenEnable(PWM0_BASE, PWM_GEN_0);
    
        //
        // Loop forever blinking an LED while the PWM signals are generated and
        // PWM0 interrupts are occurring.
        //
        while(1)
        {
            //
            // Turn on the LED.
            //
            MAP_GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, GPIO_PIN_3);
    
            //
            // This function provides a means of generating a constant length
            // delay.  The function delay (in cycles) = 3 * parameter.  Delay
            // 0.5 seconds arbitrarily.
            //
            MAP_SysCtlDelay((MAP_SysCtlClockGet() / 2) / 3);
    
            //
            // Turn off the LED.
            //
            MAP_GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, 0x00);
    
            //
            // This function provides a means of generating a constant length
            // delay.  The function delay (in cycles) = 3 * parameter.  Delay
            // 0.5 seconds arbitrarily.
            //
            MAP_SysCtlDelay((MAP_SysCtlClockGet() / 2) / 3);
        }
    }
    

    Make sure you have the correct predefined symbols as follows for TM4C123. 

      

  • Yes, exactly. Your code works fine for PWM0 and GEN0, but when I change the PWM generator for PWM0 GEN1 (and PB5 as PWM output), I get the same problem:

    The interrupt callback is not called (because it was not registered). Instead, the processor gets stuck in IntDefaultHandler.

  • Looks like in _PWMGenIntNumberGet(), it only supports TM4C123 for PWM_GEN_0 and not others. Therefore, when PWM_GEN_1 is specified, it returns 0 before calling IntRegister() with 0 as the interrupt number which is incorrect. I will suggest you directly call IntRegister(INT_PWM0_1, IsrPwmZero) which you have demonstrated working.