0

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
PORTB |= _BV(PORTB2);
bitBang(controlByte);
PORTB &= ~_BV(PORTB2);

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?

asked Jan 22, 2015 at 17:25
5
  • 1
    Use the SPI facility. Commented 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? Commented 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? Commented 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. Commented Jan 22, 2015 at 19:34
  • Search for bit bang SPI since this is essentially what you're doing. Commented Jan 23, 2015 at 2:02

1 Answer 1

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_;
 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

answered Aug 21, 2015 at 23:54

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.