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.

Understanding then using PWM Module Signal Generation - Timing Configuration

Other Parts Discussed in Thread: TM4C1294NCPDT

I am having the worst time conceivable understanding and using the PWM module for my Tiva TM4C1294NCPDT. I am trying to do the two following base tasks:

  • Set the Period (target: 20ms)
  • Set the Pulse Width (target: any value 0-20ms)

I have consulted the Peripheral Library Reference Manual and the provided example code (TivaWare_C_Series-2.1.3.156/examples/peripherals/pwm) and am very painfully unable to correctly set Period or Pulse Width. See the attached code ZIP, and code below for full illustration.

Problems

  • I cannot successfully change the period
  • I cannot understand how the math here works
    • e.g. how to calculate the values for the set functions below. I can provide numeric examples of my results and confusion in them if needed

Questions (the starters I think...!)

  • How to get a PWM signal with a 20ms pulse??

  • How does the math work for setting the period?

This feels mystical and confusing, in strong contrast to my MSP430 experiences. What am I missing here?? :(

#define F_CPU_MHZ	(20000000)
#define PWM_PER		(6400000)

int main(void) {

/* Begin TivaWare Peripheral Driver Library Example (p.433)
   Note: Uses 'PWM_BASE', replaced here with valid 'PWM0_BASE'
   Changes - PWM_GEN_0 -> PWM_GEN_1
   	   	     OUT_0 -> OUT_2, OUT_1 -> OUT_3
   	   	     OUT_0_BIT -> OUT_2_BIT, OUT_1_BIT -> OUT_3_BIT
   Goal - Set the period of the PWM signal
   Goal - Set each pulse width, OUT_2 and OUT_3

   A Target - Set PWM0 to 20ms period, with 1ms pulse on OUT_2 and 19ms pulse on OUT_3

   Ultimate Goal - Have this dang thing make sense, and be able to use it like TivaWare states it can! 												*/

	//<Begin Added Code>
	g_ui32SysClock = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_OSC), F_CPU_MHZ);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    GPIOPinConfigure(GPIO_PF2_M0PWM2);
    GPIOPinConfigure(GPIO_PF3_M0PWM3);
    GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_2);
    GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_3);
	//<End Added Code>

	//The following example shows how to use the PWM API to initialize the PWM0 with a 50 KHz frequency, and with a 25% duty cycle
	//on PWM2 and a 75% duty cycle on PWM3.

    // Enable the PWM0 peripheral
    SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);

    // Wait for the PWM0 module to be ready.
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_PWM0));

    // Configure the PWM generator for count down mode with immediate updates to the parameters.
    PWMGenConfigure(PWM0_BASE, PWM_GEN_1, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);

    // Set the period. For a 50 KHz frequency, the period = 1/50,000, or 20 microseconds. For a 20 MHz clock, this translates to 400
    // clock ticks. Use this value to set the period
    PWMGenPeriodSet(PWM0_BASE, PWM_GEN_1, 400);

    // Set the pulse width of PWM2 for a 25% duty cycle
    PWMPulseWidthSet(PWM0_BASE, PWM_OUT_2, 100);

    // Set the pulse width of PWM3 for a 75% duty cycle
    PWMPulseWidthSet(PWM0_BASE, PWM_OUT_3, 300);

    // Start the timers in generator 0
    PWMGenEnable(PWM0_BASE, PWM_GEN_1);

    // Enable the outputs.
    PWMOutputState(PWM0_BASE, (PWM_OUT_2_BIT | PWM_OUT_3_BIT), true);

	for(;;);
}

5342.ref_pwm_servos.zip

  • Hello Justin

    The system clock is the PWM clock as well. So if you need 20ms PWM time period, the value to be loaded is System Clock*20ms. The Duty Cycle should now be a value between the Period count and 0.
  • Hi Amit,

     Yes that is what I thought. This is also what my many years of MSP430 work have instilled in me, so I was confused at my errors here.
     

    I used your math (f_clk*period) from the beginning, and re-ran them this AM and recorded the results, see below and attached for result using the code above. N-Attempted listed below is used for 'ui32Period' in PWMGenPeriodSet():

    Period[us] N-Attempted t_meas[ms]
    20 400 1.74
    40 800 3.23
    45 900 0.50
    50 1000 0.25
    100 2000 X
    200 4000 X
    20000 400000 X

    Questions

    • What on earth is happening here wrt N->t_meas??
    • How to correct this, how to obtain T=20ms?

    Measured Results

  • How are you measuring your period? The fact that the period drops with increasing count suggests an aliasing error.

    Robert
  • Hello Justin,

    The issue likes in the first line

    g_ui32SysClock = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_OSC), F_CPU_MHZ);

    You are trying to derive 20MHz system clock from a 25MHz oscillator without using the PLL. You must either use PLL to get 20MHz or scale the F_CPU_MHZ to be a integral divided value of 25MHz crystal.
  • Justin Reina89 said:
    I am having the worst time conceivable understanding and using the PWM module

    Yet - you reported success w/in a related, earlier PWM post - did you not?   Might your "worst time conceivable" then be "Self-Caused?"

    Changes - even those believed to be small/inconsequential - are to be avoided when entering new territory.   (superior, non MPS430 MCU)

    Again - you must learn to "Build effectively" upon past successes.   (thus avoiding "worst times.")

  • Hello cb1

    And you have successfully shown in the post on PWM, how to configure almost all parameters for the PWM. I think I should make it a link on the tips...
  • I will break this into two separate posts:

    • Post #1: To say thank you, express my gratitude and share my woeful, humorous story here (this post)
    • Post #2: The working final solution (code attached)

    With the unique, well-balanced and high-impact feedback above from Robert (t&m proc), Amit (fclk) and cb1 (peripheral config), you guys helped me surmount a challenge and a task that was of unquantifiable and had unachievable magnitude and presence for me, and now after today it is no big deal. THANK YOU!!! :)!!

    Here are my key learning's which resulted and allowed me to come to resolution. In retrospect they are quite embarrassing in their naivety, but now that they are closed, I'll get over it :)

    1. YOU ACTUALLY CAN ACCESS THE DANG Tiva registers directly!!! O... M... Sheesh!

    When I first ramped into Tiva, ~2mo ago I did so with TivaWare. All of the code, documentation and examples never ever use the dang registers, they always use the cute and fancy SysCtl and TivaWare calls.

    And as there was no #include <tiva.h>, like I was used to in my MSP430 work I just assumed "this was the best interface you could get. It was ARM Cortex, this is just the best that life gets. And so when I found the contents of TivaWare/inc today, for the first time ever, my world changed. Thank you so much cb1 for that final nudge to this realization!!!!

    2. The PWM0 Load & Compare Registers are actually only 16-bits!!!

    So previously when I tried to use enormous vals to them, thinking they were 32-bits I was completely wrong! :(

    3. There is a clock division feature on the PWM Module, so I can make it work and still leave F_CLK_CPU unmodified!! :)

    *But the SysCtl & Pwm library routines don't work?

    4. I am a bonehead and had my T&M tool timescales dramatically wrong. My OScope is down for the count and I am using an NI DAQ right now... :(!
        

    Once those four were addressed, the answer kind of just came naturally. This is thanks to you three, thank you!!! :)!

  • Key Solution Points:

    • Use valid F_Clk and make use of PWM input clock division
    • Use valid PWM0 Load Values (2^16 max)

    Remaining Open Question:

        The timing value for period below was empirically established. Every attempt I have taken to correctly calculate has failed, I ultimately ended up simply empirically finding them. Help please! :(...

    Code (also attached in Zip):

    #define XTAL_HZ		(25000000)
    #define F_CPU_HZ	(XTAL_HZ / 1)											/* @hazard	must be N division of XTAL					*/
    
    #define USEPWM_BIT		 	(1<<8)										  /* PWMCC.USEPWM. enabling adds %2, in addtn to 'N'		*/
    
    #define PWM_CLOCK_DIV_N		(2)
    #define PWM_CLOCK_DIV_BITS	(PWM_CLOCK_DIV_N<<0)
    #define PWM_CLOCK_HZ		(XTAL_HZ / PWM_CLOCK_DIV_N)						//?
    
    /************************************************************************************************************************************/
    /**	@fcn		int main(void)
     *  @brief		generate PWM signals for GPIO pins PF2 and PF3
     *  @details	uses PWM0 Generator Module 1 (PWM0_1)
     *
     *  @section	Purpose
     *  			Help Justin figure out how to use the dang Tiva PWM module
     *
     *  @pre		any
     *
     *  @post		PWM0.1 is configured and running:
     *  			- PWM2 (PF2) running with t_period=20.3ms, t_on=1ms
     *  			- PWM3 (PF3) running with t_period=20.3ms, t_on=19s
     *
     *  @section	Operation
     *				initialize the GPIO & PWM modules, then configure PWM Generator 1 for continuous output on both output signals
     *
     *  @section	Opens
     *  			Be able to set the PWM period and duty cycles mathematically, current values are empirically established
     *
     *  @note		Periods and Pulse Durations were empirically established, there derivations are presently unknown
     */
    /************************************************************************************************************************************/
    int main(void) {
    
    	//Set Clock
    	SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_OSC), F_CPU_HZ);
    
    	//******************************************************************************************************************************//
    	//	 											    GPIO MODULE INIT															//
    	//******************************************************************************************************************************//
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    
    	GPIOPinConfigure(GPIO_PF2_M0PWM2);
        GPIOPinConfigure(GPIO_PF3_M0PWM3);
    
        GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_2);
        GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_3);
    
    
    	//******************************************************************************************************************************//
    	//	 											   PWM MODULE #0 INIT															//
    	//******************************************************************************************************************************//
        SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);								    /* Enable the PWM0 peripheral						*/
    
        while(!SysCtlPeripheralReady(SYSCTL_PERIPH_PWM0));							/* Wait for the PWM0 module to be ready				*/
    
        PWMGenConfigure(PWM0_BASE, PWM_GEN_1, 										/* Configure the PWM generator for count down with 	*/
        				PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);					/* with immediate updates to the parameters 		*/
    
        PWM0_CC_R = (USEPWM_BIT | PWM_CLOCK_DIV_BITS);							    /* Set the PWM clock to the system clock			*/
    
    
    	//******************************************************************************************************************************//
    	//	 											 PWM GENERATOR #1 INIT															//
    	//******************************************************************************************************************************//
        PWMGenPeriodSet(PWM0_BASE, PWM_GEN_1, 500000);								/* Set the period of Generator Module 1 to 20ms		*/
    
        PWMPulseWidthSet(PWM0_BASE, PWM_OUT_2, 2000);							    /* Set the pulse width of PWM2 for a 1ms pulse		*/
    
        PWMPulseWidthSet(PWM0_BASE, PWM_OUT_3, 497500);								/* Set the pulse width of PWM3 for a 19ms pulse		*/
    
    
    	//******************************************************************************************************************************//
    	//	 										      PWM GENERATOR #1 START												        //
    	//******************************************************************************************************************************//
        PWMGenEnable(PWM0_BASE, PWM_GEN_1);											/* Start the timers in Generator Module 1			*/
    
        PWMOutputState(PWM0_BASE, (PWM_OUT_2_BIT | PWM_OUT_3_BIT), true);			/* Enable the outputs								*/
    
        SysCtlSleep();																/* End in sleep, pwm0.1 on							*/
    
        for(;;);																	/* safety											*/
    }
    
    

    ref_pwm_servos-e2e-2.zip    measurement.zip

  • Hello Justin,

    So what is the value being programmed and what is being measured?
  • I measured the following values, using the code attached above (ref_pwm_servos-e2e-2.zip):

    Config   Measure   Measure Pic Name
    PWM0.1 Period PWM2 Width PWM3 Width   Period [ms] PF2 On Width [ms] PF3 On Width [ms]    
    500000 2000 497500   20.30 1.00 19.00   msrPic1
    400000 1600 398000   3.30 0.75 2.30   msrPic2
    250000 1000 248750   26.30 0.50 25.70   msrPic3
    100000 400 99500   17.00 0.20 16.75   msrPic4
    Note: Measurements are instantaneous and have about 99% accuracy

    This table is also attached in the XLSX below. Thoughts? These values seem offensive they are so strange!

    :(

    measurement values.xlsx    msrPic1-4.zip   

  • Hello Justin

    The PWM counters are 16-bit which means it can count from 0 to 65535. How can a value of 100000 be loaded for PWM period?
  • My strong apologies. I am starting a new venture and working completely solo right now, wearing many dang  hats (mechE, compSci, biz, marketing, etc...). And when I get back into my primary and best seat, that of firmware development it is quite bumpy! ;)...

    Let's examine only the following test vector, for simplification and for elimination of prior errors... :(

    Test Vector: Last posted code with the following changes:

    	//******************************************************************************************************************************//
    	//	 											 PWM GENERATOR #1 INIT															//
        //
    	//******************************************************************************************************************************//
        PWMGenPeriodSet(PWM0_BASE, PWM_GEN_1, 40500);								/* Set the period of Generator Module 1 to 20ms		*/
    
        PWMPulseWidthSet(PWM0_BASE, PWM_OUT_2, 40500/2);					   		/* Set the pulse width of PWM2 for a 10ms pulse		*/
    
        PWMPulseWidthSet(PWM0_BASE, PWM_OUT_3, 40500/4);							/* Set the pulse width of PWM3 for a 5ms pulse		*/


     

    My apologies again for any and for all errors that I have injected in this thread. Work is monstrous right now! :(

    Assumptions:

    • These are valid values

    Questions:

    • How does the math work for the calculation of period?
    • My math shows this period should be 6.48ms, see attached XLSX

    final math.xlsx

  • Hello Justin

    For a system clock of 25MHz which is given to the PWM without a divider, this would be a PWM of period 40500/25e6 = 1.62 ms
  • Yes when N_pll in the XLSX is set to '1' a period of 1.62ms is the result.

    Why am I seeing my Tiva output 20.0ms then, using this value of 40500??
  • Hello Justin

    The only reason why it could be so is if the system clock is incorrectly configured or if the PWM divider is also being used.
  • My friend - you ask that we examine (only) the following test vector - which is wildly incomplete. Yet - few more lines down - we're presented w/"finalmath.xlsx" which conflicts w/your (earlier) [test vector only] direction. That's krazy making - is it not?

    A 25MHz PWM clock yields a period of 40nS - that's easily handled by a "focused" Justin - is it not? You must "zero in" on this PWM clock period - to have any chance of PWM success.

    We note that your comments (w/in the "test vector") are displaced far to the right - forcing more (unwanted) effort upon your "for free" help crue! (that's unwanted & improper - surely you know that.)

    It was your choice to "attempt to wear many hats" - might that have been a key source of your (now) obvious overload?   You write (uniquely), "worst time conceivable" - that's packed w/emotion and reflects far more upon your state of mind - little upon this MCU.

    The decision to, "Do everything by yourself" (while) learning a powerful, new MCU - may not set records for excellence. Relaxed learning always trumps "rushed/pressure-cooker" learning - it would appear that "IF" your past MCU is "up to task" (its usage) would have proved a better decision.

    You're smart enough to know all of this - and the PWM clocking & duty cycle setting is NOT that challenging.   All this suggests that your thinking has become muddied - and that cannot bode well for your project's success.

  • With very humble, and lightly embarrassing words I agree, you are quite correct.

    I took a breather just now, stepped out and stretched my legs. And realized in reflection how asinine it is for me to debug MCU timing with no Oscilloscope and only a 48 KS/s USB DAQ. My oh my, the crazy train needs to take a quick pause here, who does that!!

    I will return and post up working code once my new Oscilloscope comes in, but that is a few months out.

    Thank you guys for your help, and your patience!! :)

  • Hello Justin,

    Would suggest using a Saleae logic analyzer or a LogicPort LA. Both of them are easily available and work well for TM4C129x devices.
  • Perhaps another option is a local school - or Tech Club (ham radio even) - or the "rental" of a scope for a 30-60 day period.   (often the rental firm will apply the bulk of that rental fee to the instrument's purchase price.)

    We note that you employed at least one line of (nicely banned, at least discouraged) "DRM" code for a key/critical PWM Frequency Setting.   That's unwise - don't add complications when you've previously experienced trouble in that (or similar) area.

    While you await instrumentation - nothing prevents you from the study & engaging of one of the MCU's Timers.   Then - is it not possible to route your (suspect) PWM output into that (very) Timer input pin - with the Timer "enabled" for a brief - but precise period.   You may wish to dial down the PWM Frequency during these checkouts - yet many, many here employ the MCU Timers to perform key/critical: pulse width, pulse count, and pulse frequency measurements.

    We remain confused as you've reported (both) mistaken (outside of expectation) PWM frequency measurements AND lack of instrumentation.   Some way/how - those two reports fail to completely mesh...

  • Hi cb1,

    With respect to the measurements in error I will be in response here soon. My target is two weeks upon purchase of our own oscilloscope. Thank you for the advice on a school or tech club option, we have local amateur houses which I can visit if needed (e.g. Hedron PDX)! I am quite embarrassed on my failures here, and my weaknesses this illustrates.

    The timer feedback and suggestion is quite interesting, I will be considering this during upcoming work thank you.

    Question:

    Which line of PWM Frequency Configuration are you referencing as 'bad mojo', as DRM? DRM is 'Direct Register Modification' correct?

    Here is my guess:

    • PWM0_CC_R = (USEPWM_BIT | PWM_CLOCK_DIV_BITS);
  • Bingo! Firm/I have run BLDC Motors - employing 3 PWM Generators (6 outputs) for beyond six years - never/ever employing that line of code!