From d0232d126a200fd9d22117ad1cd73bebd9002fdd Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Tue, 14 Feb 2017 04:15:34 +0200 Subject: [PATCH] SPI implement transaction locking Use transactions to skip mutex locks for reads and writes --- cores/esp32/esp32-hal-spi.c | 433 +++++++++++++++++++++++++++++------- cores/esp32/esp32-hal-spi.h | 37 ++- libraries/SPI/src/SPI.cpp | 62 +++++- libraries/SPI/src/SPI.h | 26 ++- 4 files changed, 449 insertions(+), 109 deletions(-) diff --git a/cores/esp32/esp32-hal-spi.c b/cores/esp32/esp32-hal-spi.c index a79082ef..9c13fbf5 100644 --- a/cores/esp32/esp32-hal-spi.c +++ b/cores/esp32/esp32-hal-spi.c @@ -517,41 +517,24 @@ uint8_t spiTransferByte(spi_t * spi, uint8_t data) return data; } -uint32_t __spiTranslate16(uint16_t data, bool msb) +uint32_t __spiTranslate24(uint32_t data) { - if(msb) { - return (data >> 8) | (data << 8); - } else { - return data; - } + union { + uint32_t l; + uint8_t b[4]; + } out; + out.l = data; + return out.b[2] | (out.b[1] << 8) | (out.b[0] << 16); } -uint32_t __spiTranslate24(uint32_t data, bool msb) +uint32_t __spiTranslate32(uint32_t data) { - if(msb) { - union { - uint32_t l; - uint8_t b[4]; - } out; - out.l = data; - return out.b[2] | (out.b[1] << 8) | (out.b[0] << 16); - } else { - return data; - } -} - -uint32_t __spiTranslate32(uint32_t data, bool msb) -{ - if(msb) { - union { - uint32_t l; - uint8_t b[4]; - } out; - out.l = data; - return out.b[3] | (out.b[2] << 8) | (out.b[1] << 16) | (out.b[0] << 24); - } else { - return data; - } + union { + uint32_t l; + uint8_t b[4]; + } out; + out.l = data; + return out.b[3] | (out.b[2] << 8) | (out.b[1] << 16) | (out.b[0] << 24); } void spiWriteWord(spi_t * spi, uint16_t data) @@ -559,10 +542,13 @@ void spiWriteWord(spi_t * spi, uint16_t data) if(!spi) { return; } + if(!spi->dev->ctrl.wr_bit_order){ + data = (data >> 8) | (data << 8); + } SPI_MUTEX_LOCK(); spi->dev->mosi_dlen.usr_mosi_dbitlen = 15; spi->dev->miso_dlen.usr_miso_dbitlen = 0; - spi->dev->data_buf[0] = __spiTranslate16(data, !spi->dev->ctrl.wr_bit_order); + spi->dev->data_buf[0] = data; spi->dev->cmd.usr = 1; while(spi->dev->cmd.usr); SPI_MUTEX_UNLOCK(); @@ -573,14 +559,20 @@ uint16_t spiTransferWord(spi_t * spi, uint16_t data) if(!spi) { return 0; } + if(!spi->dev->ctrl.wr_bit_order){ + data = (data >> 8) | (data << 8); + } SPI_MUTEX_LOCK(); spi->dev->mosi_dlen.usr_mosi_dbitlen = 15; spi->dev->miso_dlen.usr_miso_dbitlen = 15; - spi->dev->data_buf[0] = __spiTranslate16(data, !spi->dev->ctrl.wr_bit_order); + spi->dev->data_buf[0] = data; spi->dev->cmd.usr = 1; while(spi->dev->cmd.usr); - data = __spiTranslate16(spi->dev->data_buf[0] & 0xFFFF, !spi->dev->ctrl.rd_bit_order); + data = spi->dev->data_buf[0]; SPI_MUTEX_UNLOCK(); + if(!spi->dev->ctrl.rd_bit_order){ + data = (data >> 8) | (data << 8); + } return data; } @@ -589,10 +581,13 @@ void spiWriteLong(spi_t * spi, uint32_t data) if(!spi) { return; } + if(!spi->dev->ctrl.wr_bit_order){ + data = __spiTranslate32(data); + } SPI_MUTEX_LOCK(); spi->dev->mosi_dlen.usr_mosi_dbitlen = 31; spi->dev->miso_dlen.usr_miso_dbitlen = 0; - spi->dev->data_buf[0] = __spiTranslate32(data, !spi->dev->ctrl.wr_bit_order); + spi->dev->data_buf[0] = data; spi->dev->cmd.usr = 1; while(spi->dev->cmd.usr); SPI_MUTEX_UNLOCK(); @@ -603,59 +598,23 @@ uint32_t spiTransferLong(spi_t * spi, uint32_t data) if(!spi) { return 0; } + if(!spi->dev->ctrl.wr_bit_order){ + data = __spiTranslate32(data); + } SPI_MUTEX_LOCK(); spi->dev->mosi_dlen.usr_mosi_dbitlen = 31; spi->dev->miso_dlen.usr_miso_dbitlen = 31; - spi->dev->data_buf[0] = __spiTranslate32(data, !spi->dev->ctrl.wr_bit_order); + spi->dev->data_buf[0] = data; spi->dev->cmd.usr = 1; while(spi->dev->cmd.usr); - data = __spiTranslate32(spi->dev->data_buf[0], !spi->dev->ctrl.rd_bit_order); + data = spi->dev->data_buf[0]; SPI_MUTEX_UNLOCK(); + if(!spi->dev->ctrl.rd_bit_order){ + data = __spiTranslate32(data); + } return data; } -void spiTransferBits(spi_t * spi, uint32_t data, uint32_t * out, uint8_t bits) -{ - if(!spi) { - return; - } - - if(bits > 32) { - bits = 32; - } - uint32_t bytes = (bits + 7) / 8;//64 max - uint32_t mask = (((uint64_t)1 << bits) - 1) & 0xFFFFFFFF; - - SPI_MUTEX_LOCK(); - spi->dev->mosi_dlen.usr_mosi_dbitlen = (bits - 1); - spi->dev->miso_dlen.usr_miso_dbitlen = (bits - 1); - if(bytes == 1) { - spi->dev->data_buf[0] = data & mask; - } else if(bytes == 2) { - spi->dev->data_buf[0] = __spiTranslate16(data & mask, !spi->dev->ctrl.wr_bit_order); - } else if(bytes == 3) { - spi->dev->data_buf[0] = __spiTranslate24(data & mask, !spi->dev->ctrl.wr_bit_order); - } else { - spi->dev->data_buf[0] = __spiTranslate32(data & mask, !spi->dev->ctrl.wr_bit_order); - } - spi->dev->cmd.usr = 1; - - while(spi->dev->cmd.usr); - - if(out) { - if(bytes == 1) { - *out = spi->dev->data_buf[0] & mask; - } else if(bytes == 2) { - *out = __spiTranslate16(spi->dev->data_buf[0] & mask, !spi->dev->ctrl.wr_bit_order); - } else if(bytes == 3) { - *out = __spiTranslate24(spi->dev->data_buf[0] & mask, !spi->dev->ctrl.wr_bit_order); - } else { - *out = __spiTranslate32(spi->dev->data_buf[0] & mask, !spi->dev->ctrl.wr_bit_order); - } - } - SPI_MUTEX_UNLOCK(); -} - void __spiTransferBytes(spi_t * spi, uint8_t * data, uint8_t * out, uint32_t bytes) { if(!spi) { @@ -721,6 +680,320 @@ void spiTransferBytes(spi_t * spi, uint8_t * data, uint8_t * out, uint32_t size) SPI_MUTEX_UNLOCK(); } +/* + * Manual Lock Management + * */ + +#define MSB_32_SET(var, val) { uint8_t * d = (uint8_t *)&(val); (var) = d[3] | (d[2] << 8) | (d[1] << 16) | (d[0] << 24); } +#define MSB_24_SET(var, val) { uint8_t * d = (uint8_t *)&(val); (var) = d[2] | (d[1] << 8) | (d[0] << 16); } +#define MSB_16_SET(var, val) { (var) = (((val) & 0xFF00) >> 8) | (((val) & 0xFF) << 8); } + +void spiTransaction(spi_t * spi, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + spi->dev->clock.val = clockDiv; + switch (dataMode) { + case SPI_MODE1: + spi->dev->pin.ck_idle_edge = 0; + spi->dev->user.ck_out_edge = 1; + break; + case SPI_MODE2: + spi->dev->pin.ck_idle_edge = 1; + spi->dev->user.ck_out_edge = 0; + break; + case SPI_MODE3: + spi->dev->pin.ck_idle_edge = 1; + spi->dev->user.ck_out_edge = 1; + break; + case SPI_MODE0: + default: + spi->dev->pin.ck_idle_edge = 0; + spi->dev->user.ck_out_edge = 0; + break; + } + if (SPI_MSBFIRST == bitOrder) { + spi->dev->ctrl.wr_bit_order = 0; + spi->dev->ctrl.rd_bit_order = 0; + } else if (SPI_LSBFIRST == bitOrder) { + spi->dev->ctrl.wr_bit_order = 1; + spi->dev->ctrl.rd_bit_order = 1; + } +} + +void spiSimpleTransaction(spi_t * spi) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); +} + +void spiEndTransaction(spi_t * spi) +{ + if(!spi) { + return; + } + SPI_MUTEX_UNLOCK(); +} + +void spiWriteByteNL(spi_t * spi, uint8_t data) +{ + if(!spi) { + return; + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 7; + spi->dev->miso_dlen.usr_miso_dbitlen = 0; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); +} + +uint8_t spiTransferByteNL(spi_t * spi, uint8_t data) +{ + if(!spi) { + return 0; + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 7; + spi->dev->miso_dlen.usr_miso_dbitlen = 7; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0] & 0xFF; + return data; +} + +void spiWriteShortNL(spi_t * spi, uint16_t data) +{ + if(!spi) { + return; + } + if(!spi->dev->ctrl.wr_bit_order){ + MSB_16_SET(data, data); + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 15; + spi->dev->miso_dlen.usr_miso_dbitlen = 0; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); +} + +uint16_t spiTransferShortNL(spi_t * spi, uint16_t data) +{ + if(!spi) { + return 0; + } + if(!spi->dev->ctrl.wr_bit_order){ + MSB_16_SET(data, data); + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 15; + spi->dev->miso_dlen.usr_miso_dbitlen = 15; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0] & 0xFFFF; + if(!spi->dev->ctrl.rd_bit_order){ + MSB_16_SET(data, data); + } + return data; +} + +void spiWriteLongNL(spi_t * spi, uint32_t data) +{ + if(!spi) { + return; + } + if(!spi->dev->ctrl.wr_bit_order){ + MSB_32_SET(data, data); + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 31; + spi->dev->miso_dlen.usr_miso_dbitlen = 0; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); +} + +uint32_t spiTransferLongNL(spi_t * spi, uint32_t data) +{ + if(!spi) { + return 0; + } + if(!spi->dev->ctrl.wr_bit_order){ + MSB_32_SET(data, data); + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 31; + spi->dev->miso_dlen.usr_miso_dbitlen = 31; + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0]; + if(!spi->dev->ctrl.rd_bit_order){ + MSB_32_SET(data, data); + } + return data; +} + +void spiWriteNL(spi_t * spi, const void * data_in, size_t len){ + size_t longs = len >> 2; + if(len & 3){ + longs++; + } + uint32_t * data = (uint32_t*)data_in; + size_t c_len = 0, c_longs = 0; + + while(len){ + c_len = (len>64)?64:len; + c_longs = (longs > 16)?16:longs; + + spi->dev->mosi_dlen.usr_mosi_dbitlen = (c_len*8)-1; + spi->dev->miso_dlen.usr_miso_dbitlen = 0; + for (int i=0; idev->data_buf[i] = data[i]; + } + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + + data += c_longs; + longs -= c_longs; + len -= c_len; + } +} + +void spiTransferBytesNL(spi_t * spi, const void * data_in, uint8_t * data_out, size_t len){ + if(!spi) { + return; + } + size_t longs = len >> 2; + if(len & 3){ + longs++; + } + uint32_t * data = (uint32_t*)data_in; + uint32_t * result = (uint32_t*)data_out; + size_t c_len = 0, c_longs = 0; + + while(len){ + c_len = (len>64)?64:len; + c_longs = (longs > 16)?16:longs; + + spi->dev->mosi_dlen.usr_mosi_dbitlen = (c_len*8)-1; + spi->dev->miso_dlen.usr_miso_dbitlen = (c_len*8)-1; + if(data){ + for (int i=0; idev->data_buf[i] = data[i]; + } + } else { + for (int i=0; idev->data_buf[i] = 0xFFFFFFFF; + } + } + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + if(result){ + for (int i=0; idev->data_buf[i]; + } + } + if(data){ + data += c_longs; + } + if(result){ + result += c_longs; + } + longs -= c_longs; + len -= c_len; + } +} + +void spiTransferBitsNL(spi_t * spi, uint32_t data, uint32_t * out, uint8_t bits) +{ + if(!spi) { + return; + } + + if(bits > 32) { + bits = 32; + } + uint32_t bytes = (bits + 7) / 8;//64 max + uint32_t mask = (((uint64_t)1 << bits) - 1) & 0xFFFFFFFF; + data = data & mask; + if(!spi->dev->ctrl.wr_bit_order){ + if(bytes == 2) { + MSB_16_SET(data, data); + } else if(bytes == 3) { + MSB_24_SET(data, data); + } else { + MSB_32_SET(data, data); + } + } + + spi->dev->mosi_dlen.usr_mosi_dbitlen = (bits - 1); + spi->dev->miso_dlen.usr_miso_dbitlen = (bits - 1); + spi->dev->data_buf[0] = data; + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0]; + if(out) { + *out = data; + if(!spi->dev->ctrl.rd_bit_order){ + if(bytes == 2) { + MSB_16_SET(*out, data); + } else if(bytes == 3) { + MSB_24_SET(*out, data); + } else { + MSB_32_SET(*out, data); + } + } + } +} + +void spiWritePixelsNL(spi_t * spi, const void * data_in, size_t len){ + size_t longs = len >> 2; + if(len & 3){ + longs++; + } + bool msb = !spi->dev->ctrl.wr_bit_order; + uint32_t * data = (uint32_t*)data_in; + size_t c_len = 0, c_longs = 0, l_bytes = 0; + + while(len){ + c_len = (len>64)?64:len; + c_longs = (longs > 16)?16:longs; + l_bytes = (c_len & 3); + + spi->dev->mosi_dlen.usr_mosi_dbitlen = (c_len*8)-1; + spi->dev->miso_dlen.usr_miso_dbitlen = 0; + for (int i=0; idev->data_buf[i], data[i]); + } else { + spi->dev->data_buf[i] = data[i] & 0xFF; + } + } else { + MSB_32_SET(spi->dev->data_buf[i], data[i]); + } + } else { + spi->dev->data_buf[i] = data[i]; + } + } + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + + data += c_longs; + longs -= c_longs; + len -= c_len; + } +} + + + +/* + * Clock Calculators + * + * */ typedef union { uint32_t regValue; @@ -795,5 +1068,3 @@ uint32_t spiFrequencyToClockDiv(uint32_t freq) return bestReg.regValue; } - - diff --git a/cores/esp32/esp32-hal-spi.h b/cores/esp32/esp32-hal-spi.h index 5680adfc..78ef10fa 100644 --- a/cores/esp32/esp32-hal-spi.h +++ b/cores/esp32/esp32-hal-spi.h @@ -85,12 +85,15 @@ void spiSSClear(spi_t * spi); void spiWaitReady(spi_t * spi); uint32_t spiGetClockDiv(spi_t * spi); -void spiSetClockDiv(spi_t * spi, uint32_t clockDiv); - uint8_t spiGetDataMode(spi_t * spi); -void spiSetDataMode(spi_t * spi, uint8_t dataMode); - uint8_t spiGetBitOrder(spi_t * spi); + + +/* + * Non transaction based lock methods (each locks and unlocks when called) + * */ +void spiSetClockDiv(spi_t * spi, uint32_t clockDiv); +void spiSetDataMode(spi_t * spi, uint8_t dataMode); void spiSetBitOrder(spi_t * spi, uint8_t bitOrder); void spiWrite(spi_t * spi, uint32_t *data, uint8_t len); @@ -102,10 +105,32 @@ void spiTransfer(spi_t * spi, uint32_t *out, uint8_t len); uint8_t spiTransferByte(spi_t * spi, uint8_t data); uint16_t spiTransferWord(spi_t * spi, uint16_t data); uint32_t spiTransferLong(spi_t * spi, uint32_t data); - -void spiTransferBits(spi_t * spi, uint32_t data, uint32_t * out, uint8_t bits); void spiTransferBytes(spi_t * spi, uint8_t * data, uint8_t * out, uint32_t size); +void spiTransferBits(spi_t * spi, uint32_t data, uint32_t * out, uint8_t bits); +/* + * New (EXPERIMENTAL) Transaction lock based API (lock once until endTransaction) + * */ +void spiTransaction(spi_t * spi, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder); +void spiSimpleTransaction(spi_t * spi); +void spiEndTransaction(spi_t * spi); + +void spiWriteNL(spi_t * spi, const void * data, uint32_t len); +void spiWriteByteNL(spi_t * spi, uint8_t data); +void spiWriteShortNL(spi_t * spi, uint16_t data); +void spiWriteLongNL(spi_t * spi, uint32_t data); +void spiWritePixelsNL(spi_t * spi, const void * data, uint32_t len); + +#define spiTransferNL(spi, data, len) spiTransferBytesNL(spi, data, data, len) +uint8_t spiTransferByteNL(spi_t * spi, uint8_t data); +uint16_t spiTransferShortNL(spi_t * spi, uint16_t data); +uint32_t spiTransferLongNL(spi_t * spi, uint32_t data); +void spiTransferBytesNL(spi_t * spi, const void * data_in, uint8_t * data_out, uint32_t len); +void spiTransferBitsNL(spi_t * spi, uint32_t data_in, uint32_t * data_out, uint8_t bits); + +/* + * Helper functions to translate frequency to clock divider and back + * */ uint32_t spiFrequencyToClockDiv(uint32_t freq); uint32_t spiClockDivToFrequency(uint32_t freq); diff --git a/libraries/SPI/src/SPI.cpp b/libraries/SPI/src/SPI.cpp index 9a002a78..84b2cb8b 100644 --- a/libraries/SPI/src/SPI.cpp +++ b/libraries/SPI/src/SPI.cpp @@ -31,6 +31,7 @@ SPIClass::SPIClass(uint8_t spi_bus) ,_ss(-1) ,_div(0) ,_freq(1000000) + , _inTransaction(false) {} void SPIClass::begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) @@ -84,16 +85,6 @@ void SPIClass::setHwCs(bool use) _use_hw_ss = use; } -void SPIClass::beginTransaction(SPISettings settings) -{ - spiWaitReady(_spi); - setFrequency(settings._clock); - setBitOrder(settings._bitOrder); - setDataMode(settings._dataMode); -} - -void SPIClass::endTransaction() {} - void SPIClass::setFrequency(uint32_t freq) { //check if last freq changed @@ -121,38 +112,79 @@ void SPIClass::setBitOrder(uint8_t bitOrder) spiSetBitOrder(_spi, bitOrder); } +void SPIClass::beginTransaction(SPISettings settings) +{ + //check if last freq changed + uint32_t cdiv = spiGetClockDiv(_spi); + if(_freq != settings._clock || _div != cdiv) { + _freq = settings._clock; + _div = spiFrequencyToClockDiv(_freq); + } + spiTransaction(_spi, _div, settings._dataMode, settings._bitOrder); + _inTransaction = true; +} + +void SPIClass::endTransaction() +{ + if(_inTransaction){ + spiEndTransaction(_spi); + _inTransaction = false; + } +} + void SPIClass::write(uint8_t data) { + if(_inTransaction){ + return spiWriteByteNL(_spi, data); + } spiWriteByte(_spi, data); } uint8_t SPIClass::transfer(uint8_t data) { + if(_inTransaction){ + return spiTransferByteNL(_spi, data); + } return spiTransferByte(_spi, data); } void SPIClass::write16(uint16_t data) { + if(_inTransaction){ + return spiWriteShortNL(_spi, data); + } spiWriteWord(_spi, data); } uint16_t SPIClass::transfer16(uint16_t data) { + if(_inTransaction){ + return spiTransferShortNL(_spi, data); + } return spiTransferWord(_spi, data); } void SPIClass::write32(uint32_t data) { + if(_inTransaction){ + return spiWriteLongNL(_spi, data); + } spiWriteLong(_spi, data); } uint32_t SPIClass::transfer32(uint32_t data) { + if(_inTransaction){ + return spiTransferLongNL(_spi, data); + } return spiTransferLong(_spi, data); } void SPIClass::transferBits(uint32_t data, uint32_t * out, uint8_t bits) { + if(_inTransaction){ + return spiTransferBitsNL(_spi, data, out, bits); + } spiTransferBits(_spi, data, out, bits); } @@ -165,7 +197,12 @@ void SPIClass::transferBits(uint32_t data, uint32_t * out, uint8_t bits) */ void SPIClass::writeBytes(uint8_t * data, uint32_t size) { - spiTransferBytes(_spi, data, 0, size); + if(_inTransaction){ + return spiWriteNL(_spi, data, size); + } + spiSimpleTransaction(_spi); + spiWriteNL(_spi, data, size); + spiEndTransaction(_spi); } /** @@ -175,6 +212,9 @@ void SPIClass::writeBytes(uint8_t * data, uint32_t size) */ void SPIClass::transferBytes(uint8_t * data, uint8_t * out, uint32_t size) { + if(_inTransaction){ + return spiTransferBytesNL(_spi, data, out, size); + } spiTransferBytes(_spi, data, out, size); } diff --git a/libraries/SPI/src/SPI.h b/libraries/SPI/src/SPI.h index f7b50962..ffa0bdf3 100644 --- a/libraries/SPI/src/SPI.h +++ b/libraries/SPI/src/SPI.h @@ -36,6 +36,19 @@ public: class SPIClass { +private: + int8_t _spi_num; + spi_t * _spi; + bool _use_hw_ss; + int8_t _sck; + int8_t _miso; + int8_t _mosi; + int8_t _ss; + uint32_t _div; + uint32_t _freq; + bool _inTransaction; + void writePattern_(uint8_t * data, uint8_t size, uint8_t repeat); + public: SPIClass(uint8_t spi_bus=HSPI); void begin(int8_t sck=-1, int8_t miso=-1, int8_t mosi=-1, int8_t ss=-1); @@ -61,17 +74,8 @@ public: void write32(uint32_t data); void writeBytes(uint8_t * data, uint32_t size); void writePattern(uint8_t * data, uint8_t size, uint32_t repeat); -private: - int8_t _spi_num; - spi_t * _spi; - bool _use_hw_ss; - int8_t _sck; - int8_t _miso; - int8_t _mosi; - int8_t _ss; - uint32_t _div; - uint32_t _freq; - void writePattern_(uint8_t * data, uint8_t size, uint8_t repeat); + + spi_t * bus(){ return _spi; } }; extern SPIClass SPI;