diff --git a/src/BASpiMemory.cpp b/src/BASpiMemory.cpp index 9ad8410..5f7a2fa 100644 --- a/src/BASpiMemory.cpp +++ b/src/BASpiMemory.cpp @@ -36,6 +36,7 @@ constexpr int SPI_MISO_MEM1 = 5; constexpr int SPI_SCK_MEM1 = 20; // SPI Constants +constexpr int SPI_WRITE_MODE_REG = 0x1; constexpr int SPI_WRITE_CMD = 0x2; constexpr int SPI_READ_CMD = 0x3; constexpr int SPI_ADDR_2_MASK = 0xFF0000; @@ -128,6 +129,7 @@ void BASpiMemory::write(size_t address, uint8_t *data, size_t numBytes) digitalWrite(m_csPin, HIGH); } + void BASpiMemory::zero(size_t address, size_t numBytes) { m_spi->beginTransaction(m_settings); @@ -257,4 +259,163 @@ void BASpiMemory::read16(size_t address, uint16_t *dest, size_t numWords) digitalWrite(m_csPin, HIGH); } +///////////////////////////////////////////////////////////////////////////// +// BASpiMemoryDMA +///////////////////////////////////////////////////////////////////////////// +BASpiMemoryDMA::BASpiMemoryDMA(SpiDeviceId memDeviceId, size_t bufferSizeBytes) +: BASpiMemory(memDeviceId), m_bufferSize(bufferSizeBytes) +{ + int cs; + switch (memDeviceId) { + case SpiDeviceId::SPI_DEVICE0 : + cs = SPI_CS_MEM0; + break; + case SpiDeviceId::SPI_DEVICE1 : + cs = SPI_CS_MEM1; + break; + default : + cs = SPI_CS_MEM0; + } + m_cs = new ActiveLowChipSelect(cs, m_settings); + // add 4 bytes to buffer for SPI CMD and 3 bytes of addresse + m_txBuffer = new uint8_t[bufferSizeBytes+4]; + m_rxBuffer = new uint8_t[bufferSizeBytes+4]; +} + +BASpiMemoryDMA::BASpiMemoryDMA(SpiDeviceId memDeviceId, uint32_t speedHz, size_t bufferSizeBytes) +: BASpiMemory(memDeviceId, speedHz), m_bufferSize(bufferSizeBytes) +{ + int cs; + switch (memDeviceId) { + case SpiDeviceId::SPI_DEVICE0 : + cs = SPI_CS_MEM0; + break; + case SpiDeviceId::SPI_DEVICE1 : + cs = SPI_CS_MEM1; + break; + } + m_cs = new ActiveLowChipSelect(cs, m_settings); + m_txBuffer = new uint8_t[bufferSizeBytes+4]; + m_rxBuffer = new uint8_t[bufferSizeBytes+4]; +} + +BASpiMemoryDMA::~BASpiMemoryDMA() +{ + delete m_cs; + if (m_txBuffer) delete [] m_txBuffer; + if (m_rxBuffer) delete [] m_rxBuffer; +} + +void BASpiMemoryDMA::m_setSpiCmdAddr(int command, size_t address, uint8_t *dest) +{ + dest[0] = command; + dest[1] = ((address & SPI_ADDR_2_MASK) >> SPI_ADDR_2_SHIFT); + dest[2] = ((address & SPI_ADDR_1_MASK) >> SPI_ADDR_1_SHIFT); + dest[3] = ((address & SPI_ADDR_0_MASK)); +} + +void BASpiMemoryDMA::begin(void) +{ + switch (m_memDeviceId) { + case SpiDeviceId::SPI_DEVICE0 : + m_csPin = SPI_CS_MEM0; + m_spi = &SPI; + m_spi->setMOSI(SPI_MOSI_MEM0); + m_spi->setMISO(SPI_MISO_MEM0); + m_spi->setSCK(SPI_SCK_MEM0); + m_spi->begin(); + m_spiDma = &DMASPI0; + break; + +#if defined(__MK64FX512__) || defined(__MK66FX1M0__) + case SpiDeviceId::SPI_DEVICE1 : + m_csPin = SPI_CS_MEM1; + m_spi = &SPI1; + m_spi->setMOSI(SPI_MOSI_MEM1); + m_spi->setMISO(SPI_MISO_MEM1); + m_spi->setSCK(SPI_SCK_MEM1); + m_spi->begin(); + m_spiDma = &DMASPI1; + break; +#endif + + default : + // unreachable since memDeviceId is an enumerated class + return; + } + + m_spiDma->begin(); + m_spiDma->start(); + + m_started = true; +} + +// SPI must build up a payload that starts the teh CMD/Address first. It will cycle +// through the payloads in a circular buffer and use the transfer objects to check if they +// are done before continuing. +void BASpiMemoryDMA::write(size_t address, uint8_t *data, size_t numBytes) +{ + while ( m_txTransfer->busy()) {} + uint16_t transferCount = numBytes + 4; + m_setSpiCmdAddr(SPI_WRITE_CMD, address, m_txBuffer); + memcpy(m_txBuffer+4, data, numBytes); + *m_txTransfer = DmaSpi::Transfer(m_txBuffer, transferCount, nullptr, 0, m_cs); + m_spiDma->registerTransfer(*m_txTransfer); +} + +void BASpiMemoryDMA::zero(size_t address, size_t numBytes) +{ + while ( m_txTransfer->busy()) {} + uint16_t transferCount = numBytes + 4; + m_setSpiCmdAddr(SPI_WRITE_CMD, address, m_txBuffer); + *m_txTransfer = DmaSpi::Transfer(nullptr, transferCount, nullptr, 0, m_cs); + m_spiDma->registerTransfer(*m_txTransfer); +} + +void BASpiMemoryDMA::write16(size_t address, uint16_t *data, size_t numWords) +{ + while ( m_txTransfer->busy()) {} + size_t numBytes = sizeof(uint16_t)*numWords; + uint16_t transferCount = numBytes + 4; + m_setSpiCmdAddr(SPI_WRITE_CMD, address, m_txBuffer); + memcpy(m_txBuffer+4, data, numBytes); + *m_txTransfer = DmaSpi::Transfer(m_txBuffer, transferCount, nullptr, 0, m_cs); + m_spiDma->registerTransfer(*m_txTransfer); +} + +void BASpiMemoryDMA::zero16(size_t address, size_t numWords) +{ + while ( m_txTransfer->busy()) {} + size_t numBytes = sizeof(uint16_t)*numWords; + uint16_t transferCount = numBytes + 4; + m_setSpiCmdAddr(SPI_WRITE_CMD, address, m_txBuffer); + memset(m_txBuffer+4, 0, numBytes); + *m_txTransfer = DmaSpi::Transfer(m_txBuffer, transferCount, nullptr, 0, m_cs); + m_spiDma->registerTransfer(*m_txTransfer); +} + +void BASpiMemoryDMA::read(size_t address, uint8_t *data, size_t numBytes) +{ + while ( m_rxTransfer->busy()) {} + uint16_t transferCount = numBytes + 4; + m_setSpiCmdAddr(SPI_READ_CMD, address, m_rxBuffer); + *m_rxTransfer = DmaSpi::Transfer(nullptr, transferCount, m_rxBuffer, 0, m_cs); + m_spiDma->registerTransfer(*m_rxTransfer); +} + +void BASpiMemoryDMA::read16(size_t address, uint16_t *dest, size_t numWords) +{ + while ( m_rxTransfer->busy()) {} + m_setSpiCmdAddr(SPI_READ_CMD, address, m_rxBuffer); + size_t numBytes = sizeof(uint16_t)*numWords; + uint16_t transferCount = numBytes + 4; + *m_rxTransfer = DmaSpi::Transfer(nullptr, transferCount, m_rxBuffer, 0, m_cs); + m_spiDma->registerTransfer(*m_rxTransfer); +} + +void BASpiMemoryDMA::readBufferContents(size_t bufferOffset, uint8_t *dest, size_t numBytes) +{ + memcpy(dest, m_rxBuffer+4, numBytes); +} + } /* namespace BAGuitar */ diff --git a/src/BASpiMemory.h b/src/BASpiMemory.h index 2f2a765..c0a6149 100644 --- a/src/BASpiMemory.h +++ b/src/BASpiMemory.h @@ -23,7 +23,9 @@ #define __SRC_BASPIMEMORY_H #include +#include +#include "BATypes.h" #include "BAHardware.h" namespace BAGuitar { @@ -47,25 +49,25 @@ public: BASpiMemory(SpiDeviceId memDeviceId, uint32_t speedHz); virtual ~BASpiMemory(); - void begin(void); + virtual void begin(); /// write a single data word to the specified address /// @param address the address in the SPI RAM to write to /// @param data the value to write void write(size_t address, uint8_t data); - void write(size_t address, uint8_t *data, size_t numBytes); - void zero(size_t address, size_t numBytes); + virtual void write(size_t address, uint8_t *data, size_t numBytes); + virtual void zero(size_t address, size_t numBytes); void write16(size_t address, uint16_t data); - void write16(size_t address, uint16_t *data, size_t numWords); + virtual void write16(size_t address, uint16_t *data, size_t numWords); - void zero16(size_t address, size_t numWords); + virtual void zero16(size_t address, size_t numWords); /// read a single 8-bit data word from the specified address /// @param address the address in the SPI RAM to read from /// @return the data that was read uint8_t read(size_t address); - void read(size_t address, uint8_t *data, size_t numBytes); + virtual void read(size_t address, uint8_t *data, size_t numBytes); /// read a single 16-bit data word from the specified address /// @param address the address in the SPI RAM to read from @@ -76,11 +78,11 @@ public: /// @param address the address in the SPI RAM to read from /// @param dest the pointer to the destination /// @param numWords the number of 16-bit words to transfer - void read16(size_t address, uint16_t *dest, size_t numWords); + virtual void read16(size_t address, uint16_t *dest, size_t numWords); bool isStarted() const { return m_started; } -private: +protected: SPIClass *m_spi = nullptr; SpiDeviceId m_memDeviceId; // the MEM device being control with this instance uint8_t m_csPin; // the IO pin number for the CS on the controlled SPI device @@ -89,7 +91,56 @@ private: }; -class BASpiMemoryException {}; +//constexpr int MAX_DMA_XFERS = 4; + +class BASpiMemoryDMA : public BASpiMemory { + BASpiMemoryDMA() = delete; + /// Create an object to control either MEM0 (via SPI1) or MEM1 (via SPI2). + /// @details default is 20 Mhz + /// @param memDeviceId specify which MEM to control with SpiDeviceId. + /// @param bufferSize size of buffer to store DMA transfers + BASpiMemoryDMA(SpiDeviceId memDeviceId, size_t bufferSizeBytes); + /// Create an object to control either MEM0 (via SPI1) or MEM1 (via SPI2) + /// @param memDeviceId specify which MEM to control with SpiDeviceId. + /// @param speedHz specify the desired speed in Hz. + /// @param bufferSize size of buffer to store DMA transfers + BASpiMemoryDMA(SpiDeviceId memDeviceId, uint32_t speedHz, size_t bufferSizeBytes); + virtual ~BASpiMemoryDMA(); + + void write(size_t address, uint8_t *data, size_t numBytes) override; + void zero(size_t address, size_t numBytes) override; + void write16(size_t address, uint16_t *data, size_t numWords) override; + void zero16(size_t address, size_t numWords) override; + + /// read a single 8-bit data word from the specified address + /// @param address the address in the SPI RAM to read from + /// @return the data that was read + void read(size_t address, uint8_t *data, size_t numBytes) override; + + /// read a block 16-bit data word from the specified address + /// @param address the address in the SPI RAM to read from + /// @param dest the pointer to the destination + /// @param numWords the number of 16-bit words to transfer + void read16(size_t address, uint16_t *dest, size_t numWords) override; + + + void begin() override; + void readBufferContents(size_t bufferOffset, uint8_t *dest, size_t numBytes); + +private: + AbstractDmaSpi *m_spiDma = nullptr; + ActiveLowChipSelect *m_cs = nullptr; + size_t m_bufferSize; + uint8_t *m_txBuffer = nullptr; + DmaSpi::Transfer *m_txTransfer; + uint8_t *m_rxBuffer = nullptr; + DmaSpi::Transfer *m_rxTransfer; + + void m_setSpiCmdAddr(int command, size_t address, uint8_t *dest); +// RingBuffer m_txFifo(MAX_DMA_XFERS); +// RingBuffer m_rxFifo(MAX_DMA_XFERS); +}; + } /* namespace BAGuitar */ diff --git a/src/BATypes.h b/src/BATypes.h new file mode 100644 index 0000000..2c2bbf9 --- /dev/null +++ b/src/BATypes.h @@ -0,0 +1,142 @@ +/* + * BATypes.h + * + * Created on: Feb 7, 2018 + * Author: slascos + */ + +#ifndef SRC_BATYPES_H_ +#define SRC_BATYPES_H_ + +namespace BAGuitar { + +/**************************************************************************//** + * Customer RingBuffer with random access + *****************************************************************************/ +template +class RingBuffer { +public: + RingBuffer() = delete; + + /// Construct a RingBuffer of specified max size + /// @param maxSize number of entries in ring buffer + RingBuffer(const size_t maxSize) : m_maxSize(maxSize) { + m_buffer = new T[maxSize](); + } + virtual ~RingBuffer(){ + if (m_buffer) delete [] m_buffer; + } + + /// Add an element to the back of the queue + /// @param element element to add to queue + /// returns 0 if success, otherwise error + int push_back(T element) { + + //Serial.println(String("RingBuffer::push_back...") + m_head + String(":") + m_tail + String(":") + m_size); + if ( (m_head == m_tail) && (m_size > 0) ) { + // overflow + Serial.println("RingBuffer::push_back: overflow"); + return -1; + } + + m_buffer[m_head] = element; + if (m_head < (m_maxSize-1) ) { + m_head++; + } else { + m_head = 0; + } + m_size++; + + return 0; + } + + /// Remove the element at teh front of the queue + /// @returns 0 if success, otherwise error + int pop_front() { + + if (m_size == 0) { + // buffer is empty + //Serial.println("RingBuffer::pop_front: buffer is empty\n"); + return -1; + } + if (m_tail < m_maxSize-1) { + m_tail++; + } else { + m_tail = 0; + } + m_size--; + //Serial.println(String("RingBuffer::pop_front: ") + m_head + String(":") + m_tail + String(":") + m_size); + return 0; + } + + /// Get the element at the front of the queue + /// @returns element at front of queue + T front() const { + return m_buffer[m_tail]; + } + + /// get the element at the back of the queue + /// @returns element at the back of the queue + T back() const { + return m_buffer[m_head-1]; + } + + /// Get a previously pushed elememt + /// @param offset zero is last pushed, 1 is second last, etc. + /// @returns the absolute index corresponding to the requested offset. + size_t get_index_from_back(size_t offset = 0) const { + // the target at m_head - 1 - offset or m_maxSize + m_head -1 - offset; + size_t idx = (m_maxSize + m_head -1 - offset); + + if ( idx >= m_maxSize) { + idx -= m_maxSize; + } + + return idx; + } + + /// get the current size of the queue + /// @returns size of the queue + size_t size() const { + return m_size; + } + + /// get the maximum size the queue can hold + /// @returns maximum size of the queue + size_t max_size() const { + return m_maxSize; + } + + /// get the element at the specified absolute index + /// @param index element to retrieve from absolute queue position + /// @returns the request element + T& operator[] (size_t index) { + return m_buffer[index]; + } + + /// get the element at the specified absolute index + /// @param index element to retrieve from absolute queue position + /// @returns the request element + T at(size_t index) const { + return m_buffer[index]; + } + + /// DEBUG: Prints the status of the Ringbuffer. NOte using this much printing will usually cause audio glitches + void print() const { + for (int idx=0; idxdata, HEX); + } + } +private: + size_t m_head=0; ///< back of the queue + size_t m_tail=0; ///< front of the queue + size_t m_size=0; ///< current size of the qeueu + T *m_buffer = nullptr; ///< pointer to the allocated buffer array + const size_t m_maxSize; ///< maximum size of the queue +}; + +} // BAGuitar + + +#endif /* SRC_BATYPES_H_ */ diff --git a/src/LibBasicFunctions.h b/src/LibBasicFunctions.h index 2498155..757ce4d 100644 --- a/src/LibBasicFunctions.h +++ b/src/LibBasicFunctions.h @@ -27,6 +27,7 @@ #include "Arduino.h" #include "Audio.h" +#include "BATypes.h" #include "LibMemoryManagement.h" #ifndef __LIBBASICFUNCTIONS_H @@ -213,131 +214,131 @@ private: }; -/**************************************************************************//** - * Customer RingBuffer with random access - *****************************************************************************/ -template -class RingBuffer { -public: - RingBuffer() = delete; - - /// Construct a RingBuffer of specified max size - /// @param maxSize number of entries in ring buffer - RingBuffer(const size_t maxSize) : m_maxSize(maxSize) { - m_buffer = new T[maxSize](); - } - virtual ~RingBuffer(){ - if (m_buffer) delete [] m_buffer; - } - - /// Add an element to the back of the queue - /// @param element element to add to queue - /// returns 0 if success, otherwise error - int push_back(T element) { - - //Serial.println(String("RingBuffer::push_back...") + m_head + String(":") + m_tail + String(":") + m_size); - if ( (m_head == m_tail) && (m_size > 0) ) { - // overflow - Serial.println("RingBuffer::push_back: overflow"); - return -1; - } - - m_buffer[m_head] = element; - if (m_head < (m_maxSize-1) ) { - m_head++; - } else { - m_head = 0; - } - m_size++; - - return 0; - } - - /// Remove the element at teh front of the queue - /// @returns 0 if success, otherwise error - int pop_front() { - - if (m_size == 0) { - // buffer is empty - //Serial.println("RingBuffer::pop_front: buffer is empty\n"); - return -1; - } - if (m_tail < m_maxSize-1) { - m_tail++; - } else { - m_tail = 0; - } - m_size--; - //Serial.println(String("RingBuffer::pop_front: ") + m_head + String(":") + m_tail + String(":") + m_size); - return 0; - } - - /// Get the element at the front of the queue - /// @returns element at front of queue - T front() const { - return m_buffer[m_tail]; - } - - /// get the element at the back of the queue - /// @returns element at the back of the queue - T back() const { - return m_buffer[m_head-1]; - } - - /// Get a previously pushed elememt - /// @param offset zero is last pushed, 1 is second last, etc. - /// @returns the absolute index corresponding to the requested offset. - size_t get_index_from_back(size_t offset = 0) const { - // the target at m_head - 1 - offset or m_maxSize + m_head -1 - offset; - size_t idx = (m_maxSize + m_head -1 - offset); - - if ( idx >= m_maxSize) { - idx -= m_maxSize; - } - - return idx; - } - - /// get the current size of the queue - /// @returns size of the queue - size_t size() const { - return m_size; - } - - /// get the maximum size the queue can hold - /// @returns maximum size of the queue - size_t max_size() const { - return m_maxSize; - } - - /// get the element at the specified absolute index - /// @param index element to retrieve from absolute queue position - /// @returns the request element - T& operator[] (size_t index) { - return m_buffer[index]; - } - - /// get the element at the specified absolute index - /// @param index element to retrieve from absolute queue position - /// @returns the request element - T at(size_t index) const { - return m_buffer[index]; - } - - /// DEBUG: Prints the status of the Ringbuffer. NOte using this much printing will usually cause audio glitches - void print() const { - for (int idx=0; idxdata, HEX); - } - } -private: - size_t m_head=0; ///< back of the queue - size_t m_tail=0; ///< front of the queue - size_t m_size=0; ///< current size of the qeueu - T *m_buffer = nullptr; ///< pointer to the allocated buffer array - const size_t m_maxSize; ///< maximum size of the queue -}; +///**************************************************************************//** +// * Customer RingBuffer with random access +// *****************************************************************************/ +//template +//class RingBuffer { +//public: +// RingBuffer() = delete; +// +// /// Construct a RingBuffer of specified max size +// /// @param maxSize number of entries in ring buffer +// RingBuffer(const size_t maxSize) : m_maxSize(maxSize) { +// m_buffer = new T[maxSize](); +// } +// virtual ~RingBuffer(){ +// if (m_buffer) delete [] m_buffer; +// } +// +// /// Add an element to the back of the queue +// /// @param element element to add to queue +// /// returns 0 if success, otherwise error +// int push_back(T element) { +// +// //Serial.println(String("RingBuffer::push_back...") + m_head + String(":") + m_tail + String(":") + m_size); +// if ( (m_head == m_tail) && (m_size > 0) ) { +// // overflow +// Serial.println("RingBuffer::push_back: overflow"); +// return -1; +// } +// +// m_buffer[m_head] = element; +// if (m_head < (m_maxSize-1) ) { +// m_head++; +// } else { +// m_head = 0; +// } +// m_size++; +// +// return 0; +// } +// +// /// Remove the element at teh front of the queue +// /// @returns 0 if success, otherwise error +// int pop_front() { +// +// if (m_size == 0) { +// // buffer is empty +// //Serial.println("RingBuffer::pop_front: buffer is empty\n"); +// return -1; +// } +// if (m_tail < m_maxSize-1) { +// m_tail++; +// } else { +// m_tail = 0; +// } +// m_size--; +// //Serial.println(String("RingBuffer::pop_front: ") + m_head + String(":") + m_tail + String(":") + m_size); +// return 0; +// } +// +// /// Get the element at the front of the queue +// /// @returns element at front of queue +// T front() const { +// return m_buffer[m_tail]; +// } +// +// /// get the element at the back of the queue +// /// @returns element at the back of the queue +// T back() const { +// return m_buffer[m_head-1]; +// } +// +// /// Get a previously pushed elememt +// /// @param offset zero is last pushed, 1 is second last, etc. +// /// @returns the absolute index corresponding to the requested offset. +// size_t get_index_from_back(size_t offset = 0) const { +// // the target at m_head - 1 - offset or m_maxSize + m_head -1 - offset; +// size_t idx = (m_maxSize + m_head -1 - offset); +// +// if ( idx >= m_maxSize) { +// idx -= m_maxSize; +// } +// +// return idx; +// } +// +// /// get the current size of the queue +// /// @returns size of the queue +// size_t size() const { +// return m_size; +// } +// +// /// get the maximum size the queue can hold +// /// @returns maximum size of the queue +// size_t max_size() const { +// return m_maxSize; +// } +// +// /// get the element at the specified absolute index +// /// @param index element to retrieve from absolute queue position +// /// @returns the request element +// T& operator[] (size_t index) { +// return m_buffer[index]; +// } +// +// /// get the element at the specified absolute index +// /// @param index element to retrieve from absolute queue position +// /// @returns the request element +// T at(size_t index) const { +// return m_buffer[index]; +// } +// +// /// DEBUG: Prints the status of the Ringbuffer. NOte using this much printing will usually cause audio glitches +// void print() const { +// for (int idx=0; idxdata, HEX); +// } +// } +//private: +// size_t m_head=0; ///< back of the queue +// size_t m_tail=0; ///< front of the queue +// size_t m_size=0; ///< current size of the qeueu +// T *m_buffer = nullptr; ///< pointer to the allocated buffer array +// const size_t m_maxSize; ///< maximum size of the queue +//}; }