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?
-
1Use the SPI facility.Ignacio Vazquez-Abrams– Ignacio Vazquez-Abrams01/22/2015 17:29:47Commented 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?Chris Stratton– Chris Stratton01/22/2015 17:31:47Commented 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– blarg01/22/2015 18:52:38Commented 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.Chris Stratton– Chris Stratton01/22/2015 19:34:44Commented Jan 22, 2015 at 19:34
-
Search for bit bang SPI since this is essentially what you're doing.Oleg Mazurov– Oleg Mazurov01/23/2015 02:02:36Commented 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_;
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