The ATmega4809 organizes I/O registers by module and puts all the registers in C structures. The older AVR microcontrollers have a flat organization. For instance, take the Data Direction register for port B. In the ATmega328P this is simply defined:
#define DDRB _SFR_IO8(0x04)
This puts the register at location 0x04.
In the ATmega4809, all the registers of PORTB are defined as an instance of structure PORT_t at location 0x0400:
#define PORTB (*(PORT_t *) 0x0420) /* I/O Ports */
But this structure contains multiple registers, of course. The structure is defined:
typedef struct PORT_struct { register8_t DIR; /* Data Direction */ register8_t DIRSET; /* Data Direction Set */ register8_t DIRCLR; /* Data Direction Clear */ register8_t DIRTGL; /* Data Direction Toggle * ... 27 registers omitted for clarity ... register8_t reserved_0x1F; } PORT_t;
So instead of writing DDRB |= 1<<2; one would write PORTB.DIR |= 1<<2;
However, I would guess as a concession to the traditionalists, they have also defined:
#define PORTB_DIR _SFR_MEM8(0x0420)
So you can avoid the structure (but why?) and write PORTB_DIR |= 1<<2;
In this particular case we would probably use the Virtual Port feature which allows us to set the direction using a lower address, which generates smaller and faster code. VPORTB.DIR |= 1<<2;
Instead we could eliminate the need for a read-modify-write of the register, which would not be an atomic operation for PORTB (it would be for VPORTB) by using the DIRSET register. They have cleverly added what I call virtual registers that are write only and modify an existing register. DIRSET will set bits in the DIR register eliminating the need to read the register first. So PORTB.DIRSET = 1<<2;
I’ve revised the Pins library provided in Far Inside The Arduino to handle the new structure of the ATmega4809. Using this library we can still write ddr.digital_5 = 1; to change digital pin 5 (Port B bit 2 on the Arduino Nano Every) to be an output pin.