Mongoose_Arduino_RadioHead/src/RHSoftwareSPI.cpp

167 lines
3.3 KiB
C++

// SoftwareSPI.cpp
// Author: Chris Lapa (chris@lapa.com.au)
// Copyright (C) 2014 Chris Lapa
// Contributed by Chris Lapa
#include <RHSoftwareSPI.h>
RHSoftwareSPI::RHSoftwareSPI(Frequency frequency, BitOrder bitOrder, DataMode dataMode)
:
RHGenericSPI(frequency, bitOrder, dataMode)
{
setPins(12, 11, 13);
}
// Caution: on Arduino Uno and many other CPUs, digitalWrite is quite slow, taking about 4us
// digitalWrite is also slow, taking about 3.5us
// resulting in very slow SPI bus speeds using this technique, up to about 120us per octet of transfer
uint8_t RHSoftwareSPI::transfer(uint8_t data)
{
uint8_t readData;
uint8_t writeData;
uint8_t builtReturn;
uint8_t mask;
if (_bitOrder == BitOrderMSBFirst)
{
mask = 0x80;
}
else
{
mask = 0x01;
}
builtReturn = 0;
readData = 0;
for (uint8_t count=0; count<8; count++)
{
if (data & mask)
{
writeData = HIGH;
}
else
{
writeData = LOW;
}
if (_clockPhase == 1)
{
// CPHA=1, miso/mosi changing state now
digitalWrite(_mosi, writeData);
digitalWrite(_sck, ~_clockPolarity);
delayPeriod();
// CPHA=1, miso/mosi stable now
readData = digitalRead(_miso);
digitalWrite(_sck, _clockPolarity);
delayPeriod();
}
else
{
// CPHA=0, miso/mosi changing state now
digitalWrite(_mosi, writeData);
digitalWrite(_sck, _clockPolarity);
delayPeriod();
// CPHA=0, miso/mosi stable now
readData = digitalRead(_miso);
digitalWrite(_sck, ~_clockPolarity);
delayPeriod();
}
if (_bitOrder == BitOrderMSBFirst)
{
mask >>= 1;
builtReturn |= (readData << (7 - count));
}
else
{
mask <<= 1;
builtReturn |= (readData << count);
}
}
digitalWrite(_sck, _clockPolarity);
return builtReturn;
}
/// Initialise the SPI library
void RHSoftwareSPI::begin()
{
if (_dataMode == DataMode0 ||
_dataMode == DataMode1)
{
_clockPolarity = LOW;
}
else
{
_clockPolarity = HIGH;
}
if (_dataMode == DataMode0 ||
_dataMode == DataMode2)
{
_clockPhase = 0;
}
else
{
_clockPhase = 1;
}
digitalWrite(_sck, _clockPolarity);
// Caution: these counts assume that digitalWrite is very fast, which is usually not true
switch (_frequency)
{
case Frequency1MHz:
_delayCounts = 8;
break;
case Frequency2MHz:
_delayCounts = 4;
break;
case Frequency4MHz:
_delayCounts = 2;
break;
case Frequency8MHz:
_delayCounts = 1;
break;
case Frequency16MHz:
_delayCounts = 0;
break;
}
}
/// Disables the SPI bus usually, in this case
/// there is no hardware controller to disable.
void RHSoftwareSPI::end() { }
/// Sets the pins used by this SoftwareSPIClass instance.
/// \param[in] miso master in slave out pin used
/// \param[in] mosi master out slave in pin used
/// \param[in] sck clock pin used
void RHSoftwareSPI::setPins(uint8_t miso, uint8_t mosi, uint8_t sck)
{
_miso = miso;
_mosi = mosi;
_sck = sck;
pinMode(_miso, INPUT);
pinMode(_mosi, OUTPUT);
pinMode(_sck, OUTPUT);
digitalWrite(_sck, _clockPolarity);
}
void RHSoftwareSPI::delayPeriod()
{
for (uint8_t count = 0; count < _delayCounts; count++)
{
__asm__ __volatile__ ("nop");
}
}