Questions about PWM

The posted specs on the front page and the documentation disagree on this in three different places, so I figure I’ll ask here.

How many PWM outputs does the Core have?

The homepage seems to suggest the original 4 PWMs, but the rest of the Docs say 8 PWMs: A0, A1, A4, A5, A6, A7, D0 and D1.

The Docs are wrong about:
You do not need to call pinMode() to set the pin as an output before calling analogWrite().

You in fact do need to set each pin you intend to use as a PWM as pinMode(pin,OUTPUT); or it will not work.

I just tested this code on my core with my oscilloscope and it’s working on all 8 mentioned outputs:

int analogPin = A2; // potentiometer connected to analog pin A2
int val = 0;        // variable to store the read value

void setup()
{
  pinMode(A0, OUTPUT);  // sets the pin as output
  pinMode(A1, OUTPUT);  // sets the pin as output
  pinMode(A4, OUTPUT);  // sets the pin as output
  pinMode(A5, OUTPUT);  // sets the pin as output
  pinMode(A6, OUTPUT);  // sets the pin as output
  pinMode(A7, OUTPUT);  // sets the pin as output
  pinMode(D0, OUTPUT);  // sets the pin as output
  pinMode(D1, OUTPUT);  // sets the pin as output
}

void loop()
{
  // analogRead values go from 0 to 4095, analogWrite values from 0 to 255
  // read the input pin (0-4095) and scale it to 0-255 by dividing by 16.
  val = analogRead(analogPin) / 16;  
  // Write newly scaled value to A0, A1, A4, A5, A6, A7, D0 and D1.
  analogWrite(A0, val);  
  analogWrite(A1, val);
  analogWrite(A4, val);
  analogWrite(A5, val);
  analogWrite(A6, val);
  analogWrite(A7, val);
  analogWrite(D0, val);
  analogWrite(D1, val);
  delay(10); // wait 10 milliseconds
}

There is one thing I noticed which I didn’t like to see. First of all, the PWM in the code above is being set via analogWrite() about every 15ms. 10ms in the code + 5ms of background tasks before loop() executes again. As you can see here on the scope there appears to be a glitch every 15ms:

It’s happening when the PWM is set, and it’s because the PWM output is forcibly reset each and every time it’s written. Most PWM outputs have a way to write to a register with a new duty cycle value which gets picked up on the very next rising edge, not asynchronously whenever it’s written. The result is you’ll have little glitches that may or may not be noticeable… but this is something that should be looked at for improvement.

3 Likes

Fixed the # of PWM pins mentioned on the front page, and fixed the docs as well.

@BDub regarding the point you’ve raised, I’ll add that to our backlog to fix.

2 Likes

Awesome!

Good data drives good design decisions. :smile:

2 Likes

A simple low-pass RC filter will clean that PWM glitching right up. Ultimately it should be investigated, but for any real use you’ll need to filter it anyway.

I guess that depends on what you are using it for and how fast you intend to update it. The STM32 has so many freaking features though, if it’s not just an option that can be turned on I’d be surprised :wink:

1 Like

Haha, yeah, I’d be surprised too. I’ve been meaning to crack into the full datasheet for the STM32; ideally I’d print a copy out to read, but I don’t know where I can buy the five gallon drum of ink that would be required for that. :wink:

Anyway, I guess I meant for things that don’t require a lot of precision, that glitching shouldn’t be that big of a deal. If you were using it instead of a DAC then you’d want to have a lowpass RC filter on it, for sure.

I really wish more microcontrollers would implement at least a couple of real DAC channels. It would just make my life easier all around!

Haha, you guys seriously make me smile. If you find a magical register bit we need to twiddle to change duty cycle only on an edge, of course, let us know. :smile:

I think to remember that AVRs had a register to tell the chip to transfer the new counter compare value only on starting edge, too.
And I would be surprised, if the STM32 would not, but I seem to be unable to find a propper datasheet of that chip that actually deals with the ins and outs of timer programming on that chip.
The datasheet on your GitHub and the datasheets I found on ST home page merely seem to state that there are timers with different modes and that there are registers for setting them, but not a lot about how and what the different modes do and if there are any “sub-modes” - which would be were I’d be looking for that particular feature.

I’d be happy to plow through that kind of stuff, to tweak some more out of that chip.

Meanwhile a possible solution for the PWM glitch, would be to transfer the setup of the new PWM counter compare into an interrupt service routine that gets triggert on next counter over-/underrun.

@timb: While it would normally not be a big issue, since it can be filtered, it might become a bigger issue when the analogWrite action occures often enough - e.g analogWrite(128) at 1000/sec could actually cause a puls of almost 100% since the counter gets retriggered just before the puls should have ended and thus will stay on permanenly.

After I’ve done some reading, I came to the conclusion, that this time glitch whenever the PWM ratio is set new does not actually root in the STM32, but in the way analogWrite() does it.

Normally you’d only need to write a new value into the Capture/CompareRegister TIMx->CCR1, which needn’t interfere with the currently running timer, and the current timer count would remain unchanged, keeping the PWM periode intact.
Either the CapCom has already triggered and counter will finish with the old PWM ratio or it has not and the CapCom will trigger when the new value is reached - if newVal >> oldVal - producing the new PWM ratio, or it will trigger immediately - if newVal < currentCount - producing one periode of intermediate PWM ratio.

But since the current implementation of analogWriter() seems to inevitably do the whole timer setup, including periode configuration and counter initialisation, each time it’s called, the running timer count gets compromised resulting in one incomplete PWM periode.

At least this is what I think what happens (having had only a quick glimps at spark_wiring.cpp ;-))

1 Like

@ScruffR yes that’s exactly what’s happening, good catch!
https://github.com/spark/core-firmware/blob/master/src/spark_wiring.cpp#L415

I think either by checking to see if that particular timer is already enabled or a global state variable, and then just updating the duty cycle register… it would likely fix the glitch.

Hello,

I can see that the PWM frequency is 500 Hz like in Arduino. I wonder if it could be changed to much higher like around 64 kHz? I saw an interesting circuit in an old Elektor where an Arduino was used as an adjustable power supply with a help of power mosfet.

@Kimmo why yes it can! See my post here and change the PWM_FREQ define to 64000. I just tested it and it works perfectly!

As for an application as a power supply, I would be vary careful with software controlled MOSFETs that deliver any real power. You just have to think of how to handle all of the possible failure cases. Good luck and let us know how it turns out! :slight_smile: