Improve of digitalWrite() possible - save 3.6 us

Hello,
I'm developing a time critical application. Therefore I have tested a little bit the possibility of cbi and sbi macro. This saves me 3.6 usec per set or reset an digital output against digitalWrite() function. But I don't wan't loose the good readability of my code. Therefore I have created a macro named "digWrite()" for the Atmega168. If anyone have the same timing problems, simply use the macro below and substitute for example "digitalWrite(1,LOW)" with "digWrite(1,LOW). Eventually this can be inserted inside the core of arduino?

Good luck, Thomas

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#define digWrite(bit,state) (state==LOW ? ((bit) < (8) ? cbi(PORTD, bit) : ((bit<14) ? cbi(PORTB, bit-8):cbi(PORTC, bit-14))):((bit) < (8) ? sbi(PORTD, bit) : ((bit<14) ? sbi(PORTB, bit-8):sbi(PORTC, bit-14))))

Of course your macro won't be 'portable' with some of the different AVR chips that the IDE supports as the say 328 and 1280/2650 uses different ports/pins for the same abstracted 'arduino pin' numbers. Maybe add some conditional code that deals with processor types?

Lefty

Hello,
I have now made some good oscilloscope measurements. I have switched on and off a digital output 100 times and measured the resulting frequency.
Using the digitalWrite() function the period of switch on/off is 7,36us --> 135,8kHz (each switch takes exactly 3,68us).
Using the macro digWrite() the period of switch on/off is 250ns --> 4MHz (each switch takes exactly 125ns).

This is 29.45 times faster and saves you 3.555us per switch!

@Lefty: yes you are right, but with some compiler switches (#ifdef atmega168 ... #endif) this should be possible. I'm not familar enough with arduino core to make this portable for all.

Thomas

intereting timing, question that came in my mind are these figures true for every pin ? Looking at your macro I have some doubts. Nevertheless well done.

Also I believe the Arduino development team has a fastdigitalread and fastdigitalwrite functions already developed and waiting for some future IDE release?

Lefty

I'm aware of three versions.

The Teensy core includes a variation (speed optimized for all cases) that uses inline functions...
http://pjrc.com/teensy/teensyduino.html

jrraines built a library variation that uses macros...
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1267553811;start=0;action=threadpagedrop

And, I believe, Arduino 1.0 includes a variation...

Many thank's for the last two replys!

Anyone knows about the real timing benefit using this new solutions, especially using code of arduino 1.0 Beta? If not I could test it with my Arduino NG rev. c board.

Thomas

You are certainly welcome to perform your own testing but all three have been tested to death. I vaguely recall that I've written about the Teensy performance in the forum. I believe @jrraines provided performance results in the forum topic listed above. The Arduino implementation has been discussed on the Developers List...
http://www.google.com/search?q=fast+digital+site:arduino.cc%2Fpipermail%2Fdevelopers_arduino.cc%2F

If all the parameters are constants, the function call typically reduces to: a single machine instruction, three machine instructions (not ISR safe), or five machine instructions (ISR safe).

If any parameter is a variable, then the code produced varies considerably depending on the implementation.

If all the parameters are variable, then the code produced is close to the same as what is in the Arduino 0022 core.

I did this a while ago. This is my code. It uses the C preprocessor to generate the PORTB, PORTD stuff. It also uses C++ templates to map the pin numbers at compile time.

This pin mapping works with the Duemilanove board. I think it has an ATMega328p processor. I don't remember. But anyways, you can always do...

#include <avr/io.h>
#ifdef __AVR_ATmega328P__
  // Add code here
#endif

to add support for other Arduino boards.

#ifndef PIN_TRAITS_H
#define PIN_TRAITS_H

#include <stdint.h>

#define __digital_high(L, N) PORT ## L |= _BV(N);
#define __digital_low(L, N) PORT ## L &= ~_BV(N);

inline void digital_write(boolean state);

template <uint8_t Pin> class pin_traits;

#define PIN_TRAITS_GENERATOR(L, N, P)       \
template <> class pin_traits<P>             \
{                                           \
  public:                                   \
  static void digital_write(boolean state)  \
  {                                         \
    if (state == HIGH)                      \
      __digital_high(L, N)                  \
    else                                    \
      __digital_low(L, N)                   \
  }                                         \
};

// Digital pins 0 - 7 are part of the D group
PIN_TRAITS_GENERATOR(D, 0, 0)
PIN_TRAITS_GENERATOR(D, 1, 1)
PIN_TRAITS_GENERATOR(D, 2, 2)
PIN_TRAITS_GENERATOR(D, 3, 3)
PIN_TRAITS_GENERATOR(D, 4, 4)
PIN_TRAITS_GENERATOR(D, 5, 5)
PIN_TRAITS_GENERATOR(D, 6, 6)
PIN_TRAITS_GENERATOR(D, 7, 7)

// Digital pins 8 - 13 are part of the B group
PIN_TRAITS_GENERATOR(B, 0, 8)
PIN_TRAITS_GENERATOR(B, 1, 9)
PIN_TRAITS_GENERATOR(B, 2, 10)
PIN_TRAITS_GENERATOR(B, 3, 11)
PIN_TRAITS_GENERATOR(B, 4, 12)
PIN_TRAITS_GENERATOR(B, 5, 13)

#undef PIN_TRAITS_GENERATOR

#endif /* PIN_TRAITS_H */

/** Same as digitalWrite(), except the pins need to be known at compile
    time.  This function is roughly 10 times faster than digitalWrite().
*/
template <uint8_t N>
inline void digital_write(boolean state)
{
  pin_traits<N>::digital_write(state);
}

So to set pin 5 to HIGH, you could do

digital_write<5>(HIGH);

And, I believe, Arduino 1.0 includes a variation... Arduino Forum

I am wrong. Arduino 1.0 does NOT include a variation of digital*Fast.

I am wrong. Arduino 1.0 does NOT include a variation of digital*Fast.

It should.

smeezekitty:

I am wrong. Arduino 1.0 does NOT include a variation of digital*Fast.

It should.

It will. Arduino 1.0 is at beta so features still to come.

Lefty