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.

TM4C123 PWM 0%/100% using PWMOutputInvert

Other Parts Discussed in Thread: TM4C123GH6PZ

Hello,

We are using the TM4C123GH6PZ in an application that requires the following 12 PWM channels to output 0%-100% (the other 4 channels are also used, but do not require the same type of control):

  • M0PWM0
  • M0PWM1
  • M0PWM2
  • M0PWM3
  • M1PWM0
  • M1PWM1
  • M1PWM2
  • M1PWM3
  • M1PWM4
  • M1PWM5
  • M1PWM6
  • M1PWM7

Each module is synced together, which has been working to date as we use it for triggering an ADC read. Each generator pair (i.e. M0PWM0/M0PWM1) is used such that if one channel is PWMing, the other is high (100%). We have been attempting to achieve the 100% with an inverted PWM channel with a width setting of 0, based on our interpretation of previous posters on the topic.

The problem is, it only sometimes works... other times we find that the 100% pin is actually a logic low (0%) at the output, despite no change in the observed register values for PWMxCMPA/PWMxCMPB/PWMINVERT. We have tried changing hardware, but the problem follows. Does anyone have any idea what is causing this issue, or what we are doing wrong? Can it be fixed, or do we need to toggle the pin between PWM and GPIO to achieve reliable 0%/100% operation?

I can attach more detailed code if required, but here is the specific section that seems to periodically fail (the last section, where we invert the 0%)


		PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT | PWM_OUT_2_BIT | PWM_OUT_3_BIT, false);
		PWMOutputState(PWM1_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT | PWM_OUT_2_BIT | PWM_OUT_3_BIT |
									PWM_OUT_4_BIT | PWM_OUT_5_BIT | PWM_OUT_6_BIT | PWM_OUT_7_BIT, false);

		PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, 0);
		PWMPulseWidthSet(PWM0_BASE, PWM_OUT_2, 0);
		PWMPulseWidthSet(PWM1_BASE, PWM_OUT_0, 0);
		PWMPulseWidthSet(PWM1_BASE, PWM_OUT_2, 0);
		PWMPulseWidthSet(PWM1_BASE, PWM_OUT_4, 0);
		PWMPulseWidthSet(PWM1_BASE, PWM_OUT_6, 0);

		PWMOutputInvert(PWM0_BASE, PWM_OUT_0, false);
		PWMOutputInvert(PWM0_BASE, PWM_OUT_2, false);
		PWMOutputInvert(PWM1_BASE, PWM_OUT_0, false);
		PWMOutputInvert(PWM1_BASE, PWM_OUT_2, false);
		PWMOutputInvert(PWM1_BASE, PWM_OUT_4, false);
		PWMOutputInvert(PWM1_BASE, PWM_OUT_6, false);

		PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1, 0);
		PWMPulseWidthSet(PWM0_BASE, PWM_OUT_3, 0);
		PWMPulseWidthSet(PWM1_BASE, PWM_OUT_1, 0);
		PWMPulseWidthSet(PWM1_BASE, PWM_OUT_3, 0);
		PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, 0);
		PWMPulseWidthSet(PWM1_BASE, PWM_OUT_7, 0);

		PWMOutputInvert(PWM0_BASE, PWM_OUT_1, false);
		PWMOutputInvert(PWM0_BASE, PWM_OUT_3, false);
		PWMOutputInvert(PWM1_BASE, PWM_OUT_1, false);
		PWMOutputInvert(PWM1_BASE, PWM_OUT_3, false);
		PWMOutputInvert(PWM1_BASE, PWM_OUT_5, false);
		PWMOutputInvert(PWM1_BASE, PWM_OUT_7, false);

		PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT | PWM_OUT_2_BIT | PWM_OUT_3_BIT, true);
		PWMOutputState(PWM1_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT | PWM_OUT_2_BIT | PWM_OUT_3_BIT |
									PWM_OUT_4_BIT | PWM_OUT_5_BIT | PWM_OUT_6_BIT | PWM_OUT_7_BIT, true);

		delayMS(1);

		PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT | PWM_OUT_2_BIT | PWM_OUT_3_BIT, false);
		PWMOutputState(PWM1_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT | PWM_OUT_2_BIT | PWM_OUT_3_BIT |
									PWM_OUT_4_BIT | PWM_OUT_5_BIT | PWM_OUT_6_BIT | PWM_OUT_7_BIT, false);

		PWMOutputInvert(PWM0_BASE, PWM_OUT_1, true);
		PWMOutputInvert(PWM0_BASE, PWM_OUT_3, true);
		PWMOutputInvert(PWM1_BASE, PWM_OUT_1, true);
		PWMOutputInvert(PWM1_BASE, PWM_OUT_3, true);
		PWMOutputInvert(PWM1_BASE, PWM_OUT_5, true);
		PWMOutputInvert(PWM1_BASE, PWM_OUT_7, true);

		PWMOutputInvert(PWM0_BASE, PWM_OUT_0, true);
		PWMOutputInvert(PWM0_BASE, PWM_OUT_2, true);
		PWMOutputInvert(PWM1_BASE, PWM_OUT_0, true);
		PWMOutputInvert(PWM1_BASE, PWM_OUT_2, true);
		PWMOutputInvert(PWM1_BASE, PWM_OUT_4, true);
		PWMOutputInvert(PWM1_BASE, PWM_OUT_6, true);

		PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT | PWM_OUT_2_BIT | PWM_OUT_3_BIT, true);
		PWMOutputState(PWM1_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT | PWM_OUT_2_BIT | PWM_OUT_3_BIT |
									PWM_OUT_4_BIT | PWM_OUT_5_BIT | PWM_OUT_6_BIT | PWM_OUT_7_BIT, true);

Following this bit of code, the intended PWM channel on each generator begins PWMing at a fixed duty cycle (this always works), while the other is supposed to stay high (this does not always work).

Note that we have tried alternate approaches as well, to no improvement or worse results, such as:

1) Setting the pulse width higher than the load, based on this piece from the datasheet:

Section 20.3.3 on PWM Comparators

"If either comparator match value is greater than the counter load value, then that comparator never outputs a High pulse."

2) Leaving the PWM signals always inverted (never attempting to drive true 0%, but rather something between X% and 100%)

Appreciate any help! Thanks in advance.

  • This has long been recognized as an "issue" w/ARM MCUs - from this vendor and others.   As you report having read (some) PWM-based posts here - it's bit surprising that you did not encounter this known, "PWM difficulty @ extremes " effect..

    The effect manifests - just as you report - when attempting to reach (and sustain) PWM duties of 0 or 100%.  In our BLDC motor control world - we've succeeded with 98% PWM Duty - and disabling the PWM when 0 duty is required.   (we drive high-power FETs via strong gate-driver ICs)

    Best work-around my small firm has found is to switch the PWM pin from PWM into GPIO - and then output either "1" or "0."   The switch-over can be achieved w/in a few µS - in most cases we've found that acceptable.   Another means (a faster one) is to gate the PWM output with a spare GPIO - although with the number of channels you employ - this will surely qualify as a, "Pin eater."

    Will your application (really) note (and suffer) from a PWM range of 2-98% duty?   (most do not - ours surely does not - especially the lack of "full 100%")   A case can be made for wanting the PWM output "Quiet" - and that is quickly/easily arranged via an existing API function call which disables channel PWM output...

  • It's not particularly an ARM issue although it might be related to using general purpose timers to implement PWM. Certainly I've seen other micros that could not drive to 100%. The usual solution is as you say, change to GP output although timing could be an issue. I'm surprised it's an issue though in a modern PWM peripheral and I can certainly see why it would be important to fix in some cases.

    Robert
  • Another option in some cases is to increase the period so the off becomes a vanishingly small portion of the output. I think that's not an option for the OP though.

    Robert
  • Do note that I made no attempt to "confine" this issue just to ARM - yet it appears on each/every ARM MCU my firm employs - from 5 different vendors.   As I recall we suffered similar PWM limitations (as you noted) on earlier, non-ARM MCUs - as well.

    With so many PWM channels "in play" - and should 0 & 100% duty be a "real" requirement - use of an FPGA which proves ideal for multi-channel, well-synced, PWM outputs - stands at the ready.

    Poster Robert expressed "surprise" that this issue has lingered (and as that's true across most all ARM vendors) this reporter believes that the "fix" may prove too demanding/costly and/or lead to (yet) other issues... Caveat emptor...

  • No, but it did have the appearance of confining.

    I think, on reflection, I'm more surprised the invert of width zero doesn't work. That should just be an XOR gate on the output, which rather suggests zero isn't reliable either.

    Robert
  • As far as a fix being difficult, I have used 16 bit processor with as flexible timers that managed the task. I think this vendor's DSPs manage that as well but I'm less certain. My memory of working with an ARM7 micro was it managed100% as well but it's been a little while. I do remember doing something that produced a very narrow glitch on that processor.

    Robert
  • Thanks for the offerings guys. We are still a bit confused as to why a pulse width of 0 seems to work fine, but inverting it does not. However, I stumbled on a little trick in the datasheet that fixed our problem without having to resort to GP output toggling, which would have no doubt had its own headaches.

    What we have implemented today is to leave the pins configured as PWM, but leave them disabled (PWMOutputState = false), which will still output a logic level signal at the respective pin. Then, we can toggle this value to be either a logic low (default) or logic high (using the PWMOutputInvert API function), effectively gaining 0% and 100%. The huge benefit for us is that we maintain our synchronization between channels, which may not have been possible had we toggled to GPIO (not to mention it makes the code cleaner).

    The code has been running successfully all day here, and so unless something unforeseen happens, I'm considering this closed!

  • I'd not (fully) hoist the Jolly Roger until you've confirmed that output source & sink (both) are "proper" (adequate) under the (unusual) condition you propose.

    It would be neat if vendor's Amit would "bless" your action - your method is "outside of normal" and as such may not persist under future updates/upgrades to the API - or across new MCU revisions.

    As to sync - there are standard API commands which well manage that - and such "recognized use" of the API is guaranteed to work.  (both now & into the future...)

  • That may be a useful tip for others. It looks like a relatively clean solution.

    I would like to know why the invert doesn't work. I'm somewhat troubled by that.

    Robert
  • cb1,

    While it is always nice to get vendor feedback, I think for this application it is not needed. This use is not "outside of normal" or (unusual) as you imply. In fact, it is taken directly from the datasheet:

    We have verified in our application that we see the appropriate logic signal at the output in both conditions, in line with the datasheet.

  • Here's the "method" behind my (apparent) "seek the approval of the vendor" - madness:

    (Truth in advertising - I had not noted the "fine detail" you've provided.   That certainly aids your case - and Bravo for your "find" and presentation here.)

    Now here (remains) my concern:

    • ARM MCUs (and non-ARM) as well have "long" suffered w/this "PWM at extreme" issue.   To my knowledge (and poster Robert's) no such "fix" had been found/posted/propagated!   (thus my small tech firm "never" allows such extreme PWM settings - and has successfully shipped > 25K MCU-based boards in that manner)
    • You are aware of "errata" which arrives w/each/every MCU introduction.   (all vendors!)   As I've past worked for another semi-giant - the "more usual the function" - the more likely is it to be exhaustively tested.  Any report of "working for hours/days" w/out vendor "sign-off" is (pardon) anecdotal...
    • My firm works fairly regularly w/finance/venture firms.   Always - during the "should we fund" process - the use of such "non-standard" techniques is challenged.   Indeed they may be innovative - provide competitive advantage - but they also impose risk!   Guys/gals - smart enough to invest millions - favor caution - that was my "pass away" to you/others...   (small firm's test/verify "rarely" carries much weight!)

    Do not confuse the suggestion of "VENDOR sign-off" w/lack of admiration for an (apparently) new idea.   Having (perhaps) bit more experience than many here - I try to add value by presenting a, "bigger picture."   (as best I know it...)   (much of that picture stems from my past co-founding - then taking tech firm public - and navigating all of those (very) intense tech/business/legal hurdles...)