Possible to call analogWrite() too often?

I wanted to check a bit about how analogWrite() actually works. I understand that it sets up PWM timers on any of pins 3,5,6,9,10 or 11 and that these are off for a proportion of a period and on for another proportion. When this switching occurs is determined by the analog level (out of 255) and also affected by whether fast, phase correct or some other PWM mode is used. It also acts differently on different pins, especially 5 and 6 which share a clock with millis() and delay() and run at, by default, twice the PWM frequency of the others.

What happens though if you call analogWrite(SamePin,SameAnalogVal); at very great frequency though? Does each call to it restart the PWM timing cycle, such that if the cycle were only halfway through after the first analogWrite happened then the cycle would be restarted by the next analogWrite(SamePin, SameAnalogVal); ? Or would the code which makes up the analogWrite() function recognise that it had been called twice in quick succession on the same pin with the same analog output value and effectively ignore this econd call and just let the first one's PWM cycles keep running?

I'm not sure whether, upon calling analogWrite() the pin initially is low or high, but if for the point of this argument we assume it is low and we have a duty cycle of maybe 25% high (64/255) then for the first 75% of a cycle it will be low. If analogWrite with the same pin and analog value got called again before 50% of the cycle had completed, and then kept being called with a time between calls of approximately half the period of the PWM cycle, would it keep the pin low and restart the cycle each time, hence never letting the pin get to the high 25% at the end of it's PWM cycle? if infact PWm pins are initially high when the cycle starts then this pin would spend 25% of the cycle high, followed by another 25% low but then at that point would calling analogWrite() again send the pin high again, hence giving a 50% (analog 128/255) duty cycle even though you had specified 25%?

Some of this could be mitigated by putting use of analogWrite (for a specific pin to reduce the amount of typing I do in this post) , assuming you will for various reasons be calling it very often, into function such as:

byte OldVal=0; //global variable
void AlternativeAnalogWrite(byte Val){
if(Val != OldVal){
analogWrite(PIN, Val);
OldVal=Val;
}else{
//do nothing
}
}

But is use of such a function actually needed?

What about in cases where a different analog value was supplied in subsequent uses of analogWrite? Such a function as above couldn't protect you unless analoWrite itself contains protections to ensure that giving it a new value doesn't immediately restart the PWM cycle counter?

P.S. if it helps you simplify your answers you can ignore pins 5 and 6 and just discuss 3,9,10 and 11 as, due to the way 5 and 6 can alter the millis() and delay() functions I tend to stick to the 3,11 and 9,10 pairs of PWM pins when designing things.

Thanks

1. When we execute this instruction: analogWrite(3, 100);, a free running signal of 500 Hz frequency and 0.78 ms ON-period appears at DPin-3 of UNO. There is no need for recurrent execution of the analogWrite() instruction.

2. The frequency is fixed; we can only change the duty cycle (the ON/OFF-period) of the output signal by changing the 2nd argument of the instruction: analogWrite(DPin, dutyCycle);. Now, the question is how and when this change takes place in the wave.
(1) It is intuitively correct that the duty cycle should not (cannot be) be changing until the current period of the wave is completed.

(2) The new value of the duty cycle is written in the relevant buffer register at the time the instruction (analogWrite(arg1, arg2) ; ) is executed. The new value is transferred to the actual compare register just at the beginning of the next cycle of the wave. The wave maintains the same frequency and the updated duty cycle from this cycle to onward.

3. By the way, I would like to expose you to the concept of 'Re-triggerable One Shot' to get an idea of how new duty cycle comes into action for the wave of Step-1.
(1) In the case of re-triggerable One Shot, the output goes to High State for a (Say, 100 us) programmed time when the trigger signal appears. After the elapse of 100 us time, the output will become at Low State and again assumes High State when the next trigger pulse appears.

(2) What will happen if the trigger pulse appears before the exhaust of 100 us time period? Assume that the trigger pulse has arrived when the output signal has just crossed 60 us time. In this case, the output signal stretches for another 100 us excluding the elapsed 60 us. As a result, the current ON-period of the One Shot is 160 us. This re-triggering feature of the One Shot is widely used to detect power failure where zero-crossing points of the supply line repeatedly triggers the One Shot provided that the ''separation time between two zero crossing points' is less than the one shot period.

This sounds like a job for an oscilloscope.

GolamMostafa:
There is no need for recurrent execution of the analogWrite() instruction.

I don't think OP is saying there is a need, but rather that the nature of loop() is that we do call analogWrite() over and over.

GolamMostafa:
(analogWrite(arg1, arg2) :wink: is executed.

Code tags....

edit... once again you fixed something while I was typing.

elvon_blunden:
This sounds like a job for an oscilloscope.

I don't think OP is saying there is a need, but rather that the nature of loop() is that we do call analogWrite() over and over.

Code tags....

edit... once again you fixed something while I was typing.

1. First, the theory is to be accepted and then the verification where an oscilloscope plays a great role.

2. I have said it from my own as there is a misconception that the instruction has to be executed again and again to sustain the wave; rather, it is maintained by interrupts under the control of the hidden codes of Arduino.

3. A year ago, I learnt the trick from @AWOL and now forgotten; now, I use an extra space.

4. We do edit to fix something; but, it is the net buffer delay; we have to be considerate.

When you call analogWrite() all it does is update the value in the Timer register that controls the width of the pulse. It does not restart the Timer.

You can call it as often as you like.

...R

Robin2:
You can call it as often as you like.

You can call it as often as you like with new value for the duty cycle.

Each call programs the timer for the PWM mode, but on 2nd and subsequent calls this has no
effect and thus doesn't disturb the PWM at all. Only the update of the OCR register is new, and
that is latched by the hardware for glitch-free operation of the timer hardware.

If you pass values to the PWM hardware faster than the PWM cycle rate you'll simply drop samples.

You can, after the first call to analogWrite for a particular pin, just update the relevant OCR register
directly if you want.

GolamMostafa:
You can call it as often as you like with new value for the duty cycle.

How else would one call analogWrite() ?

...R

Robin2:
How else would one call analogWrite() ?

We (me and my co-workers) may call it as often as we like with new value for the duty cycle and not faster than the frequency of the PWM wave.

GolamMostafa:
You can call it as often as you like with new value for the duty cycle.

Or with the same value, nothing wrong with that, it’s not illegal...just wont do much but use cycles

I have never thought that you needed to call analogWrite many times to give an analog voltage. I understand that when you call analogWrite(3,128) you'll get a 50% duty cycle on pin 3 until you tell it otherwise. But I'm thinking of situations where you main loop runs often, checks various sensor data and communication inputs, then on each loop adjusts the analogWrite(3,X) value based on communications sent to the arduino, sensor data and results of onboard calculations combining sensor data and comms info. If the loop which checks comms/sensors/calculations runs faster than the period of a PWM cycle and calls analogWrite(3,X) at the end each time will even a single PWM cycle ever complete properly?

Am I right in thinking that your answers say that when analogWrite(3,X) is initially set nothing happens except that the value of a register in hardware is changed to reflect the new X value, then when the prior PWM cycle has completed the next PWM cycle will be staretd using whatever that hardware register now holds? Or are you saying that as soon as analogWrite(3,X) is called a new duty cycle is set and the cycle is restarted from the start even if the prior one hasn't finished yet?

Can anyone provide me with what the code inside of analogWrite actually looks like? I can't seem to find it on google but my search terms might not have ben the best choices.

I think an oscilloscope will me the easiest way for me to gather certainty as to what is happening in ths situation. Thanks.

Can anyone provide me with what the code inside of analogWrite actually looks like?

It is on your computer; part of the Arduino installation.

Source code is here in github

Brief explanation of hardware PWM:

There are a few modules in that are called counters. a counter stores a numerical value, and when the counter receives a digital pulse its value increases by one. When a counter is given regular inputs at equal time intervals, it becomes a timer (counting the passage of time). All of the Arduino's counters are set up as timers before your code even begins to run.

These timers also have an additional function attached to them called Output Compare, and certain pins are dedicated to this function. Each pin has an output compare register (called OCRXX depending on which timer and pin it is for) associated with it, which you can load a number into. When the timer value is less than the OCR value, the pin is HIGH. When the counter value is higher than OCR, the pin is automatically set low.

It's important to know that these timers are always running, because that's how the Arduino core sets them up at the beginning of the program. The analogWrite function does not change the function of the timer itself; it just loads the value into the appropriate OCR register and connects the pin to the OCR register.

Am I right in thinking that your answers say that when analogWrite(3,X) is initially set nothing happens except that the value of a register in hardware is changed to reflect the new X value, then when the prior PWM cycle has completed the next PWM cycle will be staretd using whatever that hardware register now holds?

This is correct. Quoted from the datasheet (empahsis mine):

The OCR0x Registers are double buffered when using any of the Pulse Width Modulation (PWM) modes. When double buffering is enabled, the CPU has access to the OCR0x Buffer Register. The double buffering synchronizes the update of the OCR0x Compare Registers to either top or bottom of the counting sequence. The synchronization prevents the occurrence of odd-length, non-symmetrical PWM pulses, thereby making the output glitch-free.

If you want to dive into the nitty-gritty of how an Arduino works under the hoop, I highly encourage you to download the ATmega328P datasheet and give it a look through. It explains every aspect of the chip's functionality in far more detail then you ever though you would need.

But I'm thinking of situations where you main loop runs often, checks various sensor data and communication inputs, then on each loop adjusts the analogWrite(3,X) value based on communications sent to the arduino, sensor data and results of onboard calculations combining sensor data and comms info. If the loop which checks comms/sensors/calculations runs faster than the period of a PWM cycle and calls analogWrite(3,X) at the end each time will even a single PWM cycle ever complete properly?

If you are doing this, it is bad design. The nature of PWM means that it takes time to adjust to changes in input, so it should only be used for inputs that change slowly, with a frequency several time lower than the PWM frequency. Even though it technically works, you should never update a PWM signal multiple times per period. If you think you need to do that, it's a sign that you either need to speed up the PWM so it's faster than your signal or use a proper digital-analog converter.

Jiggy-Ninja, thanks for clarifying that. It makes sense to me now. By the way, just out of interest, does that timer system, especially the output compare part of it, rely on using interupts of any kind or not? That is to say, for future reference, would PWM fail in the case of long sections of code contained in atomic blocks.

You'll have plenty of details in Secrets of Arduino PWM

if you want to find the answer to your question, best is to try out:

  • Hook up a LED with an appropriate current limiting resistance to a PWM pin (LEDpin)
  • in the setup()
  • put LEDpin HIGH for a few sec to see its full brightness
  • do a analogWrite(LEDpin, 64) on that PWM pin so that the LED is "1/4 bright"
  • add a delay for 3 seconds (to see the LED in that mode)
  • then disable interrupts
    keep the loop empty and see what happens :slight_smile:
const byte LEDPin = 6; 

void setup() {
  Serial.begin(115200);
  pinMode(LEDPin, OUTPUT);
  Serial.println("FULL BRIGHTNESS");
  digitalWrite(LEDPin, HIGH);
  delay(3000);
  Serial.println("1/4 BRIGHTNESS");
  analogWrite(LEDPin, 64);
  delay(3000);
  Serial.println("No Interrupts");
  delay(10); // for print to work
  noInterrupts();
  Serial.println("THIS WON'T PRINT IN FULL AS No Interrupts IS ACTIVE");
}

void loop() {}