Question About using "Volatile" in Port Registers

I was wondering, do you need to mark pointers that are used to point to port registers (DDRx, PORTx, PINx) as volatile? Will the compiler know not to remove or mess with the pointers used for that, because I ran into a problem when developing a library for myself with those pointers. I was getting all sorts of weird behavior and spent a good hour or two debugging until I decided to mark the pointers to the port registers as volatile. Everything started working fine after that.

The usual reason to use the volatile specifier is to prevent the compiler putting that value into a register instead of memory which it may normally do for reasons of speed. If the value is in a register then it may be pushed onto the stack as the result of running an ISR and thus be unavailable should the value need to be read or changed

By putting the value in a memory location it is always available and can be read or changed at any time

@UKHeliBob Interesting
This is the problem I am having is with this library. I won't put the entire library in here but only the parts I had problems with:

//ONEWIRE REV 1.0.0
//.h
#ifndef ONEWIRE_h
#define ONEWIRE_h
#include "Arduino.h"
class ONEWIRE {
  public:
    void SetPin(uint8_t Pin);
    uint8_t Read(bool*, uint8_t&, uint8_t);

    void PinSet(bool Dir);
    bool PinRead();
    void PinWrite(bool Dir);
  private:
    volatile uint8_t *_PinPort_PIN;
    volatile uint8_t *_PinPort_DDR;
    volatile uint8_t *_PinPort_PORT;
    uint8_t _PinMask;
    uint8_t _PinMaskNot;
    bool Boot = false;
};
#endif
//.cpp
//#include "ONEWIRE.h"
//#include "Arduino.h"

void ONEWIRE::SetPin(uint8_t Pin) {
  if (Pin <= 19) {
    if (Boot) {
      *_PinPort_DDR &= _PinMaskNot;
      *_PinPort_PORT &= _PinMaskNot;
    }
    Boot = true;
    if (Pin <= 7) {
      _PinPort_PIN = &PIND;
      _PinPort_DDR = (_PinPort_PIN + 1);
      _PinPort_PORT = (_PinPort_PIN + 2);
      _PinMask = (1 << Pin);
      _PinMaskNot = ~_PinMask;
      //Port D
    }
    else if (Pin <= 13) {
      _PinPort_PIN = &PINB;
      _PinPort_DDR = (_PinPort_PIN + 1);
      _PinPort_PORT = (_PinPort_PIN + 2);
      _PinMask = (1 << (Pin - 8));
      _PinMaskNot = ~_PinMask;
      //Port B
    }
    else {
      _PinPort_PIN = &PINC;
      _PinPort_DDR = (_PinPort_PIN + 1);
      _PinPort_PORT = (_PinPort_PIN + 2);
      _PinMask = (1 << (Pin - 14));
      _PinMaskNot = ~_PinMask;
      //Port C
    }
    *_PinPort_DDR &= _PinMaskNot;
    *_PinPort_PORT |= _PinMask;
  }
}

void ONEWIRE::PinSet(bool Dir) {
  if (Dir)
    *_PinPort_DDR |= _PinMask;
  else {
    *_PinPort_DDR &= _PinMaskNot;
    *_PinPort_PORT |= _PinMask;
  }

}

bool ONEWIRE::PinRead() {
  return (*_PinPort_PIN & _PinMask);
}

void ONEWIRE::PinWrite(bool Dir) {
  if (Dir)
    *_PinPort_PORT |= _PinMask;
  else
    *_PinPort_PORT &= _PinMaskNot;
}

The SetPin method is where you put the pin you want to use and it will convert it into port registers and such. The pin read method is where I noticed the problems. If I use it normally in a sketch, It returns correct state of the pin. If I use in in another method within the class (Read(bool*, uint8_t&, uint8_t); for example) , It always returns a 0 even if the pin is really a 5v logic high. As said, all of these problems went away as soon as I marked

    uint8_t *_PinPort_PIN;
    uint8_t *_PinPort_DDR;
    uint8_t *_PinPort_PORT;

as volatile

I have no issues with marking them as volatile but I still don't understand why I have to mark them.

I don't see that function defined in your code.

The "volatile" keyword ensures that the compiler does not perform any optimization on the item and that the item is always read/written directly without any "caching" of the value read from or written to it.

When accessing registers, this is required because the compiler does not know that the value of the register may change for external reasons and thus a cached value is no good.

1 Like

I defined it over here:

I just did not put the actual code for it here in the forum because it was not what I had problems with :slight_smile:

If you really knew where the problem was, you wouldn't have to ask for help on the forum. Post the code for the function and the code that calls it. When dealing with memory-mapped registers you likely do need the 'volatile' designation. So, there could be something wrong with the code you didn't show.

Thank you , it helped clear up some questions.

Yes. As they are in avr/io.h (or whatever)