| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512 |
- #include "Adafruit_SPIDevice.h"
- // #define DEBUG_SERIAL Serial
- #ifdef BUSIO_USE_FAST_PINIO
- #define BUSIO_SET_CLOCK_LOW() (*clkPort = *clkPort & ~clkPinMask)
- #define BUSIO_SET_CLOCK_HIGH() (*clkPort = *clkPort | clkPinMask)
- #define BUSIO_READ_MISO() (*misoPort & misoPinMask)
- #define BUSIO_WRITE_MOSI(value) \
- do { \
- if (value) \
- *mosiPort = *mosiPort | mosiPinMask; \
- else \
- *mosiPort = *mosiPort & ~mosiPinMask; \
- } while (0)
- #else
- #define BUSIO_SET_CLOCK_LOW() digitalWrite(_sck, LOW)
- #define BUSIO_SET_CLOCK_HIGH() digitalWrite(_sck, HIGH)
- #define BUSIO_READ_MISO() digitalRead(_miso)
- #define BUSIO_WRITE_MOSI(value) digitalWrite(_mosi, value)
- #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 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)) {
- BUSIO_WRITE_MOSI(towrite);
- lastmosi = towrite;
- }
- BUSIO_SET_CLOCK_HIGH();
- if (bitdelay_us) {
- delayMicroseconds(bitdelay_us);
- }
- if (_miso != -1) {
- if (BUSIO_READ_MISO())
- reply |= b;
- }
- BUSIO_SET_CLOCK_LOW();
- } else if (_dataMode == SPI_MODE3) {
- if (_mosi != -1) { // transmit on falling edge
- BUSIO_WRITE_MOSI(send & b);
- }
- BUSIO_SET_CLOCK_LOW();
- if (bitdelay_us) {
- delayMicroseconds(bitdelay_us);
- }
- BUSIO_SET_CLOCK_HIGH();
- if (bitdelay_us) {
- delayMicroseconds(bitdelay_us);
- }
- if (_miso != -1) { // read on rising edge
- if (BUSIO_READ_MISO()) {
- reply |= b;
- }
- }
- } else { // || _dataMode == SPI_MODE1)
- BUSIO_SET_CLOCK_HIGH();
- if (bitdelay_us) {
- delayMicroseconds(bitdelay_us);
- }
- if (_mosi != -1) {
- BUSIO_WRITE_MOSI(send & b);
- }
- BUSIO_SET_CLOCK_LOW();
- if (_miso != -1) {
- if (BUSIO_READ_MISO()) {
- 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;
- }
|