Faking PWM on non-PWM pins

So I was just thinking about how PWM via analogWrite works by putting out a square wave that just varies in duty cycle where 0 = 0% and 255 = 100%

How is this different than two digitalWrite functions with appropriate delayMicroseconds?
Uno has pins (not 5 and 6) running about 490Hz (490 cycles per second), which means it's 1 cycle per 2040microseconds.

What, then, is the difference in outcome between:

analogWrite(3, 128); for 50% duty cycle, meaning for every 2040microsecond period, it's on for half and off the other half

and

digitalWrite(3, HIGH);
delayMicroseconds(1020);
digitalWrite(3, LOW);
delayMicroseconds(1020);

it is not different. However, with PWM pins you can do other things... doing it that way if you read for inputs, do a serial print or whatever, your timing of the PWM will be distorted.

Because you can't do anything else inside the delays. You can't even detect if a button was pressed.

The PWM pins use dedicated hardware modules that run independently of the main processor. You just set them going and update as needed.

You can use millis() or micros() to manage your “fake” PWM without blocking. See the demo Several Things at a Time

You need to be aware that micros() updates at 4 µsec intervals - so 4, 8, 12, 16 etc

…R

As @MorganS states, PWM pins is basically set and forget (till you need to update it).

void setup()
{
  analogWrite(3, 128);
}

void loop()
{
}

A connected LED will be ON at 'half' intensity. You can't do that with a software PWM implementation.

Sure you can. That's what I did in the first post.
Both will cause the led to be at half intensity.

INTP:
Sure you can. That's what I did in the first post.
Both will cause the led to be at half intensity.

He means that you can't set it then forget it. Note that he sets the LED to half intensity in 'setup()', then needs no other code in 'loop()' for it to stay that way.

For your code to keep a LED at half intensity, it's all that can be in 'loop()' and you can't do anything else.
Edit: If you really want to do it the hard way, while doing other things, take a look at reply #3.

INTP:
Sure you can. That's what I did in the first post.
Both will cause the led to be at half intensity.

Sorry for being unclear.

OldSteve:
He means that you can't set it then forget it. Note that he sets the LED to half intensity in 'setup()', then needs no other code in 'loop()' for it to stay that way.

For your code to keep a LED at half intensity, it's all that can be in 'loop()' and you can't do anything else.

Thanks for the further explanation.

INTP:
How is this different than two digitalWrite functions with appropriate delayMicroseconds?

One is done in hardware, the other in software. The hardware one lets you do other things, the software one doesn't.

As addressed, the hugely maligned delay function can be done away with.

My prompt is exploratory in nature, learning about what makes the Arduino do what it does, finding its limits, etc.
The non-PWM pins are then capable of PWM behavior, and analogWrite could just be a simulated with a function.
Hearing that the PWM pins have different hardware is the kind of insight I was looking for.

My original question perhaps could be rephrased as, what really is the difference between non-PWM pins and PWM pins if they are both capable of 490Hz write frequency?

Hmm, that means it has a switching speed limit of once every 2040microeconds, which means my earlier math was wrong. It isn’t on and off 50/50 within a 2040microsecond period, it has to be either on or off for entire 2040microsecond windows. Right? So it isn’t literally 50% duty cycle = 1 cycle on, 1 cycle off. There’d be nowhere to go for less than 50%. It must pick some amount of cycles, maybe 100? so it can subdivide.

INTP:
It isn't on and off 50/50 within a 2040microsecond period, it has to be either on or off for entire 2040microsecond windows. Right? So it isn't literally 50% duty cycle = 1 cycle on, 1 cycle off. There'd be nowhere to go for less than 50%. It must pick some amount of cycles, maybe 100? so it can subdivide.

What?

Look, with a frequency of 490 Hz, and a 50% duty cycle, therefore it is on for 1020 µs and off for 1020 µs.

A dutycycle of 50% means on during 50% of the ONE cycle, off during 50% of that same ONE cycle :wink:

I could be misunderstanding what pin frequency means.
I'm assuming 490Hz means it can write to the pin at most 490 times in a second. Is that correct so far?

(This may be a long back and forth with this 5min limit I have)

Look at the blink example which switches a led on for 1 second followed by off for one second. The total period of a cycle is 2 seconds resulting in a frequency of 0.5 Hz.

INTP:
My original question perhaps could be rephrased as, what really is the difference between non-PWM pins and PWM pins if they are both capable of 490Hz write frequency?

Even with BlinkWithoutDelay style code once you start adding other code it becomes more difficult to achieve a reasonably fast PWM frequency. That's not an issue with hardware PWM. digitalWrite() is very slow so it's not well suited for this application. If you want to look at some efficient software PWM code check out this library: GitHub - Palatis/arduino-softpwm: Software PWM library for Arduino. I can run 15 independent PWM channels at around 130Hz while doing a decent amount of lighting effects processing and polling inputs on an ATmega168 @8MHz. That's barely acceptable for LEDs(visible flicker getting fairly noticeable). Using hardware PWM would be much nicer but it's fun to see what the MCU can do on its own.

INTP:
My original question perhaps could be rephrased as, what really is the difference between non-PWM pins and PWM pins if they are both capable of 490Hz write frequency?

The hardware PWM is more accurate, and requires 0 computational overhead when it's running. They are based entirely off the Timer peripherals and their output compare accessories. These peripherals are specifically designed for the purpose of waveform generation. In summary, there is a counter (the Uno's chip, ATmega328P, has 2) that counts up from 0 to 255, then rolls over to 0 and starts again, with each count being done from the 16 MHz oscillator that's clocking the whole microcontroller (this frequency can be divided by powers of 2 by the prescaler, which is how the PWM frequency of Arduino is so low). An output comparison register is internally wired to a specific pin to control it. This is why analogWrite can only be used on a small number of pins: those are the pins that have an output compare register hooked up to them. A value between 0 and 255 can be written to the compare register. When the counter value is below the output compare value, the associate pin is HIGH. When it goes above the compare value, the pin is flipped LOW. It resets back to HIGH when the timer rolls over. Once all the registers are properly set up and running, everything happens automatically with no intervention needed from the CPU.

The software version has to account for the the computation required to upkeep it. Writing the pin alone using arduino's function takes a few microseconds on it's own, and if you use a non-blocking implementation reading micros() and comparing it to the timestamp are not free either. You also have to account for the other computation of your code as well: if you code is not at the PWM spot in the code exactly when it needs to be there will be jitter and your PWM output will be unstable.

For something like a heating element in an oven that has a switching period of many seconds, a software implementation is fine. In that case the switching occurs so slowly most people probably don't really think of it as PWM, even though it is. For something like an LED like an LED that will require at least a couple hundred hertz to average out vision persistence, the hardware peripheral is better. And motors may require many kilohertz PWM frequency. You are absolutely not going to do that in software.