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_; public: // 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_; else *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
Reference
SPI - Serial Peripheral Interface - for Arduino