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.

EK-TM4C1294XL Launchpad PWM configuration

Other Parts Discussed in Thread: EK-TM4C1294XL, ENERGIA, TM4C1294NCPDT, TM4C129XNCZAD

Hello!

I'm working with the EK-TM4C1294XL Launchpad and trying to figure out how to use the hardware PWM module.

Ideally I'd like to find a way to use it in energia, but my guess is that energia doesn't support chip specific features like that, sadly. So any help getting it set up in CCS 6 would be fantastic.

So far I've made a new project and tried to compile the tivaware pwm example "dead_band.c" with an added #define PART_TM4C1294NCPD


Code I've tried is copied here:

//*****************************************************************************
//
// dead_band.c - Example demonstrating the dead-band generator.
//
// Copyright (c) 2010-2014 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
// 
//   Redistribution and use in source and binary forms, with or without
//   modification, are permitted provided that the following conditions
//   are met:
// 
//   Redistributions of source code must retain the above copyright
//   notice, this list of conditions and the following disclaimer.
// 
//   Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the
//   documentation and/or other materials provided with the  
//   distribution.
// 
//   Neither the name of Texas Instruments Incorporated nor the names of
//   its contributors may be used to endorse or promote products derived
//   from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 
// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package.
//
//*****************************************************************************
#define PART_TM4C1294NCPD
#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_memmap.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/pwm.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"

//*****************************************************************************
//
//! \addtogroup pwm_examples_list
//! <h1>PWM dead-band (dead_band)</h1>
//!
//! This example shows how to setup the PWM0 block with a dead-band generation.
//!
//! This example uses the following peripherals and I/O signals.  You must
//! review these and change as needed for your own board:
//! - GPIO Port B peripheral (for PWM pins)
//! - M0PWM0 - PB6
//! - M0PWM1 - PB7
//!
//! The following UART signals are configured only for displaying console
//! messages for this example.  These are not required for operation of the
//! PWM.
//! - UART0 peripheral
//! - GPIO Port A peripheral (for UART0 pins)
//! - UART0RX - PA0
//! - UART0TX - PA1
//!
//! This example uses the following interrupt handlers.  To use this example
//! in your own application you must add these interrupt handlers to your
//! vector table.
//! - None.
//
//*****************************************************************************

//*****************************************************************************
//
// This function sets up UART0 to be used for a console to display information
// as the example is running.
//
//*****************************************************************************
void
InitConsole(void)
{
    //
    // Enable GPIO port A which is used for UART0 pins.
    // TODO: change this to whichever GPIO port you are using.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    //
    // Configure the pin muxing for UART0 functions on port A0 and A1.
    // This step is not necessary if your part does not support pin muxing.
    // TODO: change this to select the port/pin you are using.
    //
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);

    //
    // Enable UART0 so that we can configure the clock.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

    //
    // Use the internal 16MHz oscillator as the UART clock source.
    //
    UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);

    //
    // Select the alternate (UART) function for these pins.
    // TODO: change this to select the port/pin you are using.
    //
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    //
    // Initialize the UART for console I/O.
    //
    UARTStdioConfig(0, 115200, 16000000);
}

//*****************************************************************************
//
// Prints out 5x "." with a second delay after each print.  This function will
// then backspace, clearing the previously printed dots, and then backspace
// again so you can continuously printout on the same line.  The purpose of
// this function is to indicate to the user that the program is running.
//
//*****************************************************************************
void
PrintRunningDots(void)
{
    UARTprintf(". ");
    SysCtlDelay(SysCtlClockGet() / 3);
    UARTprintf(". ");
    SysCtlDelay(SysCtlClockGet() / 3);
    UARTprintf(". ");
    SysCtlDelay(SysCtlClockGet() / 3);
    UARTprintf(". ");
    SysCtlDelay(SysCtlClockGet() / 3);
    UARTprintf(". ");
    SysCtlDelay(SysCtlClockGet() / 3);
    UARTprintf("\b\b\b\b\b\b\b\b\b\b");
    UARTprintf("          ");
    UARTprintf("\b\b\b\b\b\b\b\b\b\b");
    SysCtlDelay(SysCtlClockGet() / 3);
}

//*****************************************************************************
//
// Configure the PWM0 block with dead-band generation.  The example configures
// the PWM0 block to generate a 25% duty cycle signal on PD0 with dead-band
// generation.  This will produce a complement of PD0 on PD1 (75% duty cycle).
// The dead-band generator is set to have a 10us or 160 cycle delay
// (160cycles / 16Mhz = 10us) on the rising and falling edges of the PD0 PWM
// signal.
//
//*****************************************************************************
int
main(void)
{
    //
    // Set the clocking to run directly from the external crystal/oscillator.
    // TODO: The SYSCTL_XTAL_ value must be changed to match the value of the
    // crystal on your board.
    //
    SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
                   SYSCTL_XTAL_16MHZ);

    //
    // Set the PWM clock to the system clock.
    //
    SysCtlPWMClockSet(SYSCTL_PWMDIV_1);

    //
    // Set up the serial console to use for displaying messages.  This is just
    // for this example program and is not needed for PWM operation.
    //
    InitConsole();

    //
    // Display the setup on the console.
    //
    UARTprintf("PWM ->\n");
    UARTprintf("  Module: PWM0\n");
    UARTprintf("  Pin(s): PD0 and PD1\n");
    UARTprintf("  Features: Dead-band Generation\n");
    UARTprintf("  Duty Cycle: 25%% on PD0 and 75%% on PD1\n");
    UARTprintf("  Dead-band Length: 160 cycles on rising and falling edges\n\n");
    UARTprintf("Generating PWM on PWM0 (PD0) -> ");

    //
    // The PWM peripheral must be enabled for use.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);

    //
    // For this example PWM0 is used with PortB Pins 6 and 7.  The actual port
    // and pins used may be different on your part, consult the data sheet for
    // more information.  GPIO port B needs to be enabled so these pins can be
    // used.
    // TODO: change this to whichever GPIO port you are using.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

    //
    // Configure the GPIO pin muxing to select PWM functions for these pins.
    // This step selects which alternate function is available for these pins.
    // This is necessary if your part supports GPIO pin function muxing.
    // Consult the data sheet to see which functions are allocated per pin.
    // TODO: change this to select the port/pin you are using.
    //
    GPIOPinConfigure(GPIO_PB6_M0PWM0);
    GPIOPinConfigure(GPIO_PB7_M0PWM1);

    //
    // Configure the GPIO pad for PWM function on pins PB6 and PB7.  Consult
    // the data sheet to see which functions are allocated per pin.
    // TODO: change this to select the port/pin you are using.
    //
    GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_6);
    GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_7);

    //
    // Configure the PWM0 to count up/down without synchronization.
    // Note: Enabling the dead-band generator automatically couples the 2
    // outputs from the PWM block so we don't use the PWM synchronization.
    //
    PWMGenConfigure(PWM0_BASE, PWM_GEN_0, 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.
    // In this case you get: (1 / 250Hz) * 16MHz = 64000 cycles.  Note that
    // the maximum period you can set is 2^16 - 1.
    // TODO: modify this calculation to use the clock frequency that you are
    // using.
    //
    PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, 64000);

    //
    // Set PWM0 PD0 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 16000 clock cycles (64000 / 4).
    //
    PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0,
                     PWMGenPeriodGet(PWM0_BASE, PWM_OUT_0) / 4);

    //
    // Enable the dead-band generation on the PWM0 output signal.  PWM bit 0
    // (PD0), will have a duty cycle of 25% (set above) and PWM bit 1 will have
    // a duty cycle of 75%.  These signals will have a 10us gap between the
    // rising and falling edges.  This means that before PWM bit 1 goes high,
    // PWM bit 0 has been low for at LEAST 160 cycles (or 10us) and the same
    // before PWM bit 0 goes high.  The dead-band generator lets you specify
    // the width of the "dead-band" delay, in PWM clock cycles, before the PWM
    // signal goes high and after the PWM signal falls.  For this example we
    // will use 160 cycles (or 10us) on both the rising and falling edges of
    // PD0.  Reference the datasheet for more information on dead-band
    // generation.
    //
    PWMDeadBandEnable(PWM0_BASE, PWM_GEN_0, 160, 160);

    //
    // Enable the PWM0 Bit 0 (PD0) and Bit 1 (PD1) output signals.
    //
    PWMOutputState(PWM0_BASE, PWM_OUT_1_BIT | PWM_OUT_0_BIT, true);

    //
    // Enables the counter for a PWM generator block.
    //
    PWMGenEnable(PWM0_BASE, PWM_GEN_0);

    //
    // Loop forever while the PWM signals are generated.
    //
    while(1)
    {
        //
        // Print out indication on the console that the program is running.
        //
        PrintRunningDots();
    }
}

But that generates the error:

"../main.c", line 98: error #20: identifier "GPIO_PA0_U0RX" is undefined
"../main.c", line 99: error #20: identifier "GPIO_PA1_U0TX" is undefined
"../main.c", line 214: error #20: identifier "GPIO_PB6_M0PWM0" is undefined

"../main.c", line 215: error #20: identifier "GPIO_PB7_M0PWM1" is undefined

I am betting I have the define incorrect or something, since I assume the example is meant to be easy to use, but I'm stuck and would really appreciate some help here.

Thank you!

Garrett

  • Hello Garrett,

    The define is PART_TM4C1294NCPDT and has to be defined under settings

    Right Click the Project -> Show Build Settings -> Build -> ARM Compiler -> Advanced Settings -> Predefined Symbols

    In the upper box  of Pre-define NAME you have to add the following two defines.

    PART_TM4C1294NCPDT

    TARGET_IS_TM4C129_RA1

    Regards

    Amit

  • Afternoon Amit-

    Thanks for the quick response, but I'm still getting these errors after defining those two parts as instructed.

    "../main.c", line 213: error #20: identifier "GPIO_PB6_M0PWM0" is undefined

    "../main.c", line 214: error #20: identifier "GPIO_PB7_M0PWM1" is undefined

    Any ideas?

    Garrett

  • Hello Garrett,

    On TM4C1294NCPDT PB6 and PB7 are not PWM pins. It is PF0 and PF1 and the define is

    GPIO_PF0_M0PWM0
    GPIO_PF1_M0PWM1

    You would have to enable the clock to GPIO Port F instead of GPIO Port B

    Also do note that PF0 is a Locked Pin and the following post will help you unlock it

    http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/284566.aspx

    http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/346603.aspx

    Regards

    Amit

  • Alright, so I changed the defines to PF0 and PF1 - I really only need one PWM signal so is it alright to just comment out the PF0/PWM0 line? Or can I just leave it and it won't actually do anything since the pin is locked.

    Either way, this is a hard example to run!

    After changing those lines, I now get the following errors-

    <Linking>

     undefined              first referenced
      symbol                    in file     
     ---------              ----------------
     GPIOPinConfigure       ./main.obj      
     GPIOPinTypePWM         ./main.obj      
     GPIOPinTypeUART        ./main.obj      
     PWMDeadBandEnable      ./main.obj      
     PWMGenConfigure        ./main.obj      
     PWMGenEnable           ./main.obj      
     PWMGenPeriodGet        ./main.obj      
     PWMGenPeriodSet        ./main.obj      

     PWMOutputState         ./main.obj      
    >> Compilation failure
     PWMPulseWidthSet       ./main.obj      
     SysCtlClockGet         ./main.obj      
     SysCtlClockSet         ./main.obj      
     SysCtlDelay            ./main.obj      
     SysCtlPWMClockSet      ./main.obj      
     SysCtlPeripheralEnable ./main.obj      
     UARTClockSourceSet     ./main.obj      
     UARTStdioConfig        ./main.obj      
     UARTprintf             ./main.obj     

    So fairly clearly it looks like it's missing a header file or something that defines those, but honestly I am very new at programming anything more advanced than the msp430 I used in my embedded systems lab (and here I thought this would be similar enough to not cause problems! Hah)

    Thank you very much for your help Amit, you're a saint.

    Garrett

  • Hello Garrett,

    There are a few more things to be taken care

    1. Add the the file uartstdio.c and .h from utils directory of TIVAWare to your project

    2. On TM4C129 devices the SysCtlClockSet is replaced by the SysCtlClockFreqSet API

    3. The return value of SysCtlClockFreqSet is the System Clock Frequency which replaces SysCtlClockGet API call.

    As an example you can refer to

    C:\ti\TivaWare_C_Series-2.1.0.12573\examples\boards\ek-tm4c1294xl\hello\hello.c for usage of the SysCtlClockFreqSet

    For point of other functions giving an error during Linker the reason is missing driverlib.lib

    Attached is the screenshot of the missing driverlib.lib

    Regards

    Amit

  • Thank you for being so patient and helpful again Amit.

    I made the changes you suggested, and now I only have one error -

    "../driverlib/onewire.c", line 52: fatal error #1965: cannot open source file "inc/hw_onewire.h"

    I've googled around and it seems like this is a file that's just not included in tivaware - is that true? Is there any workaround for this?

    Additionally, I'm confused by what exactly I have to change for the switch to SysCtlClockFreqSet from SysCtlClockSet, but I'll keep studying the example you suggested and hopefully can figure that out on my own. For the record, all I did so far was change SysCtlClockSet to SysCtlClockFreqSet, but that seems to just throw more errors.

     

    Garrett

  • Hello Garrett

    Yes. hw_onewire.h is not available as of now. We plan for it to be a part of the next TIVAWare release.

    As for the function replacement the example should be sufficient in my opinion but do post the project you have in case it still does not work out.

    Regards

    Amit

  • You are my favorite person right now Amit, I'm gonna buy a palette of TM4C1294NCPDT chips when I get this sorted out and include a note telling them to give you a raise :)

    So there doesn't seem to be a way to make this code run, if it won't compile without hw_onewire.h, or am I missing something?

    Maybe it would help to change my approach to this problem.

    I'm working at a startup and we want to use TI chips because of the fantastic community and low price, not to mention all the features.

    We don't need a huge amount of speed, but I need to generate a configurable PWM signal (approx 15 kHz and varying duty cycles), among other things that are already taken care of.

    So, could you point me to a compilable/uploadable PWM example that I could potentially modify to use? So far I haven't been able to get a PWM signal from PWM hardware, though I can just bitbang some (noisy) signals to get us by for now.

    Thanks Amit

    Garrett

  • Hello Garrett,

    Zip your existing project (I do not have a ready PWM project on TM4C129) and attach to your post,

    I could see what exactly is required and trim the code down for PWM operation only.

    Regards

    Amit

  • i actualy tried before using PWM module with TivaWare using Energia compiler. it was a bit harder than with timers but it eventualy worked, but i think it was just in the TM4C123, the TM4C1294 i never got it to work

    let me see if i find my code and check if it's working tomorrow (let's hope the osciloscope is free for me to use)

  • Hello All,

    I had to search but I have an example for DK-TM4C129.

    This should work on the EK-TM4C129 with the change to the define from PART_TM4C129XNCZAD to PART_TM4C1294NCPDT

    0572.TM4C129_PWM.7z

    Regards

    Amit

  • if you're starting with the tm4c1294 with tivaware you should check this out:

    http://processors.wiki.ti.com/index.php/Creating_IoT_Solutions_with_the_TM4C1294XL_Connected_LaunchPad_Workshop#Attend_a_Live_Workshop

    http://software-dl.ti.com/trainingTTO/trainingTTO_public_sw/Tiva_CLP/docs.zip

  • Luis - thanks for the links! I'll check them out. And if you find that energia code, I'd love to see it, even if it's untested.

    Amit - I got the example code running that you sent me, and it seems to activate the PWM and dimly illuminate the onboard LED4, but for whatever reason the code gets stuck in a default interrupt loop for debugging, leaving me with a fairly useless pwm signal, sadly.

    Additionally, I am under the impression this should be sending serial data via the com port it's connected to the computer by, but while my terminal connects, nothing shows up. It must be the default ISR is being called before UART communication happens. But the first thing that the code does is print a message, and I don't even see that, so how can the PWM be active? Very confusing.

    Garrett

  • Hello Garrett,

    That is strange. it worked fine on DK-TM4C129: UART and PWM output.

    Did you just copy the main code or did you import the entire CCS Project?

    For the UART make sure that the Baud Rate is correct and UART Terminal is launched after the board is connected.

    Regards

    Amit

  • Baud rate is correct I believe (115200), and terminal is launched after the board has connected and started running.

    I imported the entire project.

  • Hello Garrett,

    I would try the same on the EK-TM4C129 early next week. But from the description it would seem that for some reason startup_ccs.c does not have the PWMIntHandler from the main test code in the Interrupt Vector Table. So I went back and checked the zip package and it was correctly mapped.

    Can you check it as well.

    The other thing is that have you changed the define PART_TM4C129XNCZAD to PART_TM4C1294NCPDT?

    Regards

    Amit

  • I got suspicious it was something in my environment, so I set up a virtual machine and installed CCS, then imported the project and it worked, so I'll have to do some housekeeping with my CCS install soon I suppose.

    Anyway, now I'm running that code successfully, thank you! I think I'm probably all set here for now, but I'll leave this unanswered until monday in case I run into any more problems I want to ask you about.

    And again, I can't thank you enough Amit, you've earned TI a loyal customer if this is the kind of support I can expect to receive.

    Garrett

  • Amit is always awesome at suporting in this forum, always on active and with a solutions.

    I just have some questions left.

    Do i have always to set pin muxing with the direct access register? In the TI workshop that i liked it is never needed.

    I am unable to set any port besides the PF_0, PF_1 and PF_2 with PWM. I used the code you provided and comented out the pin unlocking

  • Hello Luis,

    Every module which has a IO connection has limited pin muxing options. For the modules which have a single Pin available for the function, you need to set the Port Control, since it still has GPIO function associated to it.

    PWM from generator 0 is available on PF0 and PF1. Each generator has two comparators. Comparator A output is the even number pin and comparator B output is the odd number pin

    Regards

    Amit

  • Thanks Amit. If i've read the datasheet with more attention i would have noticed that. That was my main problem all along.

    @ , have this to see wich comparator is for wich, page 1680 of the tm4c1294ncpdt datasheet

     

  • Could you please explain me some of your code?

    The PWM clock sorce is the 120Mhz or the 16Mhz from the PIOSC?

  • Hello Luis,

    The PWM Generator is being sourced from the 120MHz System Clock.

    Regards

    Amit

  • The code you provided makes the folowing math for the period,

    1/frequency * 16Mhz, shouldn't it be 1/frequency * 120Mhz?

  • Hello Luis,

    The code I sent uses the following for the PWM Clock Configuration in the PWMCC Register

        PWMClockSet(PWM0_BASE, PWM_SYSCLK_DIV_1);

    which would amount to System Clock Divided by 1

    Regards

    Amit

  • Exacly, so it would mean that the clock would be 120Mhz/1. Maybe there's sometigh i just misread on the datasheet or sometigh but i think that in your code the PWM frequency source is 120Mhz since the divisor is just 1.

     in you code you have 16Mhz into account wich i don't see where it comes from:

    //
    // 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.
    // In this case you get: (1 / 250Hz) * 16MHz = 64000 cycles. Note that
    // the maximum period you can set is 2^16.
    //
    PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, 64000);

  • Hello Luis,

    OK I see your point now. The computations need to be worked out for 120MHz. The code was originally meant for TM4C123, so I would have "definitely" missed redoing the PWM section.

    Regards

    Amit

  • Thank you for confirmation, i was geting realy confused about that