NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Where do 'PORTS' come from?

July 08, 2012
by TuffLux
TuffLux's Avatar

I've been going through the ATMEL datasheet for the ATMega168 and searching online, but I haven't found an explanation on where the 'PORT' character comes from?

eg. When changing the DDR Register, where does one get DDRC from?

July 08, 2012
by Rick_S
Rick_S's Avatar

I'm not quite sure I fully understand your question but if you are asking where the terms come from, they are both named registers on the chip. The PORT registers (there are 3 on the ATMEGA168 PORTB, PORTC, and PORTD) control the stage of the pins of the micro-controller. The DDR registers (Again their are 3 DDRB, DDRC, and DDRD) are the Data Direction Registers. They control whether a given pin on a port is input or output.

Here is a quote from the datasheet:

Each port pin consists of three register bits: DDxn, PORTxn, and PINxn. As shown in ”Register Description” on page 92, the DDxn bits are accessed at the DDRx I/O address, the PORTxn bits at the PORTx I/O address, and the PINxn bits at the PINx I/O address. 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.

If PORTxn is written logic one when the pin is configured as an input pin, the pull-up resistor is activated. To switch the pull-up resistor off, PORTxn has to be written logic zero or the pin has to be configured as an output pin. The port pins are tri-stated when reset condition becomes active, even if no clocks are running.

If PORTxn is written logic one when the pin is configured as an output pin, the port pin is driven high (one). If PORTxn is written logic zero when the pin is configured as an output pin, the port pin is driven low (zero).

Hope that helps clarify it a bit,

Rick

July 08, 2012
by TuffLux
TuffLux's Avatar

Rick,

Thanks. I've read that paragraph a number of times.

What I'm trying to understand is what the 'B, C and D' PORT registers refer to. eg. Are they referring to the C in PC3(PIN26) or B in PB6 (PIN9)?

July 08, 2012
by Ralphxyz
Ralphxyz's Avatar

Hi TuffLux, yes exactly!!

Pin 14, 15, 16, 17, 18, 19, 9, and 10 make the 8 pins (bits) of PORTB.

The same for PORTC and PORTD.

Ralph

July 08, 2012
by TuffLux
TuffLux's Avatar

Ralph,

Thanks. That's what I assumed, but I couldn't find any documentation referencing it.

July 08, 2012
by pcbolt
pcbolt's Avatar

TuffLux -

PORTD, PORTC and PORTB are memory addresses inside the MCU. You have to really do some digging inside each of the "include" header files to find the actual address but it is there. So if you write:

 PORTD = 1;

You are actually telling the MCU to put 1 in address register 0x0B inside the ATmega168p. The MCU then uses that info to apply 5v to any of it's 8 pins depending on the value stored at memory address 0x0B. The symbols PD1, PD2 etc are defined as values 1, 2, etc and are just used to keep things easier to read. Same goes for PC1, PC7, PB3, PB5...the last number in the statement is the value it represents. So...

PORTD |= (1<<PD3);

is exactly the same as:

PORTD |= (1<<PB3);

Except it won't make as much sense when you look at the code a few months later. Each statement tells the MCU to read the address 0x0B, bitwise OR that reading with 00001000 (1<<3) or (decimal 8) and store the results back into 0x0B. Then internally it will apply 5v to any pin that has a corresponding 1 in the value stored in 0x0B (PORTD).

July 08, 2012
by TuffLux
TuffLux's Avatar

How can:

PORTD |= (1<<PD3);

equal

PORTD |= (1<<PB3);

When PD3 is referencing PIN5 and PB3 is referencing 17?

July 09, 2012
by Rick_S
Rick_S's Avatar

PD3 and PB3 are just constants defined in an included file. Both are equal to the number 3 so when you write either one, it is equivalent to the compiler because it substitutes 3 for them like this:

PORTD |= (1<<3);

The constants are just there to make it more understandable from a programming point of view and are defined as the bit position in the register for a given pin on the micro.

July 09, 2012
by TuffLux
TuffLux's Avatar

If that is the case, how does the microcontroller know which pin you're trying to change?

Ultimately you're saying that:

(1<<PB3) and (1<<PD3) boil down to (1<<3)

July 09, 2012
by sask55
sask55's Avatar
 PORTD |= (1<<PD3)

is a kind of short hand way of writing

 PORTD = PORTD | (1<<PD3)

Since PD3 is defined as 3 in a define file that is included. (1<<3) is a one Byte (binary 00000001) shifted left 3 times to give (binary 00001000)

The compiler is instructed to take the byte that is in the PORTB register OR it with (1<<3) binary 00001000. Then take the results of that OR operation and overwrite whatever is in PORTB with that new value. The results of this operation will be to set (to high) the PD3 bit in the PORTB register while maintaining all the other bits in that register as they where.

The PORTB on the left of the = sign tells the complier what byte to work with, the 1<<PD3 tells the complier what bit to set in that byte.

Darryl

July 09, 2012
by Ralphxyz
Ralphxyz's Avatar

Where is "PD3 defined as 3"??

PD3 is bit 4 of of PORTD (PD0, PD1, PD2, PD3, PD4, PD5, PD6, PD7).

And why would it be 3 and not 4?

Ralph

July 09, 2012
by sask55
sask55's Avatar

Ralph

“Where is "PD3 defined as 3"??”

I do not know where it is defined it will be in one of these included files

  #include <stdio.h>
  #include <avr/io.h>
  #include <avr/pgmspace.h>
  #include <inttypes.h>

It is defined as 3 because this number is used as a bit shift value not the actual bit location. or bit name reference. As you have pointed out the bit name references also start at zero not at one.

For example; if we shift left once from the LSB location PD0 00000001, (1<<1) we have set the bit at PD1 00000010. Shift once to set PD1, shift twice to set PD2 and so on.

“And why would it be 3 and not 4?”

Because if we shift four times left (1<<4) form PD0 we would end up setting the PD4 bit not the PD3 bit. Using (1<<PD3) to set the PD4 bit would really be confusing

Darryl

July 09, 2012
by Ralphxyz
Ralphxyz's Avatar

Thanks Darryl, I forgot the references I'll look at them again, it has been a while.

I believe "shift left once from the LSB location PD0 00000001" is the key to understanding this.

When doing bit shifting it always starts at bit 1 (LSB).

There is an assumption (which is correct) that one is starting out from a bit 1 location (00000001 LSB), not from a empty PORT 00000000 and shifting into the PORT which almost makes sense.

Ralph

July 09, 2012
by sask55
sask55's Avatar

Yes

The thing is you have to have something to shift. There is not really any meaning to shifting an "empty” port as you call it. The shift left and shift right operations just moves each bit of a byte left or right a number of times. If there are no bits set there is nothing to move. (1<<3) moves the bit values of the number 1 to the left 3 times. I believe that any other byte can be shifted in the same way. (2<<3) or (6<<3) are just as valid an operation.

(9<<3) expressed in binary would be 00001001 becomes 01001000

We are using the value of 1 as a starting point because with one only the LSB is set. So we are just shifting that one set bit to set a new bit location where we need it.

Darryl

July 09, 2012
by pcbolt
pcbolt's Avatar

Ralph -

“Where is "PD3 defined as 3"??”

Good question. If you look in:

#include <avr/io.h>

You will find a whole bunch of "ifdef" statements pertaining to the model number of the chip you are using. One of them "iom168p.h" (I think) is included here. I believe you will find the PD3 defines in there. It is not obvious because the defines are kind of nested inside that file in an unusual way.

July 09, 2012
by Rick_S
Rick_S's Avatar

Ralph said:
" I believe "shift left once from the LSB location PD0 00000001" is the key to understanding this.

When doing bit shifting it always starts at bit 1 (LSB). "

(1<<PB3) = (1<<3) = (00000001 << 00000011) = 00001000 = 4
(2<<PB3) = (2<<3) = (00000010 << 00000011) = 00010000 = 16

The shift starts at whatever number you tell it to. 1<<3 = 8 where 2<<3 = 16

Rick

July 09, 2012
by sask55
sask55's Avatar

Rick and I are saying the exact same thing. bit shift left moves each bit left and fills the new bit values on the right with 0's any bits that are moved out of the byte on the left are lost.

(29<<5) that is 00011101 shifted left 5 times will be 10100000

Darryl

July 10, 2012
by Rick_S
Rick_S's Avatar

Yep Darryl,

We are saying the same thing...

Oh, and I need to correct a typo in my post above...

Where I had:

(1<<PB3) = (1<<3) = (00000001 << 00000011) = 00001000 = 4

It should read:

(1<<PB3) = (1<<3) = (00000001 << 00000011) = 00001000 = 8

The reason I was trying to help illustrate this was for Ralph where he said:

" -- There is an assumption (which is correct) that one is starting out from a bit 1 location (00000001 LSB), not from a empty PORT 00000000 and shifting into the PORT which almost makes sense. -- "

I was pointing out that it starts at location 1 because that is the number we tell it to shift. If we told it to shift 2<<3, the bit being shifted would start at location 00000010.

July 10, 2012
by Ralphxyz
Ralphxyz's Avatar

Yeah, thanks Darryl and Rick.

I was pointing out that it starts at location 1 because that is the number we tell it to shift.

Of course.

Ralph

July 10, 2012
by Ralphxyz
Ralphxyz's Avatar

It's funny I have been using the (1<<PB3) syntax for a couple of years now and just accepted that it worked. I never really thought about why it was working, I knew what it was supposed to do but had never really thought about it until now.

So thanks once again Darryl and Rick.

Hey TuffLux, have you got a handle on where the PORT comes from now any other questions?

Ralph

July 10, 2012
by sask55
sask55's Avatar

Your welcome Ralhp

It seams we may be a little off topic with some of this, or maybe not. I hope we have at least managed to answer some of the original questions.

Darryl

July 10, 2012
by sask55
sask55's Avatar

Man! my keyboarding is bad I meant Ralph, sorry.

July 10, 2012
by TuffLux
TuffLux's Avatar

Wow. I'm surprised there was so many responses to this question. It's interesting how a discussion on PORTS has become more of a discussion on bitshift.

I understand where and what the PORTS are. My concern was that a lot of the documentation references PORTA, B, C, D and I couldn't find PORTA in any of the ATMega168 datasheet.

July 10, 2012
by Rick_S
Rick_S's Avatar

That's what I was eplaining in my first post. There are other Atmel micro's that do have a PORTA. For instance an ATMEGA128 has PORTA, PORTB, PORTC, PORTD, PORTE, PORTF, and PORTG.

August 03, 2012
by virtual
virtual's Avatar

Theres something interesting to dig into with the original question: the PORT, PIN, DDR etc actually just reference memory locations via pointers. So setting DDRC bit 5 to one boils down to this ((volatile uint8_t)0x27 |= 1<<4 which says that an 8 bit wide variable located at address 27 needs to have its 5th bit set.

The compiler converts these low addresses into indirect memory addressing via x, y or z registers and would have maybe 5 instructions. 2 to load the address into a 16 bit register, 1 to load the current value of the register, 1 to do the logical or and 1 to store the result back.

Post a Reply

Please log in to post a reply.

Did you know that a square wave sounds different than a sine wave? Learn more...