I want to send one bit of a byte out per 'rising edge' received to an Arduino digital input. I am addressing the outputs using direct port manipulation (to achieve maximum speed), and I would like to do the same for input.

So I'm calling a function called bitBang in my main loop, sending the ready line high before moving into the function:

//Send control

What I would like to do upon entering the function is listen for a rising edge to appear on the digital input port using the direct port syntax and iterate through the byte setting the data line high or low depending on the bit's value.

How can I achieve this in the most efficient way possible?

  • 1
    Use the SPI facility. Jan 22, 2015 at 17:29
  • You could do a while loop on the bit state, or an inline asm version thereof. Consider declaring your function inline too. But are you sure you can't accomplish your need using the hardware of the ATmega's SPI engine? Jan 22, 2015 at 17:31
  • @Chris Stratton I need to send a ready line high to the peripheral device which will send clock pulses in to the arduino which will send data out. Is this achievable with SPI?
    – blarg
    Jan 22, 2015 at 18:52
  • Possibly... Depends on the details and if you can use the SPI pins, but generally clocking out data in response to a clock input is what a slave mode SPI engine does. The ready output could be a GPIO controlled in software after the SPI is armed. Jan 22, 2015 at 19:34
  • Search for bit bang SPI since this is essentially what you're doing. Jan 23, 2015 at 2:02

1 Answer 1


Example bit-banged SPI using port manipulation:

bitBangedSPIfast.h :

#include <Arduino.h>

class bitBangedSPIfast
  // addresses of output ports - NULL if not applicable
  volatile byte * const mosiPort_;
  volatile byte * const misoPin_;
  volatile byte * const sckPort_;

  // bit masks for above
  const byte mosiBit_;
  const byte misoBit_;
  const byte sckBit_;

  // addresses of data direction register ports
  volatile byte * const mosiDDR_;
  volatile byte * const misoDDR_;
  volatile byte * const sckDDR_;

  // bit masks for above
  const byte mosiDDRBit_;
  const byte misoDDRBit_;
  const byte sckDDRBit_;

  // delay for clock being high
  unsigned long delayUs_;

    // constructor
    bitBangedSPIfast (
          // output ports
          volatile byte & mosiPort, 
          const    byte mosiBit,
          volatile byte & misoPin, 
          const    byte misoBit,
          volatile byte & sckPort,
          const    byte sckBit,

          // data direction register ports
          volatile byte & mosiDDR, 
          const    byte mosiDDRBit,
          volatile byte & misoDDR, 
          const    byte misoDDRBit,
          volatile byte & sckDDR,
          const    byte sckDDRBit,

          const unsigned long delayUs = 1)
         mosiPort_ (&mosiPort), 
         mosiBit_  (1 << mosiBit),
         misoPin_  (&misoPin), 
         misoBit_  (1 << misoBit), 
         sckPort_  (&sckPort), 
         sckBit_   (1 << sckBit), 

         mosiDDR_     (&mosiDDR), 
         mosiDDRBit_  (1 << mosiDDRBit),
         misoDDR_     (&misoDDR), 
         misoDDRBit_  (1 << misoDDRBit), 
         sckDDR_      (&sckDDR), 
         sckDDRBit_   (1 << sckDDRBit), 

        delayUs_ (delayUs) { }

    void begin ();
    byte transfer (byte input);

  };  // end of bitBangedSPIfast

bitBangedSPIfast.cpp :

#include <bitBangedSPIfast.h>

void bitBangedSPIfast::begin ()
  if (mosiPort_)
    *mosiDDR_ |= mosiDDRBit_;    // output
  if (misoPin_)
    *misoDDR_ &= ~misoDDRBit_;   // input
  *sckDDR_ |= sckDDRBit_;        // output
  }   // end of bitBangedSPIfast::begin

// Bit Banged SPI transfer
byte bitBangedSPIfast::transfer (byte c)
  // loop for each bit  
  for (byte bit = 0; bit < 8; bit++) 
    // set up MOSI on falling edge of previous SCK (sampled on rising edge)
    if (mosiPort_)
      if (c & 0x80)
        *mosiPort_ |= mosiBit_;
        *mosiPort_ &= ~mosiBit_;

    // finished with MS bit, get read to receive next bit      
    c <<= 1;

    // read MISO
    if (misoPin_)
      c |= (*misoPin_ & misoBit_) != 0;

    // clock high
    *sckPort_ |= sckBit_;

    // delay between rise and fall of clock
    delayMicroseconds (delayUs_);

    // clock low
    *sckPort_ &= ~sckBit_;

    // delay between rise and fall of clock
    delayMicroseconds (delayUs_);
    }  // end of for loop, for each bit

  return c;
  }  // end of bitBangedSPIfast::transfer  

Example usage:

#include <bitBangedSPIfast.h>

bitBangedSPIfast bbSPI (PORTD,  5, PIND,  6, PORTD, 7,    // MOSI port (D5), MISO pin (D6), SCK port (D7)
                        DDRD,   5, DDRD,  6, DDRD,  7);   // MOSI ddr  (D5), MISO ddr (D6), SCK ddr  (D7)
const byte mySS =  8;  // slave select

void setup (void)
  bbSPI.begin ();
  pinMode (mySS, OUTPUT);
  }  // end of setup

void loop (void)
  char c;

  // enable Slave Select
  digitalWrite(mySS, LOW); 

  // send test string
  for (const char * p = "Hello, world!" ; c = *p; p++)
    bbSPI.transfer (c);

  // disable Slave Select
  digitalWrite(mySS, HIGH);

  delay (100); 
  }  // end of loop


SPI - Serial Peripheral Interface - for Arduino

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.