Write/read to registers with the addresses of the registers

Hi, The code below makes the led on pin 13 (arduino uno) turn on and off via registers.

#define CLR(x,y) (x&=(~(1<<y))) // set HIGH
#define SET(x,y) (x|=(1<<y)) // SEL LOW

void setup() {
  DDRB = B00100000; // set as output
  }

void loop() {
  SET(PORTB,5);  // pin 13 HIGH
  delay(1000);
  CLR(PORTB,5); // pin 13 LOW
  delay(1000);
}

DDR is actually the name of an register. When I look that up in the datasheet, I find adress '0x24'
see datasheet p624 ( http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf )

If I then change the registry name to the registry address, the code no longer works. (see code below)

#define CLR(x,y) (x&=(~(1<<y))) // set HIGH
#define SET(x,y) (x|=(1<<y)) // SEL LOW

byte DDRB_test = (*(volatile uint8_t *)(0x24));

void setup() {
  DDRB_test = B00100000; // set as output
  }

void loop() {
  SET(PORTB,5);  // pin 13 HIGH
  delay(1000);
  CLR(PORTB,5); // pin 13 LOW
  delay(1000);
}

How is this possible? Or am I forgetting something?

You're forgetting to look at the definition of DDRB for your processor. (Hint: it's in your installation)

Thank you for your response. Where can I find that?

Something like hardware/tools/avr/avr/include/avr

The DDRB address is correct.
Or rather, both codes work at least on my Nano.

The DDxn bit in the DDRx Register selects the direction of this pin. If DDxn is written logic one, Pxn is configured as an output pin. If DDxn is written logic zero, Pxn is configured as an input pin.

Very weird.... It won't work here

Please paste the compiled assembly list here.
It becomes clear what is happening.

Sketch uses 678 bytes (2%) of program storage space. Maximum is 32256 bytes.
Global variables use 9 bytes (0%) of dynamic memory, leaving 2039 bytes for local variables. Maximum is 2048 bytes.
C:\Users\LocalUser\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/bin/avrdude -CC:\Users\LocalUser\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/etc/avrdude.conf -v -patmega328p -carduino -PCOM14 -b115200 -D -Uflash:w:C:\Users\LOCALU~1\AppData\Local\Temp\arduino_build_417879/portenta_registers_led.ino.hex:i 

avrdude: Version 6.3-20190619
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2014 Joerg Wunsch

         System wide configuration file is "C:\Users\LocalUser\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/etc/avrdude.conf"

         Using Port                    : COM14
         Using Programmer              : arduino
         Overriding Baud Rate          : 115200
         AVR Part                      : ATmega328P
         Chip Erase delay              : 9000 us
         PAGEL                         : PD7
         BS2                           : PC2
         RESET disposition             : dedicated
         RETRY pulse                   : SCK
         serial program mode           : yes
         parallel program mode         : yes
         Timeout                       : 200
         StabDelay                     : 100
         CmdexeDelay                   : 25
         SyncLoops                     : 32
         ByteDelay                     : 0
         PollIndex                     : 3
         PollValue                     : 0x53
         Memory Detail                 :

                                  Block Poll               Page                       Polled
           Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
           ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
           eeprom        65    20     4    0 no       1024    4      0  3600  3600 0xff 0xff
           flash         65     6   128    0 yes     32768  128    256  4500  4500 0xff 0xff
           lfuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           hfuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           efuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           lock           0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           calibration    0     0     0    0 no          1    0      0     0     0 0x00 0x00
           signature      0     0     0    0 no          3    0      0     0     0 0x00 0x00

         Programmer Type : Arduino
         Description     : Arduino
         Hardware Version: 3
         Firmware Version: 4.4
         Vtarget         : 0.3 V
         Varef           : 0.3 V
         Oscillator      : 28.800 kHz
         SCK period      : 3.3 us

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: reading input file "C:\Users\LOCALU~1\AppData\Local\Temp\arduino_build_417879/portenta_registers_led.ino.hex"
avrdude: writing flash (678 bytes):

Writing | ################################################## | 100% 0.12s

avrdude: 678 bytes of flash written
avrdude: verifying flash memory against C:\Users\LOCALU~1\AppData\Local\Temp\arduino_build_417879/portenta_registers_led.ino.hex:
avrdude: load data flash data from input file C:\Users\LOCALU~1\AppData\Local\Temp\arduino_build_417879/portenta_registers_led.ino.hex:
avrdude: input file C:\Users\LOCALU~1\AppData\Local\Temp\arduino_build_417879/portenta_registers_led.ino.hex contains 678 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 0.09s

avrdude: verifying ...
avrdude: 678 bytes of flash verified

avrdude done.  Thank you.

The code uploads without error, but the LED does not blink.

It's an IDE message.
Look at the assembly list of the compiled your program.

For the AVR processors:
hardware/tools/avr/avr/include/avr
The files are 'ioXXX.h' where 'XX' is the processor ID. for the UNO and Nano the ID is m328p so look in iom328p.h.
In there you will find:
#define PORTB _SFR_IO8(0x05)
io.h contains:
#include <avr/sfr_defs.h>
which contains definitions for _SFR_IO8

Look like the main difference between a register address and a RAM address is an offset of 0 or 0x20 depending on version... ?

This address offset is the difference between using IN / OUT or LD / ST.
You can use both, not the difference version.

EDIT:
Look at datasheet. Page:624, Note:4

Sorry, But i didn't find that...

@laura2000
I'm so sorry, the above my post was wrong. :pensive:
I think it couldn't have worked if I deep thought about it.
The address of the register can't be put in a variable.
The compiler try optimize it into a RAM address.

The reason why it worked with my case is that it used the internal LED, so the on-board comparator detected the PULLUPed voltage of the input pin and made the board LED shine brightly.
All PORTB remains input.

I think there is no choice but to use the #defined name or direct write the address as it is.

#define CLR(x,y) (x&=(~(1<<y))) // set HIGH
#define SET(x,y) (x|=(1<<y)) // SEL LOW

#define DDRB_test (*(volatile uint8_t *)(0x24))

void setup() {
  DDRB_test = B00100000; // set as output
  }

void loop() {
  SET(PORTB,5);  // pin 13 HIGH
  delay(1000);
  CLR(PORTB,5); // pin 13 LOW
  delay(1000);
}

Well... it's the same as original DDRB... :slightly_frowning_face:

1 Like

Here ya go:

#define CLR(x,y) (x&=(~(1<<y))) // set HIGH
#define SET(x,y) (x|=(1<<y)) // SEL LOW

volatile uint8_t *DDRB_test = &DDRB;

void setup() {
  *DDRB_test = B00100000; // set as output
  }

void loop() {
  SET(PORTB,5);  // pin 13 HIGH
  delay(1000);
  CLR(PORTB,5); // pin 13 LOW
  delay(1000);
}
3 Likes

@gfvalvo

Good point! +1
Oh yeah, I forgot to add * when assign when trying various things... :rofl:

Probably it's the solution to OP @laura2000 :+1:

1 Like

Thank you for your help. The code below works :wink:

#define CLR(x,y) (x&=(~(1<<y))) // set HIGH
#define SET(x,y) (x|=(1<<y)) // SEL LOW

volatile uint8_t *DDRB_test = (0x24);

void setup() {
  *DDRB_test = B00100000; // set as output
  }

void loop() {
  SET(PORTB,5);  // pin 13 HIGH
  delay(1000);
  CLR(PORTB,5); // pin 13 LOW
  delay(1000);
}

Why ?

Optimizing for speed in the setup() function is almost never needed. The slower pinMode() can be used in setup().

The Arduino bitSet() and bitClear() can be used. They work on a variable but also on the registers. They turn into the direct and fast assembly instruction.

void loop() {
  bitSet(PORTB,5);  // pin 13 HIGH
  delay(1000);
  bitSet(PORTB,5); // pin 13 LOW
  delay(1000);
}

The ATmega328P has an extra. When a pin is OUTPUT, then writing to the PINB registers toggles those output pins. Since only one output should be toggled, the rest of the bits must be written with zero. The Arduino bit() can be used for that or just write 0b00100000 to PINB.

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

void loop() 
{
  PINB = bit( 5);   // toggle pin 13 (it is a output)
  delay(1000);
}

I know that, but I won't stick to just controlling LEDs either.

Presumably with C++. you could also use "references":

volatile uint8_t & DDRB_test = (*(volatile uint8_t *)(0x24));

void setup() {
  DDRB_test = 0b00100000; // set as output
}
1 Like