I need to capture a single parallel 'snapshot' from a 10-bit shaft encoder. I understand that I can read individual bits and shift them into position, but any given input pin might change while reading the bits sequentially, which would give an bogus value. In fact, given the asynchronous nature of the device this is guaranteed to happen on occasion.

I've read hints that it might be possible to combine pins into a 'port' for this purpose but I can't find any examples. Is this ability available only on certain processors? Or is there some way to latch the input pins while scanning them into a pair of bytes, to avoid them changing while I read?

If not I suppose I could use external latches but I'm hoping to avoid additional hardware.

It did occur to me that I could sync readings using an ISR triggered by a change in the LSB, but alas, this encoder outputs Gray code. :(

  • 1
    Which encoder? It seems odd you have to read all bits at precisely the same moment. You haven't said which Arduino you have but most of them are 8-bit processors so the maximum you can read atomically is 8 bits. – Nick Gammon Jan 18 '17 at 0:38
  • @NickGammon It isn't so odd, it's just asynchronous. I didn't specify which Arduino in case there are only some that support this, but I'd lean toward the Uno. I realize that with 8-bit ports I'd have to make two reads but I can handle the possibility of data smear with only two sequential reads as opposed to ten. – Jim Mack Jan 18 '17 at 0:50
  • 1
    What encoder? Link to datasheet? – Nick Gammon Jan 18 '17 at 1:48
  • 1
    @NickGammon Sorry, but I don't see the point. I'm not asking anyone to diagnose at a different level or to solve any problems. It's a fairly simple question about reading data in parallel which could have many applications. Maybe we could forget the encoder and think of it more generically...? – Jim Mack Jan 18 '17 at 4:07

If you would like to read multiple inputs, you can access port register directly. For 8 bit AVR:

DDRA = 0;            // set pins A0-A7 as input, modeInpit() in Arduino
uint8_t data = PINA; // read all A0-A7 inputs (8 bits)

To read more than 8 bits, you need 32 bit ARM based MCU/board like Arduino Due, other Atmel SAM, STM32Fxxx MCU etc. For STM32, there is Arduino like IDE available - http://www.stm32duino.com/

Even on 32 bit MCU you can read only 16 GPIO simultaneously (GPIO registers are 32 bit but handles 16 GPIO).

| improve this answer | |
  • Moving over here... the reason I'm asking about examples is that what I've read is inconsistent. Some docs, including the two you reference, use different names for the port registers. Is there a "port A" and a PINA instruction? Not in most docs, which say that "port C" is for the analog pins and PINC reads them. Also, you reference "GPIO_xx designations" which I can't locate in the docs I have, as a way to limit ambiguity. Any docs for that syntax? – Jim Mack Jan 18 '17 at 20:47
  • in AVR world, PINA and PORTA are both REGISTER names; they refer to same physical GPIOA (general purpose input output) pins belonging to A register (pin might designated as PA or GPIOA); for reading PINA is used, for writing PORTA register is used; so they are kinda aliases; same goes for PINB/PORTB, PINC/PORTC etc; exact physical pin and function (mostly) different on every MCU, so this is where the datasheet is needed; On Atmega328 UNO, PINC/PORTC (PC) are analog inputs (while on Atmega128 it is digital); GPIO is kinda universal naming for physical pins, IMHO it is preferred – Flanker Jan 18 '17 at 22:16
  • Nice drawings here for some Arduino GPIOs – Flanker Jan 18 '17 at 22:18

Without knowing the encoder in question (which you seem extremely reluctant to disclose for some reason that I have trouble understanding) it is hard to give a good answer. However ...

The 8-bit AVR processors can only read 8 bits atomically. This is indeed called a port. The Atmega328P (used in the Uno) organizes its I/O ports as ports B, C and D (I don't know where A went).

Exposed on the board are PORTB (bits 0 to 5), PORT C (bits 0 to 5) and PORT D (bits 0 to 7) - that's 20 pins in total.

Thus, port D is the closest you will get, as you could read 8 bits at once. However having said that, two of those bits are used for serial I/O so you couldn't use the serial hardware at the same time.

To read all of port D you could just do this:

byte foo = PIND;

(For inputting it is PINx, for outputting it is PORTx).

What could help is an I/O expander such as the MCP23017.

I'm hoping to avoid additional hardware

Well, that's a pity.

I'm a tiny bit surprised that the device doesn't support some sort of "latching" of the data which gives you a finite time to extract the information from it. However without reading the datasheet I can't comment further.

| improve this answer | |
  • It's not reluctance to disclose, it's a desire to avoid distractions. I know the hardware and how it operates; I'm constrained to use it; so the problem domain is 'reading data in parallel'. Thanks for addressing that, and for the pointer to the I/O expander. – Jim Mack Jan 18 '17 at 4:52
  • Given the answer from @Flanker, would it be possible to combine a read of PORT D (which I gather uses the digital pins) with a read of PORT A (which I gather uses the analog pins) to get a 16-bit wide value in two reads? – Jim Mack Jan 18 '17 at 4:54
  • 1
    I know the hardware and how it operates; - you might, I don't. – Nick Gammon Jan 18 '17 at 6:00
  • ... with a read of PORT A - where did you find PORT A on the datasheet? I can't spot it. – Nick Gammon Jan 18 '17 at 6:01
  • 1
    it's a desire to avoid distractions - distractions like the people who are trying to answer your question knowing what hardware you are talking about? – Nick Gammon Jan 18 '17 at 6:03

I mean, you could look up the port and the pin in the port and read in the portByte. This example does work:

//given two GPIOs on a port(PortC) eg: 12(right button) & 13(left button)     
byte portByte = (*portInputRegister(digitalPinToPort(rightButtonGpio)));
rightButtonPinHigh = portByte & (digitalPinToBitMask(rightButtonGpio));
leftButtonPinHigh = portByte & (digitalPinToBitMask(leftButtonGpio));

However, in my experience, your output will look pretty much the same as if you had done two sequential Arduino blessed digitalRead. I suppose you might (in the example above) get the leftButtonHigh value 1 instruction ealier, but I don't know if the register can even update at that resolution.

| improve this answer | |
  • digitalRead() can be super slow, something like 100 CPU cycles. In contrast, reading a port input register is a single cycle. And the register updates at the CPU clock speed, i.e. 16 million times per second on an Uno. – Edgar Bonet Jun 28 at 19:28

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.