3
\$\begingroup\$

I'm used to using PIC microcontrollers and they have only one register for input and output.

Now I'm studying MSP430 and I see they provide separate registers for input and output.

So, why is it useful to provide separate registers for input and output?

EDIT

I'm reading this book: MSP430 Microcontroller Basics

enter image description here

Normally this book compares MSP430 with PIC16 family. PIC10/12/16 don't have the register LATx. But PIC16 enhanced has.

\$\endgroup\$
2
  • \$\begingroup\$ are you talking about PxIN PxOUT? \$\endgroup\$
    – jsolarski
    Jul 6 '11 at 2:23
  • \$\begingroup\$ @jsolarski, Yes. \$\endgroup\$ Jul 6 '11 at 10:30
8
\$\begingroup\$

I don't know the MSP430 in any detail, but have a lot of experience with PICs. PICs don't specifically have a separate register for input and output, but many of them do in practise. The PORT register contains the immediate pin states, for input and output. The LAT resgister contains the last-written values, so I suppose you can call it a output register. If you use PORT for input and LAT for output, then you have separate input and output registers. Just ignore that PORT could be used for output too, with slightly different properties than LAT.

The low PICs from 16 on down don't have LAT registers, only the PORT register. You therefore use the same register for input and output. That's no big deal since reading and writing are separate operations.

There is one wrinkle with this that sometimes catches people, and much superstition-based programming has evolved around it. The issue is that the PORT register always reflects the actual pin states. This may sound simple and harmless, but you can get into trouble when the external circuit holds the pin in the opposite state it was written in. Note that enough capacitance on the pin will do this, at least for a little while.

This becomes a problem when you perform a read-modify-write operation on a port register shortly before having changed a output pin. Let's take the really obvious case of ORing 0 into the PORT register. OR is a read-modify-write operation. The OR instruction will read the existing register value, perform the OR, then write the result back to the register. Now imagine the previous instruction wrote a new value to a output pin, but that pin hasn't had time to get to its new state yet. The read part of the OR instruction reads the current PORT register value, which is not the most recent value written to it because the pin hasn't slewed to its new state. The OR with 0 doesn't change anything, so the old state of the PORT register is written back to it, essentially cancelling the previous write.

Now you may say that ORing 0 into a PORT register is silly. In most cases that's true, but that was just to make a obvious example. Consider that the BSF and BCF (bit set and bit clear) instructions actually perform a read-modify-write on the whole port register. Consider the instruction sequence:

     banksel portb
     bsf   portb, 1
     bsf   portb, 2

Let's assume all port B pins are set to outputs and are all low to start with. After the first instruction RB1 will start going high. Due to capacitive loading, RB1 is still low and PORTB therefore reads 0 when fetched as part of the second instruction. Bit 2 is now set, so the value 4 is written to PORTB. RB1 will now go low again since 0 was written to that bit. RB2 will start going high. The net result of this instruction sequence could be that only RB2 is high, not RB1 and RB2 as probably intended.

The LAT register was introduced to avoid this problem. It holds the last written value, not the actual instantaneous pins state. If this instruction sequence was performed on LATB instead of PORTB, both RB1 and RB2 would be driven high at the end regardless of how slowly they might get there.

So what do you do? On a PIC 18 and higher read from PORT and write to LAT, and there'll be no problems. On the other PICs, avoid any read-modify-write operation on PORT until you know all pin states have settled. Some people will tell you to always use a shadow copy, modify that, then write that to the PORT register. That's just silly voodoo programming, of course. You know your circuit. Most of the time a single NOP between a write and any read-modify-write is all that's needed. If the pin can't get to its new state in at least one whole instruction cycle, then the circuit should most likely be fixed anyway. In rare cases shadow register can be useful, but those are rare cases indeed. Mostly they are just a waste of cycles, RAM, and one more thing to mess up, especially for the kind of people that blindly follow rules like "always use a shadow register". A much simpler answer is that those kind of people should just stick to a PIC 18 or higher.

\$\endgroup\$
1
  • \$\begingroup\$ Shadow registers can be essential when dealing with open-collector outputs. \$\endgroup\$
    – supercat
    Jul 6 '11 at 4:59
1
\$\begingroup\$

Some processors can perform an atomic read-modify-write on a memory address; some cannot. On an ARM, for example, an instruction can read or write a memory address, but not both. Consider the sequence:

  ldr r0,[r1] ; Assume R1 points to the port
  orr r0,#1
  str r0,[r1]

Now suppose that, between the ldr and the str, an interrupt occurs and runs the following:

  ldr r2,[r1] ; Assume R1 points to the port
  orr r2,#2
  str r2,[r1]

The interrupt would set bit 1 (value #2) of the port, but that change wouldn't be reflected in the mainline code's copy of r0. Consequently, when the mainline code performs the str r0,[r1] instruction, the interrupt's change could be undone.

Various CPUs have different ways to facilitate updating ports through both mainline and interrupt code; many of them entail having more registers than would be needed if such operation were not a consideration.

\$\endgroup\$
1
\$\begingroup\$

There is a comfort level in the idea that you have one register to write to for output only. And one register you will only ever read for sampling the input. I think the comfort comes from not writing back to some of those input pins. You also are able to have the input port always reflect the state of the pin independent of the direction setting. With a single register you do read-modify-writes in order to change output registers, which carries the assumption that the read of an output bit reflects the state of what you wrote to that bit not the actual state of the bit. So you can separately manage driving the outputs and sampling the line.

If you sample a number of vendors you will find a few different methods, some vendors provide all the methods, some do not. One method is the one you are used to. Somewhere you define the input/output state for each bit, and in a separate place you have one register for both input and output. The bits declared output when written latch what you wrote and try to drive the I/O pin that way, when read the output bits return what you wrote (not necessarily the state of the I/O line). Input bits always return the state of the I/O pin. Read-modify-writes are used to change individual outputs without affecting others.

Another method is the one you have just discovered, to have a read/write register for output only, and a separate essentially read only register for input. The input register can reflect the state of the line or not for outputs depending on the vendors design choice, but for inputs you read and mask like you would with a single in/out register. For writes you perform the same read-modify-write routine to set individual bits. Or write the whole register for busses, etc. But you use a separate port output register.

Another method, useful for I/O that is single or a few bits but not a whole 8 or more bit port, is to have a set output port register that any bit that is set in the value you write to the port causes the output I/O (if configured as an output) to change to a one. And a separate clear output port register where any bit set when you write to it (for bits that are outputs) CLEARs the output port. And a third register when read returns the state of the I/O pin, here again the output pins can be the actual state or the last thing you wrote to that output. So if the direction register has a 1 for an output and 0 for an input and you write a 0x3 to that direction register making bit/pin 0 and 1 outputs and the others input. When you write a 1 to the set output register it will change the output on pin 0 to a high voltage. When you write a 0x2 to the clear output register it will drive a zero voltage on output pin 1. When you write 0xFF to the clear output register, all I/O declared output will be driven to zero volts. At first it feels weird writing a one to get a zero output on an I/O line but you get used to it pretty quick when you stop having to do read-modify-writes and even better you always write the pin number or numbers write 1<<3 to clear pin 3 and 1<<3 to set pin 3.

Each solution has their pros and cons and not one of them is a clear winner. For situations where you want an 8 bit parallel bus/port/output having a single register that you write a byte to that causes all 8 I/O pins to reflect the state of each bit (zeros are zero volts and ones are the supply voltage). For bit banging a SPI or I2C or something like that bus, the latter is ideal, write a 0x10 lets say to one register to assert a single output pin (without disturbing any other output in) and write a 0x10 to another register to clear that single output pin. There may be times and I/O configurations (weak driver with a pull up lets say) where you want to read the actual state of an output pin not the last thing you wrote to it, yet be able to write stuff to it.

You will see a trend with vendors, one vendor will often/always use one method across a family of chips, sometimes different families. And another vendor likes their method and uses it for their whole family. Once you have used one of their chips your code is sometimes, and knowledge/experience is definitely, portable from one of their products to another. As a developer you want to get comfortable with the various vendors solutions, in a lot of cases the setting and clearing of outputs and sampling of inputs is the trivial part, the configuring to enable a port and/or pin making it an input or output, pulled up or not, open collector or not, etc can be difficult at best. When implementing bi-directional data lines for example (I2C for example) you need to know that for some vendors you configure it as a pulled up thing that you can read/write, sometimes you make it a driven output when you write, and make it a pulled up input when you read. And some you make it a driven output when you write, and a high-Z input when you read and the pull up has to be on the line off chip somewhere.

I have actually worked on hardware where you couldn't read the state of the output pins, you had to keep track of them in software, and essentially the output port register was write only. Rare to see these days but I guess it is yet another solution you might find out there.

\$\endgroup\$
2
  • \$\begingroup\$ The Apple II had quite a few write-only "soft switches" which would be turned on by accessing a certain address, and turned off by accessing another (only the address mattered--not the data). The Apple //e added read addresses for many of them, though the read addresses bore no relation to the write addresses. Having the partition into a read-only and write-only universe may seem odd, but in practice it doesn't cause too many difficulties. In cases where an I/O register is divided among functions, some of which may be accessed from interrupts while others are accessed from mainline code... \$\endgroup\$
    – supercat
    Mar 8 '13 at 22:29
  • \$\begingroup\$ ...being able to set and clear some bits of a register without affecting others is a big improvement over having to do a read-modify-write. Read-modify-write is particularly horrible on registers which can be asynchronously changed by external events. Why do people design such things? \$\endgroup\$
    – supercat
    Mar 8 '13 at 22:35
0
\$\begingroup\$

There are usually multiple registers if there are more than two things a pin can do.

The Atmel AVR has three registers for a PIO port: DDR, PORT, and PIN. Each bit corresponds to the PIO with the same number. PIN always indicates the current level of the pin, regardless of its direction. DDR sets the direction for a pin: 0 means input and 1 means output. PORT sets the drive level (0 = GND, 1 = VCC) if the pin is an output and the pullup state (0 = no pullup, 1 = pullup) for an input.

Another thing that is really handy with multiple registers: if you have one register per thing you want to do with a peripheral, so that one write makes something happen, and you can avoid read-modify-write, then you can easily avoid race conditions (i.e. something interrupts between the read and write of a read-modify-write and changes the register) and keep code size down (by not needing to turn interrupts off before read-modify-write and back on after).

The Atmel ATSAM3U (Cortex-M3 core) has over 20 registers per PIO port. They control:

  • Whether the pin is PIO or peripheral controlled
  • Output enable
  • Input filter enable
  • Output drive
  • Pin state read
  • Interrupt on change
  • Push-pull or open-drain output
  • Pullup resistor
  • Peripheral A or B selected
  • Glitch or debounce filter setup

Most of these settings are in groups of three registers:

  • A status register, read-only, which shows the status of the function per pin,
  • an enable register, write-only, where writing a 1 to a bit sets the corresponding bit in the status register, and writing a 0 to a bit leaves it alone, and
  • a disable register, write-only, which works just like the enable register except writing a 1 clears the corresponding bit in the status register.

To illustrate, say you want to enable the output on pin 5, only, on port A. On the AVR, you'd have to write:

PORTA |= 0x20;

which reads PORTA, sets bit 5, and writes it back. The AVR architecture has a specialized instruction (sbi) that does this in one instruction for some registers, but not all of them on the bigger chips, so you have to be careful. If PORTA weren't covered by the sbi instruction, then you'd have to write:

disable_interrupts();
PORTA |= 1 << 5;
enable_interrupts();

(And to really do it right, you'd have to save the state when disabling interrupts, so you don't accidentally reenable interrupts if called while they are disabled.)

But on the SAM3U, you just write:

PIOA.OER = 1 << 5;

and be done.

(Actually, it's only PIOA.OER in C++; in C, you have to use a big #define name because of lack of namespace control. Or, if you use templates in C++, it's PIO::OutputPin<'A', 5>::Setup(), which turns into the above code, but can be combined with other PIO setups to set all the PIO registers at once. I digress...)

\$\endgroup\$
0
\$\begingroup\$

Multiple registers allow you to use more functions with one pin. MSP430 has a 9 registers per Px port which allows you to set it up the way you need it. Like using the built in pullups and pulldown resistors when you select the pin as input. Or setting the Pin as an output and selecting High or low, or even selecting the built in peripherals like clock source out (MCLK, SCLK, ACLK, ADC10, Timer_A).

\$\endgroup\$

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.