SOLVED (thank you all!) How to pass a PORT to a function?

Hi all,

Title says it all. How can I pass a port to a function… like this:

void write_bits (uint8_t data, ??? port)
{
    uint8_t n = 8;
    while (n--) {
        data & (1 << n) ? port |= bit : port &= ~bit;
    }
}

// to call the function:
write_bits (0x55, PORTB);

Where “port” would be “PORTB” or “PORTK” or any other AVR port.

Thanks.

volatile uint8_t

This works too :slight_smile: :

#include <avr/io.h>
#define PORT_ON(port,pin) port |= (1<<pin)
#define PORT_OFF(port,pin) port &= ~(1<<pin)
#define PORT_TOGGLE(port,pin) port ^= (1<<pin)

int main(void)
{

  sei();

  DDRB |= 1 << DDB5;

  TCCR1B |= 1 << CS12 | 1 << WGM12 | 1 << CS10;
  TIMSK1 |= 1 << OCIE1A;
  OCR1A = 16383; 

  while (1);
  
}
ISR(TIMER1_COMPA_vect)
{
  PORT_TOGGLE(PORTB, PINB5);
}

septillion:
volatile uint8_t

Nope. Tried that and everything else.

ard_newbie:
This works too :slight_smile: :

#include <avr/io.h>

#define PORT_ON(port,pin) port |= (1<<pin)
#define PORT_OFF(port,pin) port &= ~(1<<pin)
#define PORT_TOGGLE(port,pin) port ^= (1<<pin)

int main(void)
{

sei();

DDRB |= 1 << DDB5;

TCCR1B |= 1 << CS12 | 1 << WGM12 | 1 << CS10;
 TIMSK1 |= 1 << OCIE1A;
 OCR1A = 16383;

while (1);
 
}
ISR(TIMER1_COMPA_vect)
{
 PORT_TOGGLE(PORTB, PINB5);
}

??? Um not what I was asking. And, you missed one:

PIN |= BIT;

…also toggles an output.

PORTB is defined in the tool chain as a macro#define PORTB _SFR_IO8(0x05) which uses the macro#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)which uses#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))and

#if __AVR_ARCH__ >= 100
#define __SFR_OFFSET 0x00
#else
#define __SFR_OFFSET 0x20
#endif
#endif

So PORTB expands into (*(volatile uint8_t *)((0x05) + 0x20)) which the pre-processor will calculate as (*(volatile uint8_t *)0x25)

So that makes 0x25 the address of a volatile uint8_t and the expression uses the dereference operator to get that address (either reading or writing)

So with the dereference....

J-M-L:
So that makes 0x25 the address of a volatile uint8_t and the expression uses the dereference operator to get that address (either reading or writing)

So now you know the type of PORTB and the fact it’s a byte value...

... and this code will give the address of any Port :slight_smile: :

#define STRING_IT(s) #s
#define EXPAND_IT(s) STRING_IT(s)

void setup(void)
{
  Serial.begin(250000);
  Serial.println(EXPAND_IT(PORTC));
 //output: (*(volatile uint8_t *)((0x08) + 0x20))

}

void loop(void) { }

What everyone else said. The PORTs are memory addresses. The MACROS dereference those addresses, but you can un-dereference them just by taking the address:

void foo(volatile byte *thePort) {
}

void bar() {
  foo(&PORTA);
}

PaulMurrayCbr:
What everyone else said. The PORTs are memory addresses. The MACROS dereference those addresses, but you can un-dereference them just by taking the address

exactly - this should blink D13 on a UNO (untested)

// PORTB covers pins digital pin 8 to 13
// D13 is the builtin LED
// I want to blink D13 to demonstrate passing a PORT address as a param
// This is of course a crappy code to toggle bit 5 (pin 13) of PORTB on a UNO


void toggle(volatile uint8_t* port, boolean state)
{
  if (state) {
    *port |= B00100000; // D13 ON
  } else {
    *port &= B11011111; // D13 OFF
  }
}


void setup(void)
{
  pinMode(13, OUTPUT);
}

void loop(void) {
  toggle(&PORTB, true);
  delay(200);
  toggle(&PORTB, false);
  delay(200);
}

J-M-L:
exactly - this should blink D13 on a UNO (untested)

Well, thanks to your guidance and explanation of how PORTx is defined, I figured it out. I modified it slightly so that I wouldn’t need to specify anything but PORTx. This works (tested and runs on my MEGA2560):

#define BIT7 ((uint8_t)(1 << 7)) // pin 13 LED

void blink (volatile uint8_t &port, uint8_t bit)
{
    port |= bit;
    _delay_ms (250);
    port &= ~bit;
    _delay_ms (250);
}

int main (void)
{
    DDRB |= BIT7;

    while (1) {
        blink (PORTB, BIT7);
    }
}

Thanks for the help!

PaulMurrayCbr:
What everyone else said. The PORTs are memory addresses. The MACROS dereference those addresses, but you can un-dereference them just by taking the address:

void foo(volatile byte *thePort) {

}

void bar() {
 foo(&PORTA);
}

Thanks! See previous post: I modified the code slightly so that the "address of" operand wasn't needed.

J-M-L:
exactly - this should blink D13 on a UNO (untested)

Well, I was wrong. Not using the “address of” operator caused it to fail with ports above 0xFF (for example, PORTK).

However, I am now confused. The code below works (i.e. blinks the LED) but the print statements don’t make sense:

#define BIT7 ((uint8_t)(1 << 7)) // pin 13 LED

void blink (volatile uint8_t *port, uint8_t bit)
{
    fprintf (stdout, " PORT: 0x%04X\n", (port-0)); // -0 == PORT
    fprintf (stdout, " DDR : 0x%04X\n", (port-1)); // -1 == DDR
    fprintf (stdout, " PIN : 0x%04X\n", (port-2)); // -2 == PIN
    fprintf (stdout, "\n"); // new line

//  fprintf (stdout, "&PORT: 0x%04X\n", &(port-0)); // compile fails
//  fprintf (stdout, "&DDR : 0x%04X\n", &(port-1));
//  fprintf (stdout, "&PIN : 0x%04X\n", &(port-2));
//  fprintf (stdout, "\n"); // new line

    fprintf (stdout, "*PORT: 0x%04X\n", *(port-0));
    fprintf (stdout, "*DDR : 0x%04X\n", *(port-1));
    fprintf (stdout, "*PIN : 0x%04X\n", *(port-2));
    fprintf (stdout, "\n"); // new line

    *(port-1) |= BIT7; // set DDR

    *(port-0) |= bit; // bit 7 high
    _delay_ms (250);
    *(port-0) &= ~bit; // bit 7 low
    _delay_ms (250);
}

int main (void)
{
    init();
    Serial.begin (115200);
    STDIO.open (Serial);

    blink (&PORTB, BIT7);

    while (1) {
//      blink (&PORTB, BIT7);
    }
}

The serial output is this:

[b]
 PORT: 0x0025
 DDR : 0x0024 [color=red][color=green]<-- correct for PORTB/DDRB/PINB[/color][/color]
 PIN : 0x0023

*PORT: 0x0000
*DDR : 0x0000 [color=red]<-- HUH????????[/color]
*PIN : 0x0008
[/b]

What the heck am I seeing here???

Just a note:

krupski:

PIN |= BIT;

...also toggles an output.

That is not true. It toggles BIT and all pins that happen to read HIGH.

The correct way to toggle a single bit is

PIN = BIT;
#define BIT7 ((uint8_t)(1 << 7)) // pin 13 LED

seems fishy → PORTB covers pins digital pin 8 to 13 so D13 on PORTB is (1 << 5) not (1 << 7)

bit 6 and 7 of PORTB should not be touched as they relate to your crystal
→ Port B, Bit 7 = XTAL2: Chip clock Oscillator pin 2
→ Port B, Bit 6 = XTAL1: Chip clock Oscillator pin 1

*DDR : 0x0000 ← HUH???

DDRB is Port B Data Direction Register, you see a 1 if the associated pin is an output.

As you ask to print with the format %04X the value is formatted as a hexadecimal integer with four digits and leading zeros. Here you have done nothing on the pins (you should actually set D13 as output), so they are all input, so 0 is te right answer.

Whandall:
Just a note:

That is not true. It toggles BIT and all pins that happen to read HIGH.

The correct way to toggle a single bit is

PIN = BIT;

From the ATMega2560 datasheet 2549Q–AVR–02/2014 page 67:

Three I/O memory address locations are allocated for each port, one each for the Data Register – PORTx, Data
Direction Register – DDRx, and the Port Input Pins – PINx. The Port Input Pins I/O location is read only, while the
Data Register and the Data Direction Register are read/write. However, writing a logic one to a bit in the PINx Reg-
ister, will result in a toggle in the corresponding bit in the Data Register. In addition, the Pull-up Disable – PUD bit
in MCUCR disables the pull-up function for all pins in all ports when set.

Writing a 1 to a particular bit (either by using “= BIT” or “|= BIT” will toggle the corresponding bit.

“Toggle” means if it’s 0, set it to 1, if it’s 1, set it to 0.

J-M-L:

#define BIT7 ((uint8_t)(1 << 7)) // pin 13 LED

seems fishy → PORTB covers pins digital pin 8 to 13 so D13 on PORTB is (1 << 5) not (1 << 7)

bit 6 and 7 of PORTB should not be touched as they relate to your crystal
→ Port B, Bit 7 = XTAL2: Chip clock Oscillator pin 2
→ Port B, Bit 6 = XTAL1: Chip clock Oscillator pin 1
DDRB is Port B Data Direction Register, you see a 1 if the associated pin is an output.

You’ve missed a tiny important detail:

krupski:
This works (tested and runs on my MEGA2560):

J-M-L:

#define BIT7 ((uint8_t)(1 << 7)) // pin 13 LED

seems fishy → PORTB covers pins digital pin 8 to 13 so D13 on PORTB is (1 << 5) not (1 << 7)

bit 6 and 7 of PORTB should not be touched as they relate to your crystal
→ Port B, Bit 7 = XTAL2: Chip clock Oscillator pin 2
→ Port B, Bit 6 = XTAL1: Chip clock Oscillator pin 1

on a 328p. I’m testing with a MEGA2560 where the Pin 13 LED is PORTB, Bit 7.

So what?

Possibly writing ones to bits that should not toggle (using |=) is simply false.

|= is way slower needing a read, a logical operation and a write as well as an non const operand.

If you want to toggle bits in a port simply write ones to bits in the PINx register.
Again, reading the PINx is superfluous and induces probably errors.

krupski:
Writing a 1 to a particular bit (either by using "= BIT" or "|= BIT" will toggle the corresponding bit.

"Toggle" means if it's 0, set it to 1, if it's 1, set it to 0.

And if there were nonzero bits in the PIN register, those will be toggled, too.

Well, that depends.

If the PIN register is in the range 0x00..0x1F and you are setting exactly one bit to 1, an SBI (set bit in I/O register) instruction will be used and only that bit will be toggled. Otherwise it is a load-modify-store sequence and you've made a booboo.

On a Mega2560 ports A to G are within the 0x00..0x1F range.

krupski:
... on a 328p. I'm testing with a MEGA2560 where the Pin 13 LED is PORTB, Bit 7.

oops :slight_smile:

OK - rest is true though, isn't' it...?