| 1 | #include "Adafruit_SPIDevice.h" |
| 2 | |
| 3 | // #define DEBUG_SERIAL Serial |
| 4 | |
| 5 | #ifdef BUSIO_USE_FAST_PINIO |
| 6 | #define BUSIO_SET_CLOCK_LOW() (*clkPort = *clkPort & ~clkPinMask) |
| 7 | #define BUSIO_SET_CLOCK_HIGH() (*clkPort = *clkPort | clkPinMask) |
| 8 | #define BUSIO_READ_MISO() (*misoPort & misoPinMask) |
| 9 | #define BUSIO_WRITE_MOSI(value) \ |
| 10 | do { \ |
| 11 | if (value) \ |
| 12 | *mosiPort = *mosiPort | mosiPinMask; \ |
| 13 | else \ |
| 14 | *mosiPort = *mosiPort & ~mosiPinMask; \ |
| 15 | } while (0) |
| 16 | #else |
| 17 | #define BUSIO_SET_CLOCK_LOW() digitalWrite(_sck, LOW) |
| 18 | #define BUSIO_SET_CLOCK_HIGH() digitalWrite(_sck, HIGH) |
| 19 | #define BUSIO_READ_MISO() digitalRead(_miso) |
| 20 | #define BUSIO_WRITE_MOSI(value) digitalWrite(_mosi, value) |
| 21 | #endif |
| 22 | |
| 23 | /*! |
| 24 | * @brief Create an SPI device with the given CS pin and settings |
| 25 | * @param cspin The arduino pin number to use for chip select |
| 26 | * @param freq The SPI clock frequency to use, defaults to 1MHz |
| 27 | * @param dataOrder The SPI data order to use for bits within each byte, |
| 28 | * defaults to SPI_BITORDER_MSBFIRST |
| 29 | * @param dataMode The SPI mode to use, defaults to SPI_MODE0 |
| 30 | * @param theSPI The SPI bus to use, defaults to &theSPI |
| 31 | */ |
| 32 | Adafruit_SPIDevice::Adafruit_SPIDevice(int8_t cspin, uint32_t freq, |
| 33 | BusIOBitOrder dataOrder, |
| 34 | uint8_t dataMode, SPIClass *theSPI) { |
| 35 | #ifdef BUSIO_HAS_HW_SPI |
| 36 | _cs = cspin; |
| 37 | _sck = _mosi = _miso = -1; |
| 38 | _spi = theSPI; |
| 39 | _begun = false; |
| 40 | _spiSetting = new SPISettings(freq, dataOrder, dataMode); |
| 41 | _freq = freq; |
| 42 | _dataOrder = dataOrder; |
| 43 | _dataMode = dataMode; |
| 44 | #else |
| 45 | // unused, but needed to suppress compiler warns |
| 46 | (void)cspin; |
| 47 | (void)freq; |
| 48 | (void)dataOrder; |
| 49 | (void)dataMode; |
| 50 | (void)theSPI; |
| 51 | #endif |
| 52 | } |
| 53 | |
| 54 | /*! |
| 55 | * @brief Create an SPI device with the given CS pin and settings |
| 56 | * @param cspin The arduino pin number to use for chip select |
| 57 | * @param sckpin The arduino pin number to use for SCK |
| 58 | * @param misopin The arduino pin number to use for MISO, set to -1 if not |
| 59 | * used |
| 60 | * @param mosipin The arduino pin number to use for MOSI, set to -1 if not |
| 61 | * used |
| 62 | * @param freq The SPI clock frequency to use, defaults to 1MHz |
| 63 | * @param dataOrder The SPI data order to use for bits within each byte, |
| 64 | * defaults to SPI_BITORDER_MSBFIRST |
| 65 | * @param dataMode The SPI mode to use, defaults to SPI_MODE0 |
| 66 | */ |
| 67 | Adafruit_SPIDevice::Adafruit_SPIDevice(int8_t cspin, int8_t sckpin, |
| 68 | int8_t misopin, int8_t mosipin, |
| 69 | uint32_t freq, BusIOBitOrder dataOrder, |
| 70 | uint8_t dataMode) { |
| 71 | _cs = cspin; |
| 72 | _sck = sckpin; |
| 73 | _miso = misopin; |
| 74 | _mosi = mosipin; |
| 75 | |
| 76 | #ifdef BUSIO_USE_FAST_PINIO |
| 77 | csPort = (BusIO_PortReg *)portOutputRegister(digitalPinToPort(cspin)); |
| 78 | csPinMask = digitalPinToBitMask(cspin); |
| 79 | if (mosipin != -1) { |
| 80 | mosiPort = (BusIO_PortReg *)portOutputRegister(digitalPinToPort(mosipin)); |
| 81 | mosiPinMask = digitalPinToBitMask(mosipin); |
| 82 | } |
| 83 | if (misopin != -1) { |
| 84 | misoPort = (BusIO_PortReg *)portInputRegister(digitalPinToPort(misopin)); |
| 85 | misoPinMask = digitalPinToBitMask(misopin); |
| 86 | } |
| 87 | clkPort = (BusIO_PortReg *)portOutputRegister(digitalPinToPort(sckpin)); |
| 88 | clkPinMask = digitalPinToBitMask(sckpin); |
| 89 | #endif |
| 90 | |
| 91 | _freq = freq; |
| 92 | _dataOrder = dataOrder; |
| 93 | _dataMode = dataMode; |
| 94 | _begun = false; |
| 95 | } |
| 96 | |
| 97 | /*! |
| 98 | * @brief Release memory allocated in constructors |
| 99 | */ |
| 100 | Adafruit_SPIDevice::~Adafruit_SPIDevice() { |
| 101 | if (_spiSetting) |
| 102 | delete _spiSetting; |
| 103 | } |
| 104 | |
| 105 | /*! |
| 106 | * @brief Initializes SPI bus and sets CS pin high |
| 107 | * @return Always returns true because there's no way to test success of SPI |
| 108 | * init |
| 109 | */ |
| 110 | bool Adafruit_SPIDevice::begin(void) { |
| 111 | if (_cs != -1) { |
| 112 | pinMode(_cs, OUTPUT); |
| 113 | digitalWrite(_cs, HIGH); |
| 114 | } |
| 115 | |
| 116 | if (_spi) { // hardware SPI |
| 117 | #ifdef BUSIO_HAS_HW_SPI |
| 118 | _spi->begin(); |
| 119 | #endif |
| 120 | } else { |
| 121 | pinMode(_sck, OUTPUT); |
| 122 | |
| 123 | if ((_dataMode == SPI_MODE0) || (_dataMode == SPI_MODE1)) { |
| 124 | // idle low on mode 0 and 1 |
| 125 | digitalWrite(_sck, LOW); |
| 126 | } else { |
| 127 | // idle high on mode 2 or 3 |
| 128 | digitalWrite(_sck, HIGH); |
| 129 | } |
| 130 | if (_mosi != -1) { |
| 131 | pinMode(_mosi, OUTPUT); |
| 132 | digitalWrite(_mosi, HIGH); |
| 133 | } |
| 134 | if (_miso != -1) { |
| 135 | pinMode(_miso, INPUT); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | _begun = true; |
| 140 | return true; |
| 141 | } |
| 142 | |
| 143 | /*! |
| 144 | * @brief Transfer (send/receive) a buffer over hard/soft SPI, without |
| 145 | * transaction management |
| 146 | * @param buffer The buffer to send and receive at the same time |
| 147 | * @param len The number of bytes to transfer |
| 148 | */ |
| 149 | void Adafruit_SPIDevice::transfer(uint8_t *buffer, size_t len) { |
| 150 | // |
| 151 | // HARDWARE SPI |
| 152 | // |
| 153 | if (_spi) { |
| 154 | #ifdef BUSIO_HAS_HW_SPI |
| 155 | #if defined(SPARK) |
| 156 | _spi->transfer(buffer, buffer, len, nullptr); |
| 157 | #elif defined(STM32) |
| 158 | for (size_t i = 0; i < len; i++) { |
| 159 | _spi->transfer(buffer[i]); |
| 160 | } |
| 161 | #else |
| 162 | _spi->transfer(buffer, len); |
| 163 | #endif |
| 164 | return; |
| 165 | #endif |
| 166 | } |
| 167 | |
| 168 | // |
| 169 | // SOFTWARE SPI |
| 170 | // |
| 171 | uint8_t startbit; |
| 172 | if (_dataOrder == SPI_BITORDER_LSBFIRST) { |
| 173 | startbit = 0x1; |
| 174 | } else { |
| 175 | startbit = 0x80; |
| 176 | } |
| 177 | |
| 178 | bool towrite, lastmosi = !(buffer[0] & startbit); |
| 179 | uint8_t bitdelay_us = (1000000 / _freq) / 2; |
| 180 | |
| 181 | for (size_t i = 0; i < len; i++) { |
| 182 | uint8_t reply = 0; |
| 183 | uint8_t send = buffer[i]; |
| 184 | |
| 185 | /* |
| 186 | Serial.print("\tSending software SPI byte 0x"); |
| 187 | Serial.print(send, HEX); |
| 188 | Serial.print(" -> 0x"); |
| 189 | */ |
| 190 | |
| 191 | // Serial.print(send, HEX); |
| 192 | for (uint8_t b = startbit; b != 0; |
| 193 | b = (_dataOrder == SPI_BITORDER_LSBFIRST) ? b << 1 : b >> 1) { |
| 194 | |
| 195 | if (bitdelay_us) { |
| 196 | delayMicroseconds(bitdelay_us); |
| 197 | } |
| 198 | |
| 199 | if (_dataMode == SPI_MODE0 || _dataMode == SPI_MODE2) { |
| 200 | towrite = send & b; |
| 201 | if ((_mosi != -1) && (lastmosi != towrite)) { |
| 202 | BUSIO_WRITE_MOSI(towrite); |
| 203 | lastmosi = towrite; |
| 204 | } |
| 205 | |
| 206 | BUSIO_SET_CLOCK_HIGH(); |
| 207 | |
| 208 | if (bitdelay_us) { |
| 209 | delayMicroseconds(bitdelay_us); |
| 210 | } |
| 211 | |
| 212 | if (_miso != -1) { |
| 213 | if (BUSIO_READ_MISO()) |
| 214 | reply |= b; |
| 215 | } |
| 216 | |
| 217 | BUSIO_SET_CLOCK_LOW(); |
| 218 | |
| 219 | } else if (_dataMode == SPI_MODE3) { |
| 220 | |
| 221 | if (_mosi != -1) { // transmit on falling edge |
| 222 | BUSIO_WRITE_MOSI(send & b); |
| 223 | } |
| 224 | |
| 225 | BUSIO_SET_CLOCK_LOW(); |
| 226 | |
| 227 | if (bitdelay_us) { |
| 228 | delayMicroseconds(bitdelay_us); |
| 229 | } |
| 230 | |
| 231 | BUSIO_SET_CLOCK_HIGH(); |
| 232 | |
| 233 | if (bitdelay_us) { |
| 234 | delayMicroseconds(bitdelay_us); |
| 235 | } |
| 236 | |
| 237 | if (_miso != -1) { // read on rising edge |
| 238 | if (BUSIO_READ_MISO()) { |
| 239 | reply |= b; |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | } else { // || _dataMode == SPI_MODE1) |
| 244 | |
| 245 | BUSIO_SET_CLOCK_HIGH(); |
| 246 | |
| 247 | if (bitdelay_us) { |
| 248 | delayMicroseconds(bitdelay_us); |
| 249 | } |
| 250 | |
| 251 | if (_mosi != -1) { |
| 252 | BUSIO_WRITE_MOSI(send & b); |
| 253 | } |
| 254 | |
| 255 | BUSIO_SET_CLOCK_LOW(); |
| 256 | |
| 257 | if (_miso != -1) { |
| 258 | if (BUSIO_READ_MISO()) { |
| 259 | reply |= b; |
| 260 | } |
| 261 | } |
| 262 | } |
| 263 | } |
| 264 | if (_miso != -1) { |
| 265 | buffer[i] = reply; |
| 266 | } |
| 267 | } |
| 268 | return; |
| 269 | } |
| 270 | |
| 271 | /*! |
| 272 | * @brief Transfer (send/receive) one byte over hard/soft SPI, without |
| 273 | * transaction management |
| 274 | * @param send The byte to send |
| 275 | * @return The byte received while transmitting |
| 276 | */ |
| 277 | uint8_t Adafruit_SPIDevice::transfer(uint8_t send) { |
| 278 | uint8_t data = send; |
| 279 | transfer(&data, 1); |
| 280 | return data; |
| 281 | } |
| 282 | |
| 283 | /*! |
| 284 | * @brief Manually begin a transaction (calls beginTransaction if hardware |
| 285 | * SPI) |
| 286 | */ |
| 287 | void Adafruit_SPIDevice::beginTransaction(void) { |
| 288 | if (_spi) { |
| 289 | #ifdef BUSIO_HAS_HW_SPI |
| 290 | _spi->beginTransaction(*_spiSetting); |
| 291 | #endif |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | /*! |
| 296 | * @brief Manually end a transaction (calls endTransaction if hardware SPI) |
| 297 | */ |
| 298 | void Adafruit_SPIDevice::endTransaction(void) { |
| 299 | if (_spi) { |
| 300 | #ifdef BUSIO_HAS_HW_SPI |
| 301 | _spi->endTransaction(); |
| 302 | #endif |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | /*! |
| 307 | * @brief Assert/Deassert the CS pin if it is defined |
| 308 | * @param value The state the CS is set to |
| 309 | */ |
| 310 | void Adafruit_SPIDevice::setChipSelect(int value) { |
| 311 | if (_cs != -1) { |
| 312 | digitalWrite(_cs, value); |
| 313 | } |
| 314 | } |
| 315 | |
| 316 | /*! |
| 317 | * @brief Write a buffer or two to the SPI device, with transaction |
| 318 | * management. |
| 319 | * @brief Manually begin a transaction (calls beginTransaction if hardware |
| 320 | * SPI) with asserting the CS pin |
| 321 | */ |
| 322 | void Adafruit_SPIDevice::beginTransactionWithAssertingCS() { |
| 323 | beginTransaction(); |
| 324 | setChipSelect(LOW); |
| 325 | } |
| 326 | |
| 327 | /*! |
| 328 | * @brief Manually end a transaction (calls endTransaction if hardware SPI) |
| 329 | * with deasserting the CS pin |
| 330 | */ |
| 331 | void Adafruit_SPIDevice::endTransactionWithDeassertingCS() { |
| 332 | setChipSelect(HIGH); |
| 333 | endTransaction(); |
| 334 | } |
| 335 | |
| 336 | /*! |
| 337 | * @brief Write a buffer or two to the SPI device, with transaction |
| 338 | * management. |
| 339 | * @param buffer Pointer to buffer of data to write |
| 340 | * @param len Number of bytes from buffer to write |
| 341 | * @param prefix_buffer Pointer to optional array of data to write before |
| 342 | * buffer. |
| 343 | * @param prefix_len Number of bytes from prefix buffer to write |
| 344 | * @return Always returns true because there's no way to test success of SPI |
| 345 | * writes |
| 346 | */ |
| 347 | bool Adafruit_SPIDevice::write(const uint8_t *buffer, size_t len, |
| 348 | const uint8_t *prefix_buffer, |
| 349 | size_t prefix_len) { |
| 350 | beginTransactionWithAssertingCS(); |
| 351 | |
| 352 | // do the writing |
| 353 | #if defined(ARDUINO_ARCH_ESP32) |
| 354 | if (_spi) { |
| 355 | if (prefix_len > 0) { |
| 356 | _spi->transferBytes((uint8_t *)prefix_buffer, nullptr, prefix_len); |
| 357 | } |
| 358 | if (len > 0) { |
| 359 | _spi->transferBytes((uint8_t *)buffer, nullptr, len); |
| 360 | } |
| 361 | } else |
| 362 | #endif |
| 363 | { |
| 364 | for (size_t i = 0; i < prefix_len; i++) { |
| 365 | transfer(prefix_buffer[i]); |
| 366 | } |
| 367 | for (size_t i = 0; i < len; i++) { |
| 368 | transfer(buffer[i]); |
| 369 | } |
| 370 | } |
| 371 | endTransactionWithDeassertingCS(); |
| 372 | |
| 373 | #ifdef DEBUG_SERIAL |
| 374 | DEBUG_SERIAL.print(F("\tSPIDevice Wrote: ")); |
| 375 | if ((prefix_len != 0) && (prefix_buffer != nullptr)) { |
| 376 | for (uint16_t i = 0; i < prefix_len; i++) { |
| 377 | DEBUG_SERIAL.print(F("0x")); |
| 378 | DEBUG_SERIAL.print(prefix_buffer[i], HEX); |
| 379 | DEBUG_SERIAL.print(F(", ")); |
| 380 | } |
| 381 | } |
| 382 | for (uint16_t i = 0; i < len; i++) { |
| 383 | DEBUG_SERIAL.print(F("0x")); |
| 384 | DEBUG_SERIAL.print(buffer[i], HEX); |
| 385 | DEBUG_SERIAL.print(F(", ")); |
| 386 | if (i % 32 == 31) { |
| 387 | DEBUG_SERIAL.println(); |
| 388 | } |
| 389 | } |
| 390 | DEBUG_SERIAL.println(); |
| 391 | #endif |
| 392 | |
| 393 | return true; |
| 394 | } |
| 395 | |
| 396 | /*! |
| 397 | * @brief Read from SPI into a buffer from the SPI device, with transaction |
| 398 | * management. |
| 399 | * @param buffer Pointer to buffer of data to read into |
| 400 | * @param len Number of bytes from buffer to read. |
| 401 | * @param sendvalue The 8-bits of data to write when doing the data read, |
| 402 | * defaults to 0xFF |
| 403 | * @return Always returns true because there's no way to test success of SPI |
| 404 | * writes |
| 405 | */ |
| 406 | bool Adafruit_SPIDevice::read(uint8_t *buffer, size_t len, uint8_t sendvalue) { |
| 407 | memset(buffer, sendvalue, len); // clear out existing buffer |
| 408 | |
| 409 | beginTransactionWithAssertingCS(); |
| 410 | transfer(buffer, len); |
| 411 | endTransactionWithDeassertingCS(); |
| 412 | |
| 413 | #ifdef DEBUG_SERIAL |
| 414 | DEBUG_SERIAL.print(F("\tSPIDevice Read: ")); |
| 415 | for (uint16_t i = 0; i < len; i++) { |
| 416 | DEBUG_SERIAL.print(F("0x")); |
| 417 | DEBUG_SERIAL.print(buffer[i], HEX); |
| 418 | DEBUG_SERIAL.print(F(", ")); |
| 419 | if (len % 32 == 31) { |
| 420 | DEBUG_SERIAL.println(); |
| 421 | } |
| 422 | } |
| 423 | DEBUG_SERIAL.println(); |
| 424 | #endif |
| 425 | |
| 426 | return true; |
| 427 | } |
| 428 | |
| 429 | /*! |
| 430 | * @brief Write some data, then read some data from SPI into another buffer, |
| 431 | * with transaction management. The buffers can point to same/overlapping |
| 432 | * locations. This does not transmit-receive at the same time! |
| 433 | * @param write_buffer Pointer to buffer of data to write from |
| 434 | * @param write_len Number of bytes from buffer to write. |
| 435 | * @param read_buffer Pointer to buffer of data to read into. |
| 436 | * @param read_len Number of bytes from buffer to read. |
| 437 | * @param sendvalue The 8-bits of data to write when doing the data read, |
| 438 | * defaults to 0xFF |
| 439 | * @return Always returns true because there's no way to test success of SPI |
| 440 | * writes |
| 441 | */ |
| 442 | bool Adafruit_SPIDevice::write_then_read(const uint8_t *write_buffer, |
| 443 | size_t write_len, uint8_t *read_buffer, |
| 444 | size_t read_len, uint8_t sendvalue) { |
| 445 | beginTransactionWithAssertingCS(); |
| 446 | // do the writing |
| 447 | #if defined(ARDUINO_ARCH_ESP32) |
| 448 | if (_spi) { |
| 449 | if (write_len > 0) { |
| 450 | _spi->transferBytes((uint8_t *)write_buffer, nullptr, write_len); |
| 451 | } |
| 452 | } else |
| 453 | #endif |
| 454 | { |
| 455 | for (size_t i = 0; i < write_len; i++) { |
| 456 | transfer(write_buffer[i]); |
| 457 | } |
| 458 | } |
| 459 | |
| 460 | #ifdef DEBUG_SERIAL |
| 461 | DEBUG_SERIAL.print(F("\tSPIDevice Wrote: ")); |
| 462 | for (uint16_t i = 0; i < write_len; i++) { |
| 463 | DEBUG_SERIAL.print(F("0x")); |
| 464 | DEBUG_SERIAL.print(write_buffer[i], HEX); |
| 465 | DEBUG_SERIAL.print(F(", ")); |
| 466 | if (write_len % 32 == 31) { |
| 467 | DEBUG_SERIAL.println(); |
| 468 | } |
| 469 | } |
| 470 | DEBUG_SERIAL.println(); |
| 471 | #endif |
| 472 | |
| 473 | // do the reading |
| 474 | for (size_t i = 0; i < read_len; i++) { |
| 475 | read_buffer[i] = transfer(sendvalue); |
| 476 | } |
| 477 | |
| 478 | #ifdef DEBUG_SERIAL |
| 479 | DEBUG_SERIAL.print(F("\tSPIDevice Read: ")); |
| 480 | for (uint16_t i = 0; i < read_len; i++) { |
| 481 | DEBUG_SERIAL.print(F("0x")); |
| 482 | DEBUG_SERIAL.print(read_buffer[i], HEX); |
| 483 | DEBUG_SERIAL.print(F(", ")); |
| 484 | if (read_len % 32 == 31) { |
| 485 | DEBUG_SERIAL.println(); |
| 486 | } |
| 487 | } |
| 488 | DEBUG_SERIAL.println(); |
| 489 | #endif |
| 490 | |
| 491 | endTransactionWithDeassertingCS(); |
| 492 | |
| 493 | return true; |
| 494 | } |
| 495 | |
| 496 | /*! |
| 497 | * @brief Write some data and read some data at the same time from SPI |
| 498 | * into the same buffer, with transaction management. This is basicaly a wrapper |
| 499 | * for transfer() with CS-pin and transaction management. This /does/ |
| 500 | * transmit-receive at the same time! |
| 501 | * @param buffer Pointer to buffer of data to write/read to/from |
| 502 | * @param len Number of bytes from buffer to write/read. |
| 503 | * @return Always returns true because there's no way to test success of SPI |
| 504 | * writes |
| 505 | */ |
| 506 | bool Adafruit_SPIDevice::write_and_read(uint8_t *buffer, size_t len) { |
| 507 | beginTransactionWithAssertingCS(); |
| 508 | transfer(buffer, len); |
| 509 | endTransactionWithDeassertingCS(); |
| 510 | |
| 511 | return true; |
| 512 | } |