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.

TM4C123GH6PGE: PWM Frequency Setup

Part Number: TM4C123GH6PGE
Other Parts Discussed in Thread: TM4C123GH6PM

Hi Team,

A customer wants to confirm if in a group of eight PWM signal lines, PWM00 to PWM07, the signal lines PWM00-PWM03 can run at a different frequency than PWM signal lines PWM04-PWM07. If yes, can you share an example program.

Thank you for your usual support!

Regards,

Danilo

  • Greetings,

    Might a, "FAR from "TI" team" (i.e.alien/outsider) serve to reduce your 'wait time?'

    As such responses are (often) suspect - this one will employ the MCU's manual to document my (small) firm's findings.

    Page 1232 presents "Figure20-1.PWM Module Diagram" - appears well mated to your question.   We present it here for ease/clarity.   (Red highlight is our own)

    The "PWM Clock" (as shown) is "commonly routed" to all Four PWM Generators - thus client's desire for "Independent Frequency Control" appears foiled.

    With that said - the MCU manual notes, " The TM4C123GH6PM controller contains two PWM modules, each with four generator blocks."    Thus - by employing 2 PWM generator blocks - from each of the PWM modules - it (then) becomes possible to, "Meet your client's objective!"    Note that (some) indirection is required - & that the MCU manual provides rich detail - apparently missed/little noted during client's review...

    As for an "example program" - again the '123's Manual advises, 26.2.2.42 SysCtlPWMClockSet() (pg. 513) as the appropriate function to "Set the PWM Module's frequency."    Note that the '123 family differs from the '129 family - and that "SysCtlPWMClockSet()" appears w/in the System Control API block - not the (expected) PWM Block!    The System Clock may be "integrally divided by powers of 2" - ranging to a maximum divide by 64.    (Again pg. 513 fully informs/advises...)

  • Hello Danilo & cb1,

    What the customer is looking for should be possible with just PWM0.

    The PWM clock comes from a common source (it's basically System Clock with an optional divider stage for the PWM) and that clock is sourced into both modules so there is no changes that can be done. The PWM has two clock source options:

    • The System Clock
    • A predivided System Clock

    Adjusting that setting is what is handled by SysCtlPWMClockSet.

    As far as frequencies go, this can be set by the PWMGenPeriodSet API.

        //
        // 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));

    The width of the PWM is then set by PWMPulseWidthSet.

        //
        // Set PWM0 to a duty cycle of 25%.  You set the duty cycle as a function
        // of the period.  Since the period was set above, you can use the
        // PWMGenPeriodGet() function.  For this example the PWM will be high for
        // 25% of the time or (PWM Period / 4).
        //
        MAP_PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0,
                             MAP_PWMGenPeriodGet(PWM0_BASE, PWM_GEN_0) / 4);

    The trick with the frequencies isn't that you can't spread them over multiple generator blocks. It's that a single Generator Block cannot have different frequencies as the frequency is configured at a generator level.

    So PWM0 and PWM1 will have the same frequences, as well PWM2/PWM3 etc.

    Each PWM can have a different duty cycle set however, so you can have PWM0 at 250 Hz and a 25% duty cycle, and PWM1 at 500 Hz and a 50% duty cycle.

    So given that, they can use different frequencies for PWM0-PWM3 and PWM4-PWM7.

    Here is a quick source code example that goes through the configuration of multiple frequencies, the customer can expand on it. This is a modified example from an upcoming project that will be our upcoming TivaWare release (I did not update comments):

    #include <stdbool.h>
    #include <stdint.h>
    #include "inc/hw_memmap.h"
    #include "driverlib/debug.h"
    #include "driverlib/gpio.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 examples_list
    //! <h1>PWM Inverted Output Demo (pwm_invert)</h1>
    //!
    //! This example shows how to setup PWM using the inverted output function.
    //! This feature allows you to invert the polarity of the PWM output.  This
    //! example is setup to invert a 25% duty cycle to get a 75% duty cycle every
    //! 2 seconds.
    //!
    //! 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 error routine that is called if the driver library encounters an error.
    //
    //*****************************************************************************
    #ifdef DEBUG
    void
    __error__(char *pcFilename, uint32_t ui32Line)
    {
    }
    #endif
    
    //*****************************************************************************
    //
    // 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 25% duty cycle signal running at 250Hz.
    //
    //*****************************************************************************
    int
    main(void)
    {
        //
        // 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 the system clock.
        //
        MAP_SysCtlPWMClockSet(SYSCTL_PWMDIV_1);
    
        //
        // Initialize the UART.
        //
        ConfigureUART();
    
        //
        // 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_GPIOPinConfigure(GPIO_PB4_M0PWM2);
        MAP_GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_4 | GPIO_PIN_6);
    
        //
        // Configure PWM0 to count up/down without synchronization.
        //
        MAP_PWMGenConfigure(PWM0_BASE, PWM_GEN_0, PWM_GEN_MODE_UP_DOWN |
                            PWM_GEN_MODE_NO_SYNC);
        MAP_PWMGenConfigure(PWM0_BASE, PWM_GEN_1, PWM_GEN_MODE_UP_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));
        MAP_PWMGenPeriodSet(PWM0_BASE, PWM_GEN_1, (SysCtlClockGet() / 500));
    
        //
        // Set PWM0 to a duty cycle of 25%.  You set the duty cycle as a function
        // of the period.  Since the period was set above, you can use the
        // PWMGenPeriodGet() function.  For this example the PWM will be high for
        // 25% of the time or (PWM Period / 4).
        //
        MAP_PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0,
                             MAP_PWMGenPeriodGet(PWM0_BASE, PWM_GEN_0) / 4);
        MAP_PWMPulseWidthSet(PWM0_BASE, PWM_OUT_2,
                             MAP_PWMGenPeriodGet(PWM0_BASE, PWM_GEN_1) / 2);
    
        //
        // Enable PWM Out Bit 0 (PB6) output signal.
        //
        MAP_PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT | PWM_OUT_2_BIT, true);
    
        //
        // Enable the PWM generator block.
        //
        MAP_PWMGenEnable(PWM0_BASE, PWM_GEN_0);
        MAP_PWMGenEnable(PWM0_BASE, PWM_GEN_1);
    
        //
        // Loop forever while the PWM signals are generated.
        //
        while(1)
        {
            //
            // Print out that the level of PWM is normal.
            //
            UARTprintf("PWM Output: Normal\n");
    
            //
            // This function provides a means of generating a constant length
            // delay.  The function delay (in cycles) = 3 * parameter.  Delay
            // 2 seconds arbitrarily.
            //
            MAP_SysCtlDelay((MAP_SysCtlClockGet() * 2) / 3);
    
            //
            // Invert PWM0 signal.
            //
            MAP_PWMOutputInvert(PWM0_BASE, PWM_OUT_0_BIT | PWM_OUT_2_BIT, true);
    
            //
            // Print out that the level of PWM is inverted.
            //
            UARTprintf("PWM Output: Inverted\n");
    
            //
            // This function provides a means of generating a constant length
            // delay.  The function delay (in cycles) = 3 * parameter.  Delay
            // 2 seconds arbitrarily.
            //
            MAP_SysCtlDelay((MAP_SysCtlClockGet() * 2) / 3);
    
            //
            // Switch PWM0 signal back to regular operation.
            //
            MAP_PWMOutputInvert(PWM0_BASE, PWM_OUT_0_BIT | PWM_OUT_2_BIT, false);
        }
    }
    

  • Hi Ralph,

    You are correct - while my group successfully demonstrated the, "Common Distribution" of the PWM Clock - to ALL of the PWM Generators - each PWM Generator (controlling 2 PWM Channels) may further divide (thus alter) the PWM Frequency.

    The "Dual Module" PWM Solution my group presented DOES succeed - yet if the objective can be satisfied w/in a "Single PWM Module" - the 2nd PWM Module may be held in reserve.

    Staff notes that their "dual Module Solution" may "better separate" (thus reduce) any potential "Signal Bleed or Cross-Talk" - thus provide improved Signal Purity -  which may prove an advantage in "high power" applications.

    [edit] 6 Mar 2020:   Reviewing - & thinking even further - certain applications may clearly (and uniquely) benefit from our "Dual  PWM Module" utilization!    One (range) of these applications involves the, "Satisfaction of both "High & Low Band" PWM Support."     This is best achieved by the "Dual PWM Mode" - which enables a, "Programmable Frequency Spread between each PWM Module" - enabled by different parameter values loaded into, "SysCtlPWMClockSet()" again for each PWM module!    This "Added attention to detail" enables vastly improved PWM resolution - especially when the "High & Low Bands" are distant...

    Again - our (much earlier) and your solution will both work...