#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 |= mosiPinMask; else *mosiPort &= ~mosiPinMask; #else digitalWrite(_mosi, towrite); #endif lastmosi = towrite; } #ifdef BUSIO_USE_FAST_PINIO *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 &= ~clkPinMask; // Clock low #else digitalWrite(_sck, LOW); #endif } else { // if (_dataMode == SPI_MODE1 || _dataMode == SPI_MODE3) #ifdef BUSIO_USE_FAST_PINIO *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 |= mosiPinMask; else *mosiPort &= ~mosiPinMask; #else digitalWrite(_mosi, send & b); #endif } #ifdef BUSIO_USE_FAST_PINIO *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(prefix_buffer, nullptr, prefix_len); } if (len > 0) { _spi->transferBytes(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(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; }