1

I think I've scoured the SE for questions about PWM, and fast digitalWrite etc, but I didn't see this point addressed.

I need to use direct writes to some digital pins. Some of the pins on the port are using analogWrite/PWM, though not the pins I'm accessing digitally.

In the digitalWrite function the Arduino core checks to see if PWM is in use and disables it if a digital write is attempted. Since the pins here are isolated -- no digital writes to PWM pins -- is this a needed step?

The reason I ask is that when accessing a port directly, as in PORTD |= (PORTD & mask) | somebit, you are in fact writing to the pins being used for PWM, you just aren't writing changed bits.

Am I overthinking this? Is it 'PWM-safe' to use direct port outputs on the neighbor pins?

  • Hint: Why not use a library for that? For instance, github.com/mikaelpatel/Arduino-GPIO – Mikael Patel Dec 31 '18 at 13:52
  • @MikaelPatel - I'll look at that, thanks. I don't expect there to be any magic there, but I'm always happy to be surprised. – Jim Mack Dec 31 '18 at 15:47
2

A timer interrupt will be periodically toggling the PWM bit and might do so between your port read and port write.

The easy solution is to turn off interrupts during the read/modify/write portion of your port update sequence. This could cause a stretched PWM cycle. If you don't turn it off, you'd just glitch the PWM - creating an extra, short cycle. Which one is worse for your PWM recipient?

A better solution might be to to watch the port for the PWM bit to change, then jump in right afterward and make your port update. You could do his in either a blocking or a non-blocking manner.

Update:

Watching the PWM bits seems as if it would be fairly slow, which somewhat defeats the purpose of the direct port writes.

Yes, it's less than ideal. There's a tradeoff here. Can your PWM consumer tolerate the potential occasional glitch, instead?

To be complete, there are a number of motivations to use direct I/O:

  • Smaller code;
  • Less I/O skew with multiple I/Os, or zero skew if the I/Os are in the same port;
  • Less execution time on a time-critical path;
  • Precise output timing;

among others. Those software solutions won't help much in any of those cases.

Can you reassign your output to another pin on a different port, avoiding the whole problem? That might be better than any of the "solutions". Adding hardware might accomplish the same thing - perhaps moving just the PWM to an ATtiny, for example.

Update 2:

This seems so baked into the structure of the Arduino... does digitalWrite() itself actually overcome this?

It "addresses" it - overcomes it? As is so often the case, "that depends".

Here is the the source-code to digitalWrite() from: /Applications/Arduino.app/Contents/Java/hardware/arduino/avr/cores/arduino /wiring_digital.c (on a Mac; the top-level folder or two will be different on Windows):

void digitalWrite(uint8_t pin, uint8_t val)
{
    uint8_t timer = digitalPinToTimer(pin);
    uint8_t bit = digitalPinToBitMask(pin);
    uint8_t port = digitalPinToPort(pin);
    volatile uint8_t *out;

    if (port == NOT_A_PIN) return;

    // If the pin that support PWM output, we need to turn it off
    // before doing a digital write.
    if (timer != NOT_ON_TIMER) turnOffPWM(timer);

    out = portOutputRegister(port);

    uint8_t oldSREG = SREG;
    cli();

    if (val == LOW) {
        *out &= ~bit;
    } else {
        *out |= bit;
    }

    SREG = oldSREG;
}

Notice that the interrupts are saved and restored around the read/modify/write of the port register. So digitalWrite() already addresses the possibility. This method will stretch one phase of an occasional PWM pulse by a few microseconds.

Alternatively, ignoring the possibility of a collision and having one occur, seems more severe: the pulse could be begun or ended prematurely. This occurrence will take longer - the rest of the disturbed phase - to get back in sync.

So the criterion for how to manage x becomes "Which way has the lesser - or more tolerable - consequence for your PWM consumer?"

  • That makes sense, thanks. Watching the PWM bits seems as if it would be fairly slow, which somewhat defeats the purpose of the direct port writes. I'm going to have to think about which of the alternatives is worse, as you ask. – Jim Mack Dec 31 '18 at 15:46
  • Thanks for the update. I am indeed going for minimum skew. I can't change the ports used, they're part of a shield. Since these PWMs are 95-99% on, I think the risk of glitching is low enough that I'll just bang through. Curious, though. This seems so baked into the structure of the Arduino... does digitalWrite() itself actually overcome this? – Jim Mack Dec 31 '18 at 21:41
  • Nice information. I can see that cli() and friends would be somewhat safer, and in fact knowing that PWM is in use removes the need to save and restore the SREG (interrupts must be on). I also guess that if the PWM is at 97% there's only a 3% chance that ignoring interrupts will introduce a meaningful glitch. Sigh. I guess I'll experiment once I have all the hardware. – Jim Mack Jan 1 at 1:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.