Go Down

Topic: How about adding digitalToggle(pin)? (Read 2072 times) previous topic - next topic

fat16lib

Jul 28, 2012, 05:45 pm Last Edit: Jul 28, 2012, 06:03 pm by fat16lib Reason: 1
Here is a neat feature of AVR I/O ports:
Quote

The Port Input Pins I/O location is read only. However, writing a logic one to a bit in the PINx Register, will result in a toggle in the corresponding bit in the Data Register

This means that you could implement an efficient atomic digitalToggle(pin) function.  It should be faster than digitalWrite().  

It would be handy when you need to pulse a pin.  You just call digitalToggle(pin) twice.

I implemented toggle() like this as an inline function in a digital I/O class:
Code: [Select]

 void toggle() {*pinReg_ = bit_;}

This function executes in 8 cycles or 500 ns on a 16 MHz CPU.  This allows generation of a 500 ns pulse.

Not as good as the 125 ns that is possible when a pin number is a constant but is about 8 times faster than digitalWrite() which takes about 4 us.

Paul Stoffregen

#1
Jul 28, 2012, 07:36 pm Last Edit: Jul 28, 2012, 07:38 pm by Paul Stoffregen Reason: 1
Single instruction (with const inputs) digitalWrite has existed since late 2009.  The idea was originally proposed by Ben Combee on the Arduino developer mail list in early 2009.  Before Ben's message, I was not aware of gcc's _builting_const_p().  Ben deserves much of the credit.  I saved his message and used his idea.

I wrote the first implementation in October-November 2009 and posted it to the list.  I started shipping it as part of Teensyduino in December 2009.  Originally I used static inline functions.  The Arduino developers asked for a macro, which I wrote.  They then asked if someone would create a macro for Arduino Mega, which I did within a matter of days.  It's been sitting unused on the issue tracker ever since:

http://code.google.com/p/arduino/issues/detail?id=140

Actually, as you can see, others have attempted to pick this up and use it over the years.  At one point it was even committed to a branch of Arduino, but of course it's never made its way into an official release.

I'm not saying a toggle function isn't a good idea.

But if digitalToggle's main purpose it to facilitate faster output, a far better solution has existed and sat unused for since November 2009.

Paul Stoffregen

#2
Jul 28, 2012, 07:44 pm Last Edit: Jul 28, 2012, 08:04 pm by Paul Stoffregen Reason: 1
I believe SAM3 chip planned for Arduino Due lacks a pin toggle register.  If this does become an official Arduino function, it will need to be implemented in as a read-modify-write sequence with interrupts disabled on Due.

fat16lib

Paul,

Of course the case of fast write for a constant pin number has been around for years.  I have posted several versions based on classes and static functions that compile to a single sbi or cbi.  I like using static const arrays which the compiler optimizes out.  I did that in December 2009 for software SPI in SdFat but didn't post it until I saw your macros. 

For classes I use templates which forces a constant pin number without the gcc __builtin_constant_p(pin).

Toggle is useful for a constant pin number since it will toggle a pin in 125 ns.  You don't need to know the state of a pin to toggle it.

For a variable pin number it is fast since you don't need to do the atomic stuff.  You just write the bit you want to toggle to the PIN register.

As for digital I/O on SAM3X or ARM Cortex M in general, I hope I am not limited to the least common subset between AVR and ARM.  This is another reason I can't believe in AVR/ARM compatibility.

I don't expect to see toggle In the Arduino core but I added it to my digital libraries and was surprised how useful it can be.  I will be posting a new version of these libraries soon.

fat16lib

Guess I didn't make my case for toggle very well.  Here is an example where toggle is very useful.

In protocols like SPI, clock can have a polarity and signals have a phase.  This leads to the four SPI modes and makes handling clock messy in a general software SPI library.

There were lot of hard to understand lines like this where all I really wanted to do was toggle clock:
Code: [Select]

  fastDigitalWrite(SckPin, MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode));

The code is much cleaner when write is replaced with toggle:
Code: [Select]

  fastDigitalToggle(SckPin);

I am finding far more cases than I expected like the above where toggle is useful.

There was no speedup in the above case since all arguments are constant but in many cases you get a speedup and simpler code.

Graynomad

Quote
I believe SAM3 chip planned for Arduino Due lacks a pin toggle register.

True AFAIK.

I think a toggle function is good, even if it does degrade to a read-modify-write sequence on some processors.

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

westfw

Quote
I believe SAM3 chip planned for Arduino Due lacks a pin toggle register.

That's surprising, since Atmel has been putting that function in their newer AVRs, and many competing ARM chips have a toggle function.  (Although that's frequently to address otherwise poor pin manipulation rates...)  (Hmm.  Perhaps not.  I was pretty sure STM32F had this, but I can't find it in the manual.)

I hadn't thought about how useful a pin toggle function would be when implementing a bit-banged serial protocol with arbitrary clock polarity.  Thanks for pointing that out!

Graynomad

Rob Gray aka the GRAYnomad www.robgray.com

westfw

It occurs to me that adding pin toggling to a device that doesn't support it in hardware could be particularly expensive, depending on what primitive are available.

Graynomad

#9
Jul 30, 2012, 09:20 am Last Edit: Jul 30, 2012, 11:07 am by Graynomad Reason: 1
I guess it's basically

Code: [Select]
get bit mask
get port address
read port
XOR with bit mask
write port


as opposed to

Code: [Select]
get bit mask
get port address
write port


EDIT:

On an LPC is would be this

Code: [Select]
LPC_GPIO0->NOT = 4;

To toggle pin 2 on port 0. So if you hard code the values that's it, but of course a generic function that works on logical pins needs more than that because it has to index into an array or whatever to get the port address and bit mask.

My code does this

Code: [Select]
port = pinPort(pin);
*gpio_regs[gpio_reg][port] |= word_bits[pin - (32 * port)];


I don't know how fast it is and will probably look at optimising later.

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

WizenedEE

I have seen the lazy do:
Code: [Select]

digitalWrite(pin, !digitalRead(pin));


I think toggling is a good idea.

fat16lib

I posted a new version of fast I/O libraries with toggle() as DigitalPinBeta20120804.zip http://code.google.com/p/beta-lib/downloads/list.

The libraries support standard 168/328 Arduino, Mega, Leonardo, Teensy, Teensy++, and Sanguino.

The DigitalPin class provides very fast inline functions. DigitalPin is a template class and pin numbers must be specified at compile time. 

For 328 pins and low address Mega pins read(), toggle(), and write() execute in two cycles or 125 ns for a 16 MHz CPU.

The main member functions for the DigitalPin class are:
Code: [Select]

void config (bool mode, bool level);
void high ();
void low ();
void mode (bool pinMode);
bool read ();
void toggle ();
void write (bool value);


The library also contains these static inline functions similar to digitalRead()/digitalWrite().  Pin number must be a constant.
Code: [Select]

static bool fastDigitalRead (uint8_t pin);
static void fastDigitalToggle (uint8_t pin);
static void fastDigitalWrite (uint8_t pin, bool level);
static void fastPinConfig (uint8_t pin, bool mode, bool level);
static void fastPinMode (uint8_t pin, bool mode);


There is also a Software SPI class that runs at about 2 MHz.  It is also a template class with compile time pin numbers and SPI mode.  Modes 0 - 3 are supported MSB first.  LSB first would be easy to implement.

The member functions are:
Code: [Select]

void begin ();
uint8_t receive ();
void send (uint8_t data);
uint8_t transfer (uint8_t txData);


Go Up