Go Down

Topic: Fast alternative to digitalRead/digitalWrite (Read 8 times) previous topic - next topic

sixeyes


Yes, I have looked at v3 GLCD and it contains the kind of macros I am trying to avoid.

DigitalPin generates the fastest and smallest possible code for reading and writing I/O port on 328 Arduinos and ports A-G on the Mega.

Sorry I don't think I made myself clear. I was referring to the discussion on pin groups. v3 GLCD has some clever code for recognising groups of pins are on the same port and generating faster code than accessing pins individually.

I think the DigitalPin template will be really useful but at present where speed is important I'm accessing the ports directly.

BTW why is access to ports H+ on the Mega slower?

Iain

fat16lib

Even for pin groups the overhead of combining pin access often is slower and takes more code.

Here are some examples (C++ statement followed by generated code):

To write one bit for ports A-G sbi/cbi is the winner:
Code: [Select]

  PORTB |= 0X1;
   c:   28 9a           sbi     0x05, 0 ; 5


With two or more pins, combining bits requires more instructions.  You also need a cli/sei to make it atomic for general use.
Code: [Select]

  cli();
   c:   f8 94           cli
  PORTB |= 0X11;
   e:   85 b1           in      r24, 0x05       ; 5
  10:   81 61           ori     r24, 0x11       ; 17
  12:   85 b9           out     0x05, r24       ; 5
  sei();
  14:   78 94           sei

So it is hard to save time or code by combining bits.  You do get all bits changing state at the same time.

The best plan for a pinGroup is to dedicate an entire port so you don't need to OR or AND bits and worry about atomic operations.  That's why I think a DigitalPort class is best.

For Mega ports H, J, and K cbi/sbi can't be used since the port address is too large.  Setting a single bit in these port is slow:
Code: [Select]

  cli();
   c:   f8 94           cli
  PORTH |= 0X1;
   e:   e2 e0           ldi     r30, 0x02       ; 2
  10:   f1 e0           ldi     r31, 0x01       ; 1
  12:   80 81           ld      r24, Z
  14:   81 60           ori     r24, 0x01       ; 1
  16:   80 83           st      Z, r24
  sei();
  18:   78 94           sei



Graynomad

#12
Jan 14, 2012, 10:49 pm Last Edit: Jan 14, 2012, 10:59 pm by Graynomad Reason: 1
Quote
On a Mega a PinGroup could become quite large so a pingroup should have its max size as param:

I would suggest limiting to 8 pins anyway.

Quote
Furthermore must it set pins of the same register simultaneously? If pins are in different registers this is not possible ...

Not necessarily, if the fact that pins are on the same port can be detected great, but even if behind the scenes it degenerates to a stack of single pin writes (as you show) at least the application code will be simpler and more readable.

Quote
You could write a templates for a given number of pins.  Not so neat but works.
TwoPinGroup<Pin0, Pin1>
ThreePinGroup<Pin0, Pin1, Pin2>

I'm not strong on C++ but can't you have 8 constructors with different numbers of parms, that way there is only a single pinGroup object and the syntax is the same up to 8 pins.

As for simultaneous writes, it would be nice if the class auto detected pins on the same port but I don't think that's really important, maybe a second Port class that boils down to
simple "PORTx =" code with .bitSet() and .bitClear() methods that just do "PORTx != val" etc. At least that will add to the current HAL and isolate beginners from such "complex" ideas.

OTOH if all this can be rolled into a single class even better.


______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

pYro_65

Quote
As for simultaneous writes, it would be nice if the class auto detected pins on the same port


The only purpose for my WriteMany class it to write like pins. A convenience factor is not really on my list at all.

Quote
imho a pingroup would have an internal collection to which runtime pins can be added and removed (don't know the purpose for remove yet)
The collection is not sorted, so the adding order applies.


Also pins probably should not be runtime, assigning runtime pins more than once doesn't really make sense unless you are physically re-wiring your hardware while the Arduino is on.

Also they are not usable values with digitalPin library and will have to resort to some slower lookup table version. making it more efficient to just individually write the pins.

Non-type template parameters also have no storage overhead, no SRAM is used to store the parameters past compilation as the compiled code is completely customised to those parameters. The alternative is a generic read/write that must look up the contents with every operation.

My code as tested for 3 and 4 pins produces less instructions on like pins rather than doing an individual write on each pin. When I finish the 4 & 5 pin writer I'll post it.

I'm not limiting this code to 8 pins though, The benefits my HAL will theoretically receive from writing any number of pins out ways this limitation by far.

fat16lib

Writing multiple pins seems like a good idea, at least in the abstract.  There are cases where dedicating an entire 8-bit port to a device makes sense but this is not write multiple pins.

I have written a lot of bit-bang code for SPI, I2C, and various devices.  When I get to real hardware, my abstract write multiple ideas never seem to help.

Does anyone have a situation with real hardware where an existing implementation would be improved by write multiple with three or four pins.  The pins must be restricted to a single port.

The best example I have is something like an LCD display.  In this case the restriction that all pins are on the same port is too severe.  The library LiquidCrystal allows any pins and that doesn't add much complication.  Here are the byte and nibble write functions.

Code: [Select]

void LiquidCrystal::write4bits(uint8_t value) {
  for (int i = 0; i < 4; i++) {
    pinMode(_data_pins[i], OUTPUT);
    digitalWrite(_data_pins[i], (value >> i) & 0x01);
  }
  pulseEnable();
}

void LiquidCrystal::write8bits(uint8_t value) {
  for (int i = 0; i < 8; i++) {
    pinMode(_data_pins[i], OUTPUT);
    digitalWrite(_data_pins[i], (value >> i) & 0x01);
  }
  pulseEnable();
}

Note this code has pinMode in the write function.  LCD displays can be written or read so your write multiple should also support read.

It's the details of real complex devices that seems to kill the advantages of a library for accessing multiple pins.

Go Up