Can I use the function `digitalPinToPort()`

I am writing a module for other people which can output a PWM signal between 20 and 100Hz depending on the dutycycle.

For that I have written this code

void Weistra::begin() {
    if( trackPin >= 13 && trackPin <= 8 ) {
        port = &PORTB;
        trackPin -= 8;  // 13 becomes 5, 11, becomes 3 etc
    }
    else if( trackPin >= A5 && trackPin <= A0 ) {
        port = &PORTC;
        trackPin -= 14; // A5 becomes 5
    }
    else if( trackPin >= 13 && trackPin <= 8 ) {
        port = &PORTD;
        // trackPin needs no alterations for port D
    }
    else {
        port = 0;
        return; // invalid pin number
    }
}

void Weistra::update() {
    static byte counter = 0;
    static uint16_t prevTime = 0;

    if( port != 0 ) {
        uint16_t currentTime = mircros() & 0x0000FFFF; // we only use the last 2 bytes of micros()

        if( currentTime - prevTime >= intervalTime ) {
            prevTime = currentTime;

            counter++;
            if( counter >= 100 ) { counter = 0; // if counter reaches 100, reset it to 0 and enable the track power pin
                *port |=  (1 << trackPin);
            }
            else if( counter == dutyCycle ) {
                *port &= ~(1 << trackPin);
            }
        }
    }
}

To make it more beginner syntax I thought I do it like this so one does not have to specify the port number or pin number for a direct port instruction. + my code only works for the atmega328P not for anything else. So I wanted to do the same as one does in digitalWrite.

In digitalWrite the make use of a macro called digitalPinToPort() to get the correct port back. But I am lost in that jungle of arduino libraries and tangled functions and macros.

extern const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[];
extern const uint8_t PROGMEM digital_pin_to_timer_PGM[];

// Get the bit location within the hardware port of the given virtual pin.
// This comes from the pins_*.c file for the active board configuration.
// 
// These perform slightly better as macros compared to inline functions
//
#define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) )
#define digitalPinToBitMask(P) ( pgm_read_byte( digital_pin_to_bit_mask_PGM + (P) ) )

I cannot find certain definitions and this does not make me happy :frowning:

anyways. I have 3 questions.

  1. Am I doing this right? Can I use a pointer to control IO ports?
  2. Must this pointer be declared as volatile?
private:
    uint16_t intervalTime;
    byte dutyCycle;
    byte trackPin;
    byte *port; // < volatile needed?
};
  1. Can I simply use the macros digitalPinToBitMask and digitalPinToPort and suddenly have compatibility with the arduino mega and leonardo was well?

Kind regards,

Bas

Edit my topic is called wrong as I am talking about a macro

  uint8_t pin; // arduino pin number
  uint8_t mask;
  uint8_t port;
  volatile uint8_t *portx_p;

  mask    = digitalPinToBitMask( pin );
  port    = digitalPinToPort( pin );
  portx_p = portOutputRegister( port ); 
  
  write low  ( *portx_p &= ~mask )  
  write high ( *portx_p |= mask )

Arduino core
Microcontroller data sheets
AvrLibC docs
AvrLibC microcontroller header files

I would think it will work for you the same as it works for Arduino.

1 Like