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: Using the PWM for SPWM

Part Number: TM4C123GH6PM

In the TM4C123GHPM datasheet, there's the following example shown in the picture.

I notice that there are no Tivaware methods that allow me to set the PWM0CMPA values. I want to utilize this example to create a varying duty cycle PWM that mimics a sine curve after filtering. The idea is having the triangle wave in PWM gen compare with an incoming, processed sinewave (so the value is an ADC reading that goes through some processing) and if the triangle >sine, I would output high on PWMA and low otherwise. Ideally, these would be at the same height, where both peaks are at 3300. Also, I want to have this switching happen at 10kHz. Would my code be as follows?

I'm assuming:

1) The Load value is the peak of the triangle waveform for the PWM Gen (based on what I understand of the datasheet)

2) I would use a timer interrupt to have the switching happen at every 100us (10kHz)

3) To have the comparison change, in the timer interrupt, I would change the PWM0_0_CMPA_R value to match my current sinewave sample.

4)  To get the height of the triangle to be 3300, so if it's 80Mhz clock frequency, I want the reload to be at 3299 when setting the PWMGenPeriodSet(PWM0_BASE, PWM_GEN_1, SysCtlClockGet()/24250) method.

5) In the general setup, I should set PWM_O_0_CTL=x80 //GenA is locally synchronized     and  PWM0CMPA= 0x000000E0  // Drive pwmA Low when it's Comparator A Up and high when it's Comparator A down, while doing nothing when counter=0 and Load. How do I ensure the output is high while it's counting up to Compactor A?             

// Setup
SysCtlPWMClockSet(SYSCTL_PWMDIV_1); //set PWM clock to processor clock with multiplier of 1
SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
	
GPIOPinConfigure(GPIO_PB4_M0PWM2);
GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_4);
PWMGenConfigure(PWM0_BASE, PWM_GEN_1, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_DB_NO_SYNC);
PWMGenPeriodSet(PWM0_BASE, PWM_GEN_1, SysCtlClockGet()/24250); //I want the height of the triangle to be 3300, so if it's 80Mhz clock frequency, I want the reload to be 
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_2, 2500* 50/100);
PWMOutputState(PWM0_BASE, PWM_OUT_2_BIT, true);
PWMDeadBandEnable(PWM0_BASE, PWM_GEN_1, 0xF, 0xF);
GPIO_PORTB_DR8R_R |=0xC0; // 8mA output
PWMGenEnable(PWM0_BASE, PWM_GEN_1);
  • Hi,

     Take a look at the below diagram. Why don't you pre-record all the duty cycles in a table. Generate a PWM interrupt when the counter reaches zero and reload the next new duty cycle. For example, you first start with a pulse with at 75% duty cycle and in the next PWM cycle change the duty cycle to 93.3% and so on so forth. This example divides (digitizes) the sine wave into 12 samples. You can increase the sample points. What you are doing is to create varying duty cycle PWM. Once the PWM signal runs through a low pass filter you will see a sine wave. 

  • Hi Charles,

    I initially thought - as did you - that poster seeks to,  "Manufacture a PWM signal."      

    Upon repeat reading - I believe she seeks (instead) to,  "having the triangle wave in PWM gen compare with an incoming, processed sinewave" - which is NOT - the (usual) PWM Signal - as Generated by the MCUs -  here.     (i.e. highly unusual)    

    As (most always)  "NO justification for this unique requirement is provided" and even w/much experience - I'm unable to note the "utility" of this (unusual) approach.    Had the "end-game" been (even) "hinted" - another approach (may) have been suggested.

    It would appear that her desire (may) be better achieved via:

    • buffering her original sine wave signal
    • then running that (scaled or amplified) signal (as/if required) through a conventional Analog Comparator (which has its "comparison voltage" regularly, "Adjusted to suit."   In this manner - a "constant level output would be achieved (one of her goals) - and the pulse width (duty cycle) would be based upon the level of the "ADJUSTABLE Voltage" - being fed to the "Comparator's (other) input.   (i.e. the higher the level of that voltage - the lower the pulse width/duty cycle.
    • By increasing the frequency of these, "Comparator Voltage Settings" - the frequency of the PWM impulses may be controlled.

    She mentions,  "Filtering the PWM output - created by the MCU - yet the PWM period (revealed by her example) is SO LOW in FREQUENCY - that the filter components would be "very large" - and extremely taxed to "reduce ripple."

  • Hi cb1,
    Thanks. You make me go back to the post and read again. :-) Perhaps I'm not understanding what is being asked and most importantly what is to be achieved. I first read "I want to utilize this example to create a varying duty cycle PWM that mimics a sine curve after filtering." and thought it was about creating a sine wave signal using a varying PWM and run it through a Low-pass filter. I guess the poster can elaborate again.
  • Hi Charles,

    The "unusual nature" of the post - highlights WHY - posters should include (some) detail - in explaining their REAL OBJECTIVE.     Minus (proper) poster guidance (long suggested) - such is observed (often) here...

    Poster's "3300Hz" PWM Frequency will substantially limit the "reconstruction capability" of her effort.    (if indeed - as my (multiple reads) have (reading between the lines) - "teased out.")

    Tasking any MCU to such task - when "standard, analog components may better (i.e. FASTER, EASIER & MORE DIRECTLY)  satisfy" - is outside my understanding...

  • Oriana Wong said:
    ... create a varying duty cycle PWM that mimics a sine curve after filtering  ...  this switching happens at 10kHz 

    There is no mention of your target accuracy - nor the (real) end objective.

    With that noted - I mentioned to friend Charles - a (long) past design an earlier firm of mine had implemented - which (appears) to (somewhat) "overlap" your design requirements.

    Here's what we produced (> 25 years past) - the "Glowing, Neon - Gas-Discharge Displays" - required signal treatment - similar to yours:             (Shown here - a full dashboard, built for my, "308GTB" -  (cop/gurl magnet) - Italian Sports Car [red])

    You should note 2 Linear Bar Graphs (left bottom) and 1 Circular Bar Graph (center).    Both  Displays "deflected" (i.e. produced those bars)  - ONLY when the "Incoming ("your" word) Input Signal" exceeded a, "Linear, Monotonic, Cyclic, Ramping  Signal - applied to individual, analog comparators.    When the "always  Ramping (Comparison) signal "overtook" ("cop" word,  "exceeded")  the input signal - no further bars would illuminate.   (i.e. the PWM output switched to logic "low.") 

    Thus our Analog Comparator Method - produced a, "Pulsed PWM Output"  - not too unlike - that which you propose!     Note that our signals were NOT sine-waves - thus we were "freed" from the requirement to,  "Precisely Sync to a Sine-Wave" - which (may) be "your fate."    (very likely - your fate...)

    Again - the beauty of this Linear Ramp approach - was the "Automatic generation of a "consistent level (and level-adjustable) pulse" - whose duty cycle - (very) accurately - reflected the,  "Incoming signal's amplitude."   

    I'm able to quickly identify (several) ways for you to properly "Sync to your incoming Sine-Wave" - (so that you may exploit my firm's proven method) - yet as this approach is (just) my "best estimate" (in your behalf) - I prefer to await your comments.

  • Thanks for all the feedback and discussion.

    I want to compare a triangle wave and sine wave, so these comparisons will provide the PWM signals to switches in a grid-tied h-bridge inverter. By comparing the triangle and sine waves, two PWM signals will be generated that, after an external filter, will be a sine wave. The end result is to perform power flow, so my output sine would need to be a time shifted version of the incoming sine wave. I want my comparisons to happen every 100 us, but I wasn't sure whether to set my PWM period at that (80MHz/10kHz) or have that be another parameter.

    If I'm not making any sense, please let me know. I was hoping to use the PWM comparator as the triangle wave running at 10kHz I compare my sine wave to, but I'm not sure if I'm using all the correct registers and settings. I also want to control the height of the triangle wave, but I'm not sure if that's possible.
  • May it be noted that (most) of what you seek - may (still) be nicely achieved - via a "basic" Analog Comparator IC?     While you (may) be able to "tease this out" - via (other) than "normal/customary" MCU-PWM means - that will require "extra time & effort" (far beyond the quicker & simpler method),  which I've detailed in your behalf.

    Have you realized that the drawing you've imported - while displaying what (appears) to be a triangle "wave" - is in reality a "visual representation" of the MCU's  "FREE-RUNNING, counter/timer"  (incrementing then decrementing)  -  NO "Triangular, Analog waveform" (to my best understanding) is - internally or externally, created or available.      (that's your job - via a proper filter - which,  "Returns you to the Analog Realm."     (as suggested earlier  -  by one here...)  

    I'm not so sure the issue here is,  "One of Not Making Sense."     Relative "Speed, Ease, & (past) Proven results" - register higher - in my opinion.

    It would be useful if you'd (somewhat) detail:

    • your accuracy requirements   (requested - yet to arrive)
    • specify the "spec" of  your  output sine's  "time shift"
    • explain why you seek comparisons every 100µS  (10KHz)
    • explain your objective in "controlling the height" (assumed to be signal level) of the triangle wave
    • explain why the sine-wave - is not to have its "level" - similarly controlled
    • is it not  "fair/proper" to ask,   "Where is your instructor - or boss" - and their, "expertise/guidance?"     (if yours is a "DRM restricted" project - may we note, "Nightmare in the making?")

    It's hoped that this (necessary) added detail - will induce (others) to offer their views.    

    My firm - most always - starts such tech investigations by  "Methodically Surveying"  that which is Commercially Available  - and then "drilling down" - to see if our group may,  (if & when lucky)  effect improvements and/or savings...     Have you conducted such survey?      And - if so - would you be so good as to,  "List your findings?"    (Saves substantial  "wear/tear" upon your  "Helper Crüe"  -  which proves (surely) "high" among your key objectives - does it not?)

  • Hi,

    Just for your info, I would like to add this link:

    Best regards.

  • Hi Petrei,

    That's of course the "nom de plume" of Mr. Don Lancaster - he of TTL, CMOS, Video (even "Son of Cheap Video") (really) "Cookbook" fame.

    Always good to view your return - wish you well - and thanks for the neat reference.
  • "The idea is having the triangle wave in PWM gen compare with an incoming, processed sinewave (so the value is an ADC reading that goes through some processing) and if the triangle >sine, I would output high on PWMA and low otherwise."

    your code doesn't have ADC so there is no way that your code does what you want to do.

    plus, your use case is hard to understand. sounds almost like you are trying to recreate a sine wave that you already have.

    with that said, your code can be broken down to something like this:

    1. read the adc;
    2. generate a ramp;
    3. if adc > ramp, output high; otherwise, output low.

    you just need to code accordingly.

    numerous ways to do it:

    1) using an analog comparator, as already mentioned;
    2) using a digital comparator: adc vs. a single slope timer count (as a ramp);
    ...

    but you have to figure out what you are trying to do here. SPWM, as conventionally understood, isn't what you are trying to do here.
  • Oriana Wong said:
    I notice that  there are no Tivaware methods that allow me to set the PWM0CMPA values.

    Is this statement (your 2nd sentence, opening post) really true?      Might it - instead - be "offered up" by those enforcing,  "DRM and ONLY DRM" - as a Coding Vehicle?

    You should note that vendor's extensive API,  "Provides the "Source Code" (which rarely proved the case - here or elsewhere - in the near past.)

    Examination of this (relatively) obvious API Function:  "PWMPulseWidthSet()" - conflicts w/your quoted statement - does it not?

    void
    PWMPulseWidthSet(uint32_t ui32Base, uint32_t ui32PWMOut,
    uint32_t ui32Width)
    {
    .
    .
    .
    // Write to the appropriate registers.      We've "dropped down" to the final "code-block" - w/in this long-standing API function.
    //
    if(PWM_IS_OUTPUT_ODD(ui32PWMOut))
    {
    HWREG(ui32GenBase + PWM_O_X_CMPB) = ui32Reg;
    }
    else
    {
    HWREG(ui32GenBase + PWM_O_X_CMPA) = ui32Reg;
    }

    The expansiveness - and power - of  vendor's API should (most always) prove  "GREATLY to your benefit!"     (Even when - and especially when - its use is (unjustifiably) "blocked by others.")

    Nothing prevents those clever & resourceful,  "API User-Exploiters"  - from seriously reviewing the key Registers - referenced w/in their (proven & long verified)  "API Function Calls!"    Such proves - without doubt - the best means to gain,  "REAL MCU Competence & Mastery/Understanding!"      (while  freeing the user from (substantial excess time & effort)  and the production of  (always)  "very weakly" - or  (even)  "non-Tested"  DRM Code (adventures)...

  • Can the PWM timer not be the ramping signal? I believe SPWM is "generation of the desired output voltage by comparing the desired reference waveform (modulating signal) with a high-frequency triangular ‘carrier’ wave". I thought my approach is trying to do the same thing by using the PWM timer as the ramping signal and its comparators as the one that decides.

    Accuracy would be an output sine wave, after filtering, free of harmonics that would be the same magnitude as the sine wave I'm reading, but shifted in time or containing a phase shift.
    The time shift is the sine wave I'm reading, but shifted X number of degrees, so it won't be completely in phase with the sine wave I'm reading.
    I will be controlling either signals with a ratio, so I could modify the sine wave instead of the triangle wave height.

    For the Tivaware functions, you would set the duty cycle by setting the PWM pulse width, but I don't know my pulse width? It would depend on my comparisons, so I don't believe the PulseWidthSet method would work...

  • (Note to Danny - poster appears to be responding to my (series) of  (earlier) posts - although she's clicked upon yours...)

    You (O.P.)  appear (strongly) still wed to your original approach - even though - especially though - two here have now identified (substantial) weaknesses.

    Note that,  what you state now,  "Don't believe the "PulseWidthSet method would work" - strays FAR from your earlier,  "there are no Tivaware methods that allow me to set the PWM0CMPA values."    From where did that (mistaken),  "NO Tivaware methods" arise?     This goes to "coaching" - and when key questions (remain) unanswered - much extra effort falls upon your (hapless) helpers.    And motivation erodes!

    Oriana Wong said:
    I don't know my pulse width? It would depend on my comparisons, so I don't believe the PulseWidthSet method would work...

    Yet - is this thinking not  "circular?"     The source code - earlier highlighted for you - clearly described the effectiveness of  "PWMPulseWidthSet()" in setting your "PWM Comparator Values!"    Thus - if the "Comparator Values are known - so too - the PWM Pulse Width...

    There is (still) no indication of  the accuracy you (may) require  (such is of (some) importance - is it not) - and multiple questions - attempting to "tease out" such "reasoned data" - remain ... without response.

    And - if you'd be so good - do list the guidance provided by  "instructors/bosses"  - they must have provided some "steering" - is that not so?         (We don't  seek  to "deprive them"  from  (contributing) to your  (continuing) progress...)

  • I thought it useful to (further)  "make the case"  for  "bit more"  (i.e. (some)  participation from  those,  "Directing your project!"     Now you've tried hard to properly detail - yet the facts are  (pardon)  "dribbling out"  -  and sometimes -  not arriving - at all.

    You should know that  "Three here"  -  have  "independently" -  sensed this.      Follows 2 key comments from poster "Danny":

    • you have to figure out - what you are trying to do here

    • your use case is hard to understand

    Neither "Danny" - nor I - have communicated - yet our  "discomfort"  is common - and strongly  aligned!     (such  agreement  - much, "removes me" from my (sometimes) exclusive role of,  "the heavy!")    Over-exercising this forum resource - (rather predictably) occurs  -  under such  "loosely guided" conditions!     And serves to lessen the forum's intended,  "Delivery of Service."   

    No one blames you (the messenger)  yet  those "directing your efforts" should prove "more willing"  (i.e. "somewhat" willing)  to participate.    (either  "behind the scenes" - or here - and indeed ... both poster Danny and I - find ourselves "equally" busy - if not more so - than those "behind the scenes" actors...)

  • It does look like you are trying to use pwm to generate a sine wave. So your usage of spwm is correct, except that your understanding of it is incorrect.

    Essentially you convert the magnitude information of a sine wave to duty cycle. For example, at time 0, you run the pwm at 50 percent. At time 1, you run it at 51 percent,  ...

    By controlling how fast you update the duty cycle, you control the output frequency. By controlling the duty cycle changes per step, you control the amplitude.

    That's how spwm works.

    In some cases, however, you can simplify that by holding the output to 1 or 0, to reproduce a sine output. This approach can produce very fast waveform.

  • here is an example of implementing the 2nd approach mentioned above.

    the code runs on an AVR but the logic is the same for your TM4C:

    /lsb to msb, 8 bits * 4*8 = 256 points
    const uint8_t spwm_table[]={
    	254, 247, 191, 191, 247, 118,  91, 171,
    	170, 148,  68,  66,   8,   8,  64,   0,
    	  0,   1,  32,  32, 132,  72, 146,  82,
    	213, 218, 182, 123, 239, 239, 255, 253,
    
    };
    
    	while(1) {
                    //generate spwm
    		//send the current bit
    		if (spwm_table[spwm_index] & spwm_mask) IO_SET(SPWM_PORT, SPWM); else IO_CLR(SPWM_PORT, SPWM);
    		spwm_mask = spwm_mask << 1;			//advance the mask
    		if (spwm_mask == 0) {				//if all 8 bits have been sent
    			spwm_mask = 1<<0;				//reset the mask
    			spwm_index += 1;				//advance the byte index
    			if (spwm_index == 32) spwm_index = 0;
    		}
    	}
    

    Here is the output, both the digital and the analog equivalent:

    the advantage of this approach is its simplicity: no pwm modules needed. you simply output the pwm periodically, like from a timer ISR and the digital output will reproduce a waveform. the whole routine takes less than 40 instructions on an 8-bitter. less on a 32-bit chip.

  • Danny F said:
    your code doesn't have ADC so there is no way that your code does what you want to do.

    Has not poster's desire to:  "Sample & Recreate & Time-Shift"  her original,  "Incoming, Sine-Wave" - somehow - been discarded?

  • No, the responses gave me the answer on how to recreate the sine wave. I have the ideas for sampling and time-shifting.

    Thank you all for all the responses!
  • Wish you well - it is expected that you'll (soon) be in compliance w/(Arnold's noted phrase), "I'll (you'll) be back!"
    The absence of (any) error specification - or proper/detailed, "leader guidance" - is noted.     (and while "avoided"  (still)  disturbs...)