Faster digitalWrite

Hello Experts

I have this Interrupt Service Routine in my project that flips pin 11 in my Arduino UNO, as you can see it uses the typical and slowest method of digitalWrite, I know that there is a faster way to flip the pin, by directly manipulating the pin on its port, I assume it is port B, can someone please advice me with the line of code to replace my code but will perform the digitalWrite much fatser

Cheers

ISR(TIMER1_COMPA_vect){//timer1 interrupt 
  if (activateHorn) 
  {
    hornValue = !hornValue;
    digitalWrite(hornPin,hornValue);
  }
}

Well not from that snippet because we have no idea as to what sort of variable you are dealing with. Please post all your code because often the problem is not in the bit you thought it was.

You might want to look at this How to get the best from this from this Forum before you proceed any further.

FYI


//********************************************^************************************************
// Atmega328: UNO, ProMini or Nano
//
// Note:
// PIND D0 to D7
// D0=0x01 D1=0x02 D2=0x04 D3=0x08 D4=0x10 D5=0x20 D6=0x40 D7=0x80
// PIND0   PIND1   PIND2   PIND3   PIND4   PIND5   PIND6   PIND7
//
// PINB D8 to D13
// D8=0x01 D9=0x02 D10=0x04 D11=0x08 D12=0x10 D13=0x20
// PINB0   PINB1   PINB2    PINB3    PINB4    PINB5
 
// PINC D14 to D19
// D14=0x01 D15=0x02 D16=0x04 D17=0x08 D18=0x10 D19=0x20
// PINC0    PINC1    PINC2    PINC3    PINC4    PINC5
//
//Macro definition for generating a 62.5ns pulse on UNO pin 13 i.e. PINB5
//
//Add this line to setup()
//pinMode(13,OUTPUT);
//
//62.5ns pulse
#define PULSE62_13     cli(); PINB = bit(PINB5); PINB = bit(PINB5); sei()    
//add this line where you want to generate a 62ns pulse
//PULSE62_13;
//
//For more delay
#define NOOP           asm("nop\n") 
  
//Add a NOOP to get 62.5ns more delay (for each NOOP)
#define PULSE125_13    cli(); PINB = bit(PINB5); NOOP; PINB = bit(PINB5); sei()
//PULSE125_13;

Toggle pin 13.

PINB = bit(PINB5);

Hello mate

Simply it is digital pin A2 in my code and it is an output pin, and all i need is to toggle it in my ISR

so, can you advice please how to replace the following two lines in my sketch for my UNO with a direct port manipulation. Cheers

digitalWrite(A2, HIGH);
digitalWrite(A2, LOW);

// myUNOMacros.h
// this file goes into the C:\Users\YOURUSER\Documents\Arduino\libraries\ folder.
// LarryD
//===============================================================================
// These Macros can be used with UNO board
//
//
// ATMEL ATMEGA8 & 168/328  ARDUINO UNO
//
//                  +-\/-+
//            PC6  1|    |28  PC5 (AI 5)
//      (D 0) PD0  2|    |27  PC4 (AI 4)
//      (D 1) PD1  3|    |26  PC3 (AI 3)
//      (D 2) PD2  4|    |25  PC2 (AI 2)
// PWM+ (D 3) PD3  5|    |24  PC1 (AI 1)
//      (D 4) PD4  6|    |23  PC0 (AI 0)
//            VCC  7|    |22  GND
//            GND  8|    |21  AREF
//            PB6  9|    |20  AVCC
//            PB7 10|    |19  PB5 (D 13)
// PWM+ (D 5) PD5 11|    |18  PB4 (D 12)
// PWM+ (D 6) PD6 12|    |17  PB3 (D 11) PWM
//      (D 7) PD7 13|    |16  PB2 (D 10) PWM
//      (D 8) PB0 14|    |15  PB1 (D 9)  PWM
//                  +----+
//
// **************************************************************************** 
// Note: 
// PIND D0 to D7
// D0=0x01 D1=0x02 D2=0x04 D3=0x08 D4=0x10 D5=0x20 D6=0x40 D7=0x80
//
// PINB D8 to D13
// D8=0x01 D9=0x02 D10=0x04 D11=0x08 D12=0x10 D13=0x20  
//
// PINC D14 to D19
// D14=0x01 D15=0x02 D16=0x04 D17=0x08 D18=0x10 D19=0x20  
// **************************************************************************** 
//
// SETd?          Makes D?     HIGH
// RESETd?        Makes D?     LOW
// TOGGLEd?       Makes D?     go to the opposite state
// TESTd?         Reads D?     the pins state is read
//
//===============================================================================
// Port D port pins 0-7
//                              DDDDDDDD
//                              76543210        
//#define SETd0       PORTD |= B00000001  // Pin D0 HIGH Shared with USB RX
//#define RESETd0     PORTD &= B11111110  // Pin D0 LOW Shared with USB TX
//#define TOGGLEd0    PIND   = B00000001  // Toggle digital pin D0 Shared with USB RX
//#define TESTd0      (PIND  & B00000001) // D0 is tested

//#define SETd0       PORTD |= B00000010  // Pin D1 HIGH Shared with USB RX
//#define RESETd0     PORTD &= B11111101  // Pin D1 LOW Shared with USB TX
//#define TOGGLEd1    PIND   = B00000010  // Toggle digital pin D1 Shared with USB TX
//#define TESTd1      (PIND  & B00000010) // D1 is tested

//                            DDDDDDDD
//                            76543210        
#define SETd2       PORTD |= B00000100  // D2 HIGH
#define RESETd2     PORTD &= B11111011  // D2 LOW
#define TOGGLEd2     PIND  = B00000100  // D2 Toggle
#define TESTd2      (PIND  & B00000100) // D2 is tested

#define SETd3       PORTD |= B00001000  // D3 HIGH
#define RESETd3     PORTD &= B11110111  // D3 LOW
#define TOGGLEd3     PIND  = B00001000  // D3 Toggle
#define TESTd3      (PIND  & B00001000) // D3 is tested

#define SETd4       PORTD |= B00010000  // D4 HIGH
#define RESETd4     PORTD &= B11101111  // D4 LOW
#define TOGGLEd4     PIND  = B00010000  // D4 Toggle   
#define TESTd4      (PIND  & B00010000) // D4 is tested

#define SETd5       PORTD |= B00100000  // D5 HIGH
#define RESETd5     PORTD &= B11011111  // D5 LOW
#define TOGGLEd5     PIND  = B00100000  // D5 Toggle 
#define TESTd5      (PIND  & B00100000) // D5 is tested

#define SETd6       PORTD |= B01000000  // D6 HIGH
#define RESETd6     PORTD &= B10111111  // D6 LOW
#define TOGGLEd6     PIND  = B01000000  // D6 Toggle  
#define TESTd6      (PIND  & B01000000) // D6 is tested

#define SETd7       PORTD |= B10000000  // D7 HIGH
#define RESETd7     PORTD &= B01111111  // D7 LOW
#define TOGGLEd7     PIND  = B10000000  // D7 Toggle  
#define TESTd7      (PIND  & B10000000) // D7 is tested

// Port B port pins 0-5
//                              DDDDDD
//                              111100 
//                              321098     
#define SETd8       PORTB  = B00000001  // D8 HIGH
#define RESETd8     PORTB &= B11111110  // D8 LOW
#define TOGGLEd8     PINB  = B00000001  // D8 Toggle
#define TESTd8      (PINB  & B00000001) // D8 is tested

#define SETd9       PORTB |= B00000010  // D9 HIGH
#define RESETd9     PORTB &= B11111101  // D9 LOW
#define TOGGLEd9     PINB  = B00000010  // D9 Toggle
#define TESTd9      (PINB  & B00000010) // D9 is tested

#define SETd10      PORTB |= B00000100  // D10 HIGH
#define RESETd10    PORTB &= B11111011  // D10 LOW
#define TOGGLEd10    PINB  = B00000100  // D10 Toggle
#define TESTd10     (PINB  & B00000100) // D10 is tested

#define SETd11      PORTB |= B00001000  // D11 HIGH
#define RESETd11    PORTB &= B11110111  // D11 LOW
#define TOGGLEd11    PINB  = B00001000  // D11 Toggle
#define TESTd11     (PINB  & B00001000) // D11 is tested

#define SETd12      PORTB |= B00010000  // D12 HIGH
#define RESETd12    PORTB &= B11101111  // D12 LOW
#define TOGGLEd12    PINB  = B00010000  // D12 Toggle
#define TESTd12     (PINB  & B00010000) // D12 is tested

#define SETd13      PORTB |= B00100000  // D13 HIGH
#define RESETd13    PORTB &= B11011111  // D13 LOW
#define TOGGLEd13    PINB  = B00100000  // D13 Toggle
#define TESTd13     (PINB  & B00100000) // D13 is tested


// Port C port pins 0-5
//                              DDDDDD
//                              111111
//                              987654     
#define SETd14      PORTC |= B00000001  // D14 HIGH
#define RESETd14    PORTC &= B11111110  // D14 LOW
#define TOGGLEd14    PINC  = B00000001  // D14 Toggle
#define TESTd14     (PINC  & B00000001) // D14 is tested

#define SETd15      PORTC |= B00000010  // D15 HIGH
#define RESETd15    PORTC &= B11111101  // D15 LOW
#define TOGGLEd15    PINC  = B00000010  // D15 Toggle
#define TESTd15     (PINC  & B00000010) // D15 is tested

#define SETd16      PORTC |= B00000100  // D16 HIGH
#define RESETd16    PORTC &= B11111011  // D16 LOW
#define TOGGLEd16    PINC  = B00000100  // D16 Toggle
#define TESTd16     (PINC  & B00000100) // D16 is tested

#define SETd17      PORTC |= B00001000  // D17 HIGH
#define RESETd17    PORTC &= B11110111  // D17 LOW
#define TOGGLEd17    PINC  = B00001000  // D17 Toggle
#define TESTd17     (PINC  & B00001000) // D17 is tested

#define SETd18      PORTC |= B00010000  // D18 HIGH
#define RESETd18    PORTC &= B11101111  // D18 LOW
#define TOGGLEd18    PINC  = B00010000  // D18 Toggle
#define TESTd18     (PINC  & B00010000) // D18 is tested

#define SETd19      PORTC |= B00100000  // D19 HIGH
#define RESETd19    PORTC &= B11011111  // D19 LOW
#define TOGGLEd19    PINC  = B00100000  // D19 Toggle
#define TESTd19     (PINC  & B00100000) // D19 is tested

You can include digitalWriteFast library, then

digitalWriteFast(A2, HIGH); 
digitalWriteFast(A2, LOW);

Regards

digitalWrite(A2, HIGH);
digitalWrite(A2, LOW);

or on ATmega328, etc.

PORTC |=  (1<<2); 
PORTC &= ~(1<<2);

Pin 11 is Port B, bit 3. Pin A2 is Port C, bit 2.

The full pinout diagram of the UNO showing the port numbers https://content.arduino.cc/assets/A000066-full-pinout.pdf

This can look a bit confusing, most of the Arduino documentation pages will tell you PINx is an INPUT register and is READ-ONLY, but if you look at the data sheet for the atmega328, it states that writing a 1 to PINx will toggle the corresponding bit in the OUTPUT register.

You may want to read the page on direct port manipulation for more information: https://docs.arduino.cc/hacking/software/PortManipulation

@AhmedBahgat
see what the original code of digitalWrite is doing and leave away things you don't need.

The nice thing about the OEM code is, that it doesn't care about which pin is on which port and that makes the code usable on lot of controllers.

my stripped down version:

    // I expect that people know what they do with their ports, 
    // therefore I use a slimmer (and faster) version of digitalWrite
    // by noiasca
    void digitalWriteFast(uint8_t pin, uint8_t val)  {
        uint8_t bit = digitalPinToBitMask(pin);
        uint8_t port = digitalPinToPort(pin);
        volatile uint8_t *out = portOutputRegister(port);
        if (val == LOW) {
          *out &= ~bit;
        } else {
          *out |= bit;
        }
      }

the remaining question is, why the heck do you want a pin toggling in an ISR. "Time saving" for switching on or off a horn sounds so curious for me that I doubt the usage of the ISR isn't the brightest idea anyway.

Yes

OP was also offered:

. . .
#define SETd16      PORTC |= B00000100  // D16 HIGH
#define RESETd16    PORTC &= B11111011  // D16 LOW
#define TOGGLEd16    PINC  = B00000100  // D16 Toggle
#define TESTd16     (PINC  & B00000100) // D16 is tested
. . .

Can't we make these global consts, evaluated at compile time?
Then also the PINx register, as a #define(?), and toggle the pin by
PINx = bit;

Debugging aid?

I couldn't resist and my figures might be wrong but...

digitalWrite will take about 5µs
a single bit port manipulation will take about 0.125µs

The speed of sound is 343 m / s

in 0,125µs the sound of the horn will travel 0,042 mm
in 5µs the sound of the horn will travel 1,715 mm

so moving the horn by less then 2mm towards the hearing position
will gain you a faster hearable noise than messing around with direct port manipulation

using constexpr in the ISR might be a good start to let the compiler test, if it can be evaluated at compile time.

The reason I want it to toggle in ISR, I treied to toggle it in my loop using millis() to toggle the car horn on/off every half second, based on a condition of course, and it just does not time right all the times especially if I try to use some external hardware while in the moment the horn is on, this causes delay for the on period and it is really annoying, So I believe the best course of action is to use ISR and it working much better but I still need to speed it as much as I can because my sketch is about 2000 lines

Cheers

Do you read the answers?
Use the PIN instruction as stated above. It is fastest way to toogle pin on atmega328 mcu

I think I found the single line of code that I needed and it seems working fine, I got it from ChatGPT

PORTC ^= (1 << PORTC2);

Cheers

Yes, I certainly did read everything, I am a good reader, but at the same time I only wanted a single line of code to get me going quickly for now and I got it from ChhatGPT

Cheers

PORTC ^= (1 << PORTC2);

I agree, however I only wanted it for UNO and will not be used on another controller

I actually tried your code and it is working but I honestly needed just a single line of code for now and without adding more libraries as I already added a lot

Cheers

You have been shown a simpler and faster solution already.

artificial intelligence rules