You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
508 lines
14 KiB
508 lines
14 KiB
#include "Adafruit_SPIDevice.h" |
|
|
|
//#define DEBUG_SERIAL Serial |
|
|
|
/*! |
|
* @brief Create an SPI device with the given CS pin and settings |
|
* @param cspin The arduino pin number to use for chip select |
|
* @param freq The SPI clock frequency to use, defaults to 1MHz |
|
* @param dataOrder The SPI data order to use for bits within each byte, |
|
* defaults to SPI_BITORDER_MSBFIRST |
|
* @param dataMode The SPI mode to use, defaults to SPI_MODE0 |
|
* @param theSPI The SPI bus to use, defaults to &theSPI |
|
*/ |
|
Adafruit_SPIDevice::Adafruit_SPIDevice(int8_t cspin, uint32_t freq, |
|
BusIOBitOrder dataOrder, |
|
uint8_t dataMode, SPIClass *theSPI) { |
|
#ifdef BUSIO_HAS_HW_SPI |
|
_cs = cspin; |
|
_sck = _mosi = _miso = -1; |
|
_spi = theSPI; |
|
_begun = false; |
|
_spiSetting = new SPISettings(freq, dataOrder, dataMode); |
|
_freq = freq; |
|
_dataOrder = dataOrder; |
|
_dataMode = dataMode; |
|
#else |
|
// unused, but needed to suppress compiler warns |
|
(void)cspin; |
|
(void)freq; |
|
(void)dataOrder; |
|
(void)dataMode; |
|
(void)theSPI; |
|
#endif |
|
} |
|
|
|
/*! |
|
* @brief Create an SPI device with the given CS pin and settings |
|
* @param cspin The arduino pin number to use for chip select |
|
* @param sckpin The arduino pin number to use for SCK |
|
* @param misopin The arduino pin number to use for MISO, set to -1 if not |
|
* used |
|
* @param mosipin The arduino pin number to use for MOSI, set to -1 if not |
|
* used |
|
* @param freq The SPI clock frequency to use, defaults to 1MHz |
|
* @param dataOrder The SPI data order to use for bits within each byte, |
|
* defaults to SPI_BITORDER_MSBFIRST |
|
* @param dataMode The SPI mode to use, defaults to SPI_MODE0 |
|
*/ |
|
Adafruit_SPIDevice::Adafruit_SPIDevice(int8_t cspin, int8_t sckpin, |
|
int8_t misopin, int8_t mosipin, |
|
uint32_t freq, BusIOBitOrder dataOrder, |
|
uint8_t dataMode) { |
|
_cs = cspin; |
|
_sck = sckpin; |
|
_miso = misopin; |
|
_mosi = mosipin; |
|
|
|
#ifdef BUSIO_USE_FAST_PINIO |
|
csPort = (BusIO_PortReg *)portOutputRegister(digitalPinToPort(cspin)); |
|
csPinMask = digitalPinToBitMask(cspin); |
|
if (mosipin != -1) { |
|
mosiPort = (BusIO_PortReg *)portOutputRegister(digitalPinToPort(mosipin)); |
|
mosiPinMask = digitalPinToBitMask(mosipin); |
|
} |
|
if (misopin != -1) { |
|
misoPort = (BusIO_PortReg *)portInputRegister(digitalPinToPort(misopin)); |
|
misoPinMask = digitalPinToBitMask(misopin); |
|
} |
|
clkPort = (BusIO_PortReg *)portOutputRegister(digitalPinToPort(sckpin)); |
|
clkPinMask = digitalPinToBitMask(sckpin); |
|
#endif |
|
|
|
_freq = freq; |
|
_dataOrder = dataOrder; |
|
_dataMode = dataMode; |
|
_begun = false; |
|
} |
|
|
|
/*! |
|
* @brief Release memory allocated in constructors |
|
*/ |
|
Adafruit_SPIDevice::~Adafruit_SPIDevice() { |
|
if (_spiSetting) |
|
delete _spiSetting; |
|
} |
|
|
|
/*! |
|
* @brief Initializes SPI bus and sets CS pin high |
|
* @return Always returns true because there's no way to test success of SPI |
|
* init |
|
*/ |
|
bool Adafruit_SPIDevice::begin(void) { |
|
if (_cs != -1) { |
|
pinMode(_cs, OUTPUT); |
|
digitalWrite(_cs, HIGH); |
|
} |
|
|
|
if (_spi) { // hardware SPI |
|
#ifdef BUSIO_HAS_HW_SPI |
|
_spi->begin(); |
|
#endif |
|
} else { |
|
pinMode(_sck, OUTPUT); |
|
|
|
if ((_dataMode == SPI_MODE0) || (_dataMode == SPI_MODE1)) { |
|
// idle low on mode 0 and 1 |
|
digitalWrite(_sck, LOW); |
|
} else { |
|
// idle high on mode 2 or 3 |
|
digitalWrite(_sck, HIGH); |
|
} |
|
if (_mosi != -1) { |
|
pinMode(_mosi, OUTPUT); |
|
digitalWrite(_mosi, HIGH); |
|
} |
|
if (_miso != -1) { |
|
pinMode(_miso, INPUT); |
|
} |
|
} |
|
|
|
_begun = true; |
|
return true; |
|
} |
|
|
|
/*! |
|
* @brief Transfer (send/receive) a buffer over hard/soft SPI, without |
|
* transaction management |
|
* @param buffer The buffer to send and receive at the same time |
|
* @param len The number of bytes to transfer |
|
*/ |
|
void Adafruit_SPIDevice::transfer(uint8_t *buffer, size_t len) { |
|
// |
|
// HARDWARE SPI |
|
// |
|
if (_spi) { |
|
#ifdef BUSIO_HAS_HW_SPI |
|
#if defined(SPARK) |
|
_spi->transfer(buffer, buffer, len, nullptr); |
|
#elif defined(STM32) |
|
for (size_t i = 0; i < len; i++) { |
|
_spi->transfer(buffer[i]); |
|
} |
|
#else |
|
_spi->transfer(buffer, len); |
|
#endif |
|
return; |
|
#endif |
|
} |
|
|
|
// |
|
// SOFTWARE SPI |
|
// |
|
uint8_t startbit; |
|
if (_dataOrder == SPI_BITORDER_LSBFIRST) { |
|
startbit = 0x1; |
|
} else { |
|
startbit = 0x80; |
|
} |
|
|
|
bool towrite, lastmosi = !(buffer[0] & startbit); |
|
uint8_t bitdelay_us = (1000000 / _freq) / 2; |
|
|
|
for (size_t i = 0; i < len; i++) { |
|
uint8_t reply = 0; |
|
uint8_t send = buffer[i]; |
|
|
|
/* |
|
Serial.print("\tSending software SPI byte 0x"); |
|
Serial.print(send, HEX); |
|
Serial.print(" -> 0x"); |
|
*/ |
|
|
|
// Serial.print(send, HEX); |
|
for (uint8_t b = startbit; b != 0; |
|
b = (_dataOrder == SPI_BITORDER_LSBFIRST) ? b << 1 : b >> 1) { |
|
|
|
if (bitdelay_us) { |
|
delayMicroseconds(bitdelay_us); |
|
} |
|
|
|
if (_dataMode == SPI_MODE0 || _dataMode == SPI_MODE2) { |
|
towrite = send & b; |
|
if ((_mosi != -1) && (lastmosi != towrite)) { |
|
#ifdef BUSIO_USE_FAST_PINIO |
|
if (towrite) |
|
*mosiPort = *mosiPort | mosiPinMask; |
|
else |
|
*mosiPort = *mosiPort & ~mosiPinMask; |
|
#else |
|
digitalWrite(_mosi, towrite); |
|
#endif |
|
lastmosi = towrite; |
|
} |
|
|
|
#ifdef BUSIO_USE_FAST_PINIO |
|
*clkPort = *clkPort | clkPinMask; // Clock high |
|
#else |
|
digitalWrite(_sck, HIGH); |
|
#endif |
|
|
|
if (bitdelay_us) { |
|
delayMicroseconds(bitdelay_us); |
|
} |
|
|
|
if (_miso != -1) { |
|
#ifdef BUSIO_USE_FAST_PINIO |
|
if (*misoPort & misoPinMask) { |
|
#else |
|
if (digitalRead(_miso)) { |
|
#endif |
|
reply |= b; |
|
} |
|
} |
|
|
|
#ifdef BUSIO_USE_FAST_PINIO |
|
*clkPort = *clkPort & ~clkPinMask; // Clock low |
|
#else |
|
digitalWrite(_sck, LOW); |
|
#endif |
|
} else { // if (_dataMode == SPI_MODE1 || _dataMode == SPI_MODE3) |
|
|
|
#ifdef BUSIO_USE_FAST_PINIO |
|
*clkPort = *clkPort | clkPinMask; // Clock high |
|
#else |
|
digitalWrite(_sck, HIGH); |
|
#endif |
|
|
|
if (bitdelay_us) { |
|
delayMicroseconds(bitdelay_us); |
|
} |
|
|
|
if (_mosi != -1) { |
|
#ifdef BUSIO_USE_FAST_PINIO |
|
if (send & b) |
|
*mosiPort = *mosiPort | mosiPinMask; |
|
else |
|
*mosiPort = *mosiPort & ~mosiPinMask; |
|
#else |
|
digitalWrite(_mosi, send & b); |
|
#endif |
|
} |
|
|
|
#ifdef BUSIO_USE_FAST_PINIO |
|
*clkPort = *clkPort & ~clkPinMask; // Clock low |
|
#else |
|
digitalWrite(_sck, LOW); |
|
#endif |
|
|
|
if (_miso != -1) { |
|
#ifdef BUSIO_USE_FAST_PINIO |
|
if (*misoPort & misoPinMask) { |
|
#else |
|
if (digitalRead(_miso)) { |
|
#endif |
|
reply |= b; |
|
} |
|
} |
|
} |
|
if (_miso != -1) { |
|
buffer[i] = reply; |
|
} |
|
} |
|
} |
|
return; |
|
} |
|
|
|
/*! |
|
* @brief Transfer (send/receive) one byte over hard/soft SPI, without |
|
* transaction management |
|
* @param send The byte to send |
|
* @return The byte received while transmitting |
|
*/ |
|
uint8_t Adafruit_SPIDevice::transfer(uint8_t send) { |
|
uint8_t data = send; |
|
transfer(&data, 1); |
|
return data; |
|
} |
|
|
|
/*! |
|
* @brief Manually begin a transaction (calls beginTransaction if hardware |
|
* SPI) |
|
*/ |
|
void Adafruit_SPIDevice::beginTransaction(void) { |
|
if (_spi) { |
|
#ifdef BUSIO_HAS_HW_SPI |
|
_spi->beginTransaction(*_spiSetting); |
|
#endif |
|
} |
|
} |
|
|
|
/*! |
|
* @brief Manually end a transaction (calls endTransaction if hardware SPI) |
|
*/ |
|
void Adafruit_SPIDevice::endTransaction(void) { |
|
if (_spi) { |
|
#ifdef BUSIO_HAS_HW_SPI |
|
_spi->endTransaction(); |
|
#endif |
|
} |
|
} |
|
|
|
/*! |
|
* @brief Assert/Deassert the CS pin if it is defined |
|
* @param value The state the CS is set to |
|
*/ |
|
void Adafruit_SPIDevice::setChipSelect(int value) { |
|
if (_cs != -1) { |
|
digitalWrite(_cs, value); |
|
} |
|
} |
|
|
|
/*! |
|
* @brief Write a buffer or two to the SPI device, with transaction |
|
* management. |
|
* @brief Manually begin a transaction (calls beginTransaction if hardware |
|
* SPI) with asserting the CS pin |
|
*/ |
|
void Adafruit_SPIDevice::beginTransactionWithAssertingCS() { |
|
beginTransaction(); |
|
setChipSelect(LOW); |
|
} |
|
|
|
/*! |
|
* @brief Manually end a transaction (calls endTransaction if hardware SPI) |
|
* with deasserting the CS pin |
|
*/ |
|
void Adafruit_SPIDevice::endTransactionWithDeassertingCS() { |
|
setChipSelect(HIGH); |
|
endTransaction(); |
|
} |
|
|
|
/*! |
|
* @brief Write a buffer or two to the SPI device, with transaction |
|
* management. |
|
* @param buffer Pointer to buffer of data to write |
|
* @param len Number of bytes from buffer to write |
|
* @param prefix_buffer Pointer to optional array of data to write before |
|
* buffer. |
|
* @param prefix_len Number of bytes from prefix buffer to write |
|
* @return Always returns true because there's no way to test success of SPI |
|
* writes |
|
*/ |
|
bool Adafruit_SPIDevice::write(const uint8_t *buffer, size_t len, |
|
const uint8_t *prefix_buffer, |
|
size_t prefix_len) { |
|
beginTransactionWithAssertingCS(); |
|
|
|
// do the writing |
|
#if defined(ARDUINO_ARCH_ESP32) |
|
if (_spi) { |
|
if (prefix_len > 0) { |
|
_spi->transferBytes((uint8_t *)prefix_buffer, nullptr, prefix_len); |
|
} |
|
if (len > 0) { |
|
_spi->transferBytes((uint8_t *)buffer, nullptr, len); |
|
} |
|
} else |
|
#endif |
|
{ |
|
for (size_t i = 0; i < prefix_len; i++) { |
|
transfer(prefix_buffer[i]); |
|
} |
|
for (size_t i = 0; i < len; i++) { |
|
transfer(buffer[i]); |
|
} |
|
} |
|
endTransactionWithDeassertingCS(); |
|
|
|
#ifdef DEBUG_SERIAL |
|
DEBUG_SERIAL.print(F("\tSPIDevice Wrote: ")); |
|
if ((prefix_len != 0) && (prefix_buffer != nullptr)) { |
|
for (uint16_t i = 0; i < prefix_len; i++) { |
|
DEBUG_SERIAL.print(F("0x")); |
|
DEBUG_SERIAL.print(prefix_buffer[i], HEX); |
|
DEBUG_SERIAL.print(F(", ")); |
|
} |
|
} |
|
for (uint16_t i = 0; i < len; i++) { |
|
DEBUG_SERIAL.print(F("0x")); |
|
DEBUG_SERIAL.print(buffer[i], HEX); |
|
DEBUG_SERIAL.print(F(", ")); |
|
if (i % 32 == 31) { |
|
DEBUG_SERIAL.println(); |
|
} |
|
} |
|
DEBUG_SERIAL.println(); |
|
#endif |
|
|
|
return true; |
|
} |
|
|
|
/*! |
|
* @brief Read from SPI into a buffer from the SPI device, with transaction |
|
* management. |
|
* @param buffer Pointer to buffer of data to read into |
|
* @param len Number of bytes from buffer to read. |
|
* @param sendvalue The 8-bits of data to write when doing the data read, |
|
* defaults to 0xFF |
|
* @return Always returns true because there's no way to test success of SPI |
|
* writes |
|
*/ |
|
bool Adafruit_SPIDevice::read(uint8_t *buffer, size_t len, uint8_t sendvalue) { |
|
memset(buffer, sendvalue, len); // clear out existing buffer |
|
|
|
beginTransactionWithAssertingCS(); |
|
transfer(buffer, len); |
|
endTransactionWithDeassertingCS(); |
|
|
|
#ifdef DEBUG_SERIAL |
|
DEBUG_SERIAL.print(F("\tSPIDevice Read: ")); |
|
for (uint16_t i = 0; i < len; i++) { |
|
DEBUG_SERIAL.print(F("0x")); |
|
DEBUG_SERIAL.print(buffer[i], HEX); |
|
DEBUG_SERIAL.print(F(", ")); |
|
if (len % 32 == 31) { |
|
DEBUG_SERIAL.println(); |
|
} |
|
} |
|
DEBUG_SERIAL.println(); |
|
#endif |
|
|
|
return true; |
|
} |
|
|
|
/*! |
|
* @brief Write some data, then read some data from SPI into another buffer, |
|
* with transaction management. The buffers can point to same/overlapping |
|
* locations. This does not transmit-receive at the same time! |
|
* @param write_buffer Pointer to buffer of data to write from |
|
* @param write_len Number of bytes from buffer to write. |
|
* @param read_buffer Pointer to buffer of data to read into. |
|
* @param read_len Number of bytes from buffer to read. |
|
* @param sendvalue The 8-bits of data to write when doing the data read, |
|
* defaults to 0xFF |
|
* @return Always returns true because there's no way to test success of SPI |
|
* writes |
|
*/ |
|
bool Adafruit_SPIDevice::write_then_read(const uint8_t *write_buffer, |
|
size_t write_len, uint8_t *read_buffer, |
|
size_t read_len, uint8_t sendvalue) { |
|
beginTransactionWithAssertingCS(); |
|
// do the writing |
|
#if defined(ARDUINO_ARCH_ESP32) |
|
if (_spi) { |
|
if (write_len > 0) { |
|
_spi->transferBytes((uint8_t *)write_buffer, nullptr, write_len); |
|
} |
|
} else |
|
#endif |
|
{ |
|
for (size_t i = 0; i < write_len; i++) { |
|
transfer(write_buffer[i]); |
|
} |
|
} |
|
|
|
#ifdef DEBUG_SERIAL |
|
DEBUG_SERIAL.print(F("\tSPIDevice Wrote: ")); |
|
for (uint16_t i = 0; i < write_len; i++) { |
|
DEBUG_SERIAL.print(F("0x")); |
|
DEBUG_SERIAL.print(write_buffer[i], HEX); |
|
DEBUG_SERIAL.print(F(", ")); |
|
if (write_len % 32 == 31) { |
|
DEBUG_SERIAL.println(); |
|
} |
|
} |
|
DEBUG_SERIAL.println(); |
|
#endif |
|
|
|
// do the reading |
|
for (size_t i = 0; i < read_len; i++) { |
|
read_buffer[i] = transfer(sendvalue); |
|
} |
|
|
|
#ifdef DEBUG_SERIAL |
|
DEBUG_SERIAL.print(F("\tSPIDevice Read: ")); |
|
for (uint16_t i = 0; i < read_len; i++) { |
|
DEBUG_SERIAL.print(F("0x")); |
|
DEBUG_SERIAL.print(read_buffer[i], HEX); |
|
DEBUG_SERIAL.print(F(", ")); |
|
if (read_len % 32 == 31) { |
|
DEBUG_SERIAL.println(); |
|
} |
|
} |
|
DEBUG_SERIAL.println(); |
|
#endif |
|
|
|
endTransactionWithDeassertingCS(); |
|
|
|
return true; |
|
} |
|
|
|
/*! |
|
* @brief Write some data and read some data at the same time from SPI |
|
* into the same buffer, with transaction management. This is basicaly a wrapper |
|
* for transfer() with CS-pin and transaction management. This /does/ |
|
* transmit-receive at the same time! |
|
* @param buffer Pointer to buffer of data to write/read to/from |
|
* @param len Number of bytes from buffer to write/read. |
|
* @return Always returns true because there's no way to test success of SPI |
|
* writes |
|
*/ |
|
bool Adafruit_SPIDevice::write_and_read(uint8_t *buffer, size_t len) { |
|
beginTransactionWithAssertingCS(); |
|
transfer(buffer, len); |
|
endTransactionWithDeassertingCS(); |
|
|
|
return true; |
|
}
|
|
|