I’m not sure if this is the right forum to ask this question, I teach a class on microcontrollers. We are working with the ATMEGA 2560 and programming in C. I was showing the students how to blink 3 LEDS using the I/O registers and a timer functionbuilt into the Arduino library. What I realized that in my code I had an error in specifying the data direction register, but the code still functioned properly.
I initialized 3 ports for output by initializing the Data Direction Register B, but used the wrong register macro names for the bits:
DDRB |= (1 << PORTB5) | (1 << PORTB6) | (1 << PORTB7);
It should have been stated:
DDRB |= (1 << DDB5) | (1 << DDB6) | (1 << DDB7);
The code still works even though it appears that the wrong macros were used. Did my compiler outsmart me?
My understanding is that the when you include avr/io.h, the macros give the preprocessor directives to associate the “Bitname” such as DB5 with the address in memory. It looks like that is not correct. Why does DDB5 = PORTB5?
The code is equivalent if the macros happen to have the same value. I have seen this incidentally happen more than once in AVR where people technically used the non-intended macros and still had the same result. Just Ctrl+Click on the macro name in VSCode to follow them to the source and inspect their value, and you shall find the truth.
Just to add my £0.02.
The three registers for the pins, DDRx, PORTx & PINx, use exactly the same bits for the same pins, so your code worked fine because the bit patterns were identical. I suffered from exactly this “problem” when I first delved into AVR programming and away from Arduino.
DDB5 = PINB5 = PORTB5 = (1 << 5).
Thank you very much for your reply. I was thinking that each register bit location in a register has a unique address, but that was my mistake. I guess there is no such thing as a register bit number address!
Thanks so much for your reply.
I was overthinking it. I was under the impression that each bit in the register is somehow unique in the address.
So I could write
DDRB |= (1 >> ADATE); and get the same result as
DDRB |= (1 << DDB5);
because ADATE = 5. Do you agree?
You could indeed use ADATE like that. Or anything else that has the same value.
It would confuse people used to using the “correct” bit names though!
You can also use 32, 0x20, 0b0010000 etc without the shift of course.
Yeah, I would never use ADATE like that. I was just realizing that all of the macros for bit names are just numbers. I originally thought that the macro for a bit name was a pointer that had to be referenced from its register address. But that wouldnt make sense since the address of the registers are 8 bits wide so it already accounts for the bit locations. Hope that makes sense what i was thinking.
if I remember correctly, the register names are
volatile uint8_t * pointers but yes, the bit names are just the bit positions in the register. So you have this for
PINB, for example:
#define PINB _SFR_IO8(0x03)
#define PINB0 0
#define PINB1 1
#define PINB2 2
#define PINB3 3
#define PINB4 4
#define PINB5 5
#define PINB6 6
#define PINB7 7
#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
So, I did remember correctly!