0

I am new to Arduino and I have recently started off by trying to turn on an LED connected to pin 13 on my Arduino Uno using pointers.

I opted to do this with pointers because this would give me a better understanding on what really goes behind when I use predefined Arduino functions.

My simple code looks like this,

uint8_t *data = (uint8_t *) 0x05; //PortB data register ptr (Since pin 13 is PB6) 
uint8_t *dir = (uint8_t *) 0x04;  //PortB dir register ptr (Since pin 13 is PB6)

void setup() {
    *dir = 0x40;  //PB6 direction set to O/P
    *data = 0x40; //PB6 data set to 1 to turn on the LED
}

void loop() {
  *data = 0x40;
}

I tried the same program using pinMode(13, OUTPUT) and digitalWrite(13, LOW) functions and it works fine. But I do not understand what could be wrong with my pointers. If you find anything wrong, kindly let me know.

  • 1
    "I opted to do this with pointers because this would give me a better understanding on what really goes behind when I use predefined Arduino functions." No it won't. – Ignacio Vazquez-Abrams Sep 18 '17 at 17:35
  • I would like to really access the registers on my own rather than through functions. I know it would be easier with functions but I want to do this to understand register access better. Moreover behind a function like pinMode, I do beleive that all it does is write a bit onto a register which I would like to do on my own for starters. – Bhuvanesh Narayanan Sep 18 '17 at 17:39
  • But using pointers won't help with that. – Ignacio Vazquez-Abrams Sep 18 '17 at 17:40
3

You made two small mistakes:

  1. The addresses of the I/O registers are 0x24 for DDRB and 0x25 for PORTB. The numbers you used are I/O port numbers, but since pointers are supposed to point to memory, you have to use the memory-mapped addresses instead.
  2. Pin 13 is PB5, not PB6.

With these two mistakes fixed, your code works as expected. There are still two issues however:

  1. Under some circumstances, the compiler can optimize away some writes to these registers. You have to qualify the pointers as “pointer to volatile data” in order to avoid this. Even if your simple example does not show this problem, it is always safer to use the qualifier.
  2. The compiler generates inefficient code. Not as inefficient as digitalWrite(), but still... It knows how to optimize, but it is prevented to do so by the fact that your pointers are modifiable. You should qualify them as const if you want to benefit from compiler optimizations.

With those fixes, your pointer definitions would look like:

volatile uint8_t * const data = (uint8_t *) 0x25;
volatile uint8_t * const dir  = (uint8_t *) 0x24;

and access to them gets compiled into efficient in ans out assembly instructions.

That being said, if you want to do direct port access, I would recommend doing it the avr-libc way:

void setup() {
    DDRB  = _BV(PB5);
    PORTB = _BV(PB5);
}

void loop() {
    PORTB = _BV(PB5);
}

The way the AVR-libc defines the I/O registers is roughly equivalent to

#define PORTB (* (volatile uint8_t *) 0x25)
  • Thank you so much ! I was a bit confused with the address of the register. But once I used a main function and the proper register address it all started working without library variables like PORTB ! :) And also thank you for the volatile and constant suggestions. – Bhuvanesh Narayanan Sep 19 '17 at 20:56
1

Port definitions in AVR-Libc are done via black magic. Don't attempt to replicate them as a beginner. That being said...

"How do I pass an IO port as a parameter to a function?"

volatile uint8_t *data = &PORTB;
volatile uint8_t *dir = &DDRB;
  • Thank you so much ! After I checked your 'black magic' link it was a bit clear. I implemented what you had suggested and used a main function. There on it worked fine. – Bhuvanesh Narayanan Sep 18 '17 at 18:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.