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.

CCS/EK-TM4C1294XL: Difficulty setting PWM clock correctly using recommended function...

Part Number: EK-TM4C1294XL

Tool/software: Code Composer Studio

Hello all,

So this is a problem with a couple of twists for me:

In the code attached, I'm attempting to generate a simple PWM servo-type control with a certain period (50Hz) and using the ADC input to tweak the duty cycle within limits.

1) I've gotten the code to work using the SysCtlClockFreqSet function as given, where I generate a 120MHz clock, then divide down the PWM clock by 64 using the recommended PWMClockSet. Problem is, it doesn't sitck around upon powering down the eval board if I need to use it remotely, away from the computer.

2) Now, when I use the SysCtlPWMClockSet function, which is not recommended for use with 1294 devices, using my above system clock settings, I can't seem to get the PWM output to get to 50Hz reliably. But, using this function gets the settings to stick where upon power down and power up via only USB, it's held reliably in memory.

Any ideas and thanks in advance?

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "inc/tm4c1294ncpdt.h"
#include "driverlib/adc.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/pwm.h"

struct pwm_pair {
    float period1;
    float cycle1;
};

#define MAX_COUNTS 4096.0F

int
main(void)
{
    float PM;
    struct pwm_pair duty;

    //Section 1 - This block activates the ADC0 with its associated GPIO port
//******************************************************************************
    // This array is used for storing the data read from the ADC FIFO. It
    // must be as large as the FIFO for the sequencer in use.  This example
    // uses sequence 3 which has a FIFO depth of 1.  If another sequence
    // was used with a deeper FIFO, then the array size must be changed.
    //
    uint32_t pui32ADC0Value[1];

    //
    // Set the clocking to run at 25 MHz using the PLL.  When
    // using the ADC, you must either use the PLL or supply a 120 MHz clock
    // source.
    SysCtlClockFreqSet(SYSCTL_XTAL_25MHZ |
                       SYSCTL_OSC_MAIN |
                       SYSCTL_USE_PLL |
                       SYSCTL_CFG_VCO_480, 12000000);

        // The ADC0 peripheral must be enabled for use.
        SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);

        //
        // GPIO port E needs to be enabled
        // so these pins can be used.
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);

        //
        // Select the analog ADC function for these pins.
        // Consult the data sheet to see which functions are allocated per pin.
        GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3);

        //
        // Enable sample sequence 3 with a processor signal trigger.  Sequence 3
        // will do a single sample when the processor sends a signal to start the
        // conversion.  Each ADC module has 4 programmable sequences, sequence 0
        // to sequence 3.  This example is arbitrarily using sequence 3.
        //
        ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 0);

        //
        // Configure step 0 on sequence 3.  Sample channel 0 (ADC_CTL_CH0) in
        // single-ended mode (default) and configure the interrupt flag
        // (ADC_CTL_IE) to be set when the sample is done.  Tell the ADC logic
        // that this is the last conversion on sequence 3 (ADC_CTL_END).  Sequence
        // 3 has only one programmable step.  Sequence 1 and 2 have 4 steps, and
        // sequence 0 has 8 programmable steps.  Since we are only doing a single
        // conversion using sequence 3 we will only configure step 0.  For more
        // information on the ADC sequences and steps, reference the datasheet.
        //
        ADCSequenceStepConfigure(ADC0_BASE, 3, 0, ADC_CTL_CH0 | ADC_CTL_IE |
                                 ADC_CTL_END);

        //
        // Since sample sequence 3 is now configured, it must be enabled.
        //
        ADCSequenceEnable(ADC0_BASE, 3);

        //
        // Clear the interrupt status flag.  This is done to make sure the
        // interrupt flag is cleared before we sample.
        //
        ADCIntClear(ADC0_BASE, 3);

        //Section 2 - This block activates the PWM0 modules 0 along with its
        //associated GPIO port
//************************************************************************************
    //Set PWM clock at the same frequency as system clock
    PWMClockSet(PWM0_BASE, PWM_SYSCLK_DIV_64);
    //SysCtlPWMClockSet(SYSCTL_PWMDIV_64);
    duty.period1 = 37500; //Switching period for 50Hz => (1/50Hz)*(120MHz/64)
    duty.cycle1 = duty.period1 * 0.1025;    //15% duty cycle (tuned)

    SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);    //Enable control of PWM module 0

    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);    //Enable control of GPIO F

    GPIOPinConfigure(GPIO_PF0_M0PWM0);    //Map PF0 to PWM0 Gen0, OP 0

    GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_0);    //Configure PF0 as PWM

    PWMGenConfigure(PWM0_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN |
        PWM_GEN_MODE_NO_SYNC);    //Configure PWM0 Gen0 as DOWN counter with no sync of updates

    PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, duty.period1);    //Set period of PWM0 Gen0

    PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, duty.cycle1);    //Set duty cycle of PWM0 Gen0

    PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT, true);    //Enable OP 0 on PWM0 Gen0

    PWMGenEnable(PWM0_BASE, PWM_GEN_0);    //Enable PWM0, Gen0
    //Section 3 - The intent of this section is to have the ADC0 sampling continuous as an input voltage is changed
    //in order to change the value being stored in pui32ADC0Value, which then should update the PWM0's pulse width within
    //5 and 15% duty cycle.
    //**************************************************************************************************************

    // Sample AIN0 forever.

    while(1)
    {
        //
        // Trigger the ADC conversion.
        //
        ADCProcessorTrigger(ADC0_BASE, 3);

        //
        // Wait for conversion to be completed.
        //
        while(!ADCIntStatus(ADC0_BASE, 3, false))
        {
        }

        //
        // Clear the ADC interrupt flag.
        //
        ADCIntClear(ADC0_BASE, 3);

        //
        // Read ADC Value.
        //
        ADCSequenceDataGet(ADC0_BASE, 3, pui32ADC0Value);

        //
        // This function provides a means of generating a constant length
        // delay.
        SysCtlDelay(SysCtlClockGet() / 12);


        PM = pui32ADC0Value[0] / MAX_COUNTS;  //use stored sampled value as ratio w/max 12-bit count from ADC
        if(PM * duty.cycle1 > duty.cycle1 / 1.95) {    //Hard limit such that 10ms pulse width is minimum (tuned)
        PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, PM * duty.cycle1);    //Set duty cycle of PWM0 Gen0
        }

    }

}

  • The function SysCtlClockGet() is only for the TM4C123 device (line 149). You can use the return value from SysCtlClockFreqSet(), or if the frequency does not change, just define it with a constant.
  • Understood, Bob. But interesting that this hasn't caused an issue before as it is mentioned as for 123 devices only in the peripherals manual. Changing this does not appear to alleviate my original issue, however.

    I did notice one other behavior, when I step through the code up until the PWMClockSet function and then into it, it becomes hung up, redirects fro the PWM.c file and sits inside of this function at an infinite while loop within FaultISR of the _startup_ccs.c file.

    But, to reiterate, using the not recommended function SysCtlPWMClockSet, although it's not producing the desired output period, works consistently. If I could only lock in the right frequency of 50Hz, it'd be good enough...