From d028b793258104eb9d886695931685e53533412f Mon Sep 17 00:00:00 2001 From: kazu Date: Sun, 12 Mar 2017 13:02:06 +0100 Subject: [PATCH] added data structures added audio support added rfid support added spi support --- data/Bitstream.h | 62 ++++++++ data/DoubleBuffer.h | 70 +++++++++ data/LinearBuffer.h | 59 ++++++++ data/RingBuffer.h | 79 +++++++++++ ext/audio/Audio.h | 124 ++++++++++++++++ ext/audio/AudioData.h | 41 ++++++ ext/audio/VS1003.h | 309 ++++++++++++++++++++++++++++++++++++++++ ext/audio/WaveHeader.h | 40 ++++++ ext/rfid/MFRC522.h | 74 ++++++++++ io/SPI.h | 312 +++++++++++++++++++++++++++++++++++++++++ io/SPIDefines.h | 4 + io/SoftSPI.h | 199 ++++++++++++++++++++++++++ 12 files changed, 1373 insertions(+) create mode 100644 data/Bitstream.h create mode 100644 data/DoubleBuffer.h create mode 100644 data/LinearBuffer.h create mode 100644 data/RingBuffer.h create mode 100644 ext/audio/Audio.h create mode 100644 ext/audio/AudioData.h create mode 100644 ext/audio/VS1003.h create mode 100644 ext/audio/WaveHeader.h create mode 100644 ext/rfid/MFRC522.h create mode 100644 io/SPI.h create mode 100644 io/SPIDefines.h create mode 100644 io/SoftSPI.h diff --git a/data/Bitstream.h b/data/Bitstream.h new file mode 100644 index 0000000..66706d4 --- /dev/null +++ b/data/Bitstream.h @@ -0,0 +1,62 @@ +#ifndef BITSTREAM_H +#define BITSTREAM_H + +#include + +template class Bitstream { + +public: + + inline void reset() { + bitMask = 128; // start with the MSB + curIdx = 0; // start with the first byte + buffer[curIdx] = 0; // initialize all bits with zero + numBitsUsed = 0; // nothing added + } + + inline void addOne() { + buffer[curIdx] |= (0xFF & bitMask); + bitMask >>= 1; + ++numBitsUsed; + checkNextByte(); + } + + inline void addZero() { + bitMask >>= 1; + ++numBitsUsed; + checkNextByte(); + } + + const uint8_t* getData() const { + return buffer; + } + + /** number of used bits */ + inline uint16_t getNumBits() const { + return numBitsUsed; + } + + /** number of used bytes. rounded-up to multiples of 8-bits */ + inline uint8_t getNumBytes() const { + return (numBitsUsed - 1) / 8 + 1; + } + +private: + + /** received 8 bits? byte complete? -> next one */ + inline void checkNextByte() { + if(bitMask == 0) { + bitMask = 128; // start with the MSB + ++curIdx; // next byte + buffer[curIdx] = 0; // initialize all bits with zero + } + } + + uint16_t numBitsUsed = 0; + uint8_t buffer[maxBytes]; //buffer containing received bits + uint8_t curIdx = 0; //index pointing to the byte currently selected within the buffer + uint8_t bitMask = 128; //mask for the next bit to write + +}; + +#endif // BITSTREAM_H diff --git a/data/DoubleBuffer.h b/data/DoubleBuffer.h new file mode 100644 index 0000000..6509a3a --- /dev/null +++ b/data/DoubleBuffer.h @@ -0,0 +1,70 @@ +#ifndef DOUBLEBUFFER_H +#define DOUBLEBUFFER_H + +template class DoubleBuffer { + +private: + + Scalar data[size*2]; + Scalar* ptr1; + Scalar* ptr2; + + volatile uint32_t head; + volatile bool _needsFlush; + +public: + + void init() { + ptr1 = &data[0]; + ptr2 = &data[size]; + head = 0; + _needsFlush = false; + } + + /** add new values to buffer A. swaps A and B if A is full */ + void add(const Scalar value) { + + ptr1[head] = value; // ptr1 + ++head; + + if (head >= size) { + head = 0; + swap(); + _needsFlush = true; + } + + } + + /** get data from buffer B */ + const Scalar* getData() const { + return ptr2; + } + + /** true if the buffer is currently empty */ + bool isEmpty() const { + return head == 0; + } + + bool needsFlush() const { + return _needsFlush; + } + + void markFlushed() { + _needsFlush = false; + } + + uint32_t getSize() const { + return size; + } + +private: + + void swap() { + Scalar* tmp = ptr1; + ptr1 = ptr2; + ptr2 = tmp; + } + +}; + +#endif // DOUBLEBUFFER_H diff --git a/data/LinearBuffer.h b/data/LinearBuffer.h new file mode 100644 index 0000000..3844635 --- /dev/null +++ b/data/LinearBuffer.h @@ -0,0 +1,59 @@ +#ifndef LINEARBUFFER_H +#define LINEARBUFFER_H + +template class LinearBuffer { + +private: + + Scalar _data[_size]; + + uint16_t head = 0; + +public: + + void add(const Scalar value) { + if (head >= _size) {return;} + _data[head] = value; + ++head; + } + + /** get the number of used entries */ + uint16_t getNumUsed() const { + return head; + } + + /** get the number of used bytes */ + uint32_t getBytesUsed() const { + return getNumUsed() * sizeof(Scalar); + } + + uint16_t size() const { + return _size; + } + + /** set the buffer to empty */ + void reset() { + head = 0; + } + + const Scalar* data() const { + return _data; + } + + /** constant array access */ + const Scalar& operator [] (const size_t idx) const { + return _data[idx]; + } + + /** true if the buffer is currently empty */ + bool isEmpty() const { + return head == 0; + } + + bool isFull() const { + return head >= size; + } + +}; + +#endif // LINEARBUFFER_H diff --git a/data/RingBuffer.h b/data/RingBuffer.h new file mode 100644 index 0000000..9cd5f95 --- /dev/null +++ b/data/RingBuffer.h @@ -0,0 +1,79 @@ +#ifndef RINGBUFFER_H +#define RINGBUFFER_H + +template class RingBuffer { + +private: + + Scalar data[size]; + + volatile size_t head; + volatile size_t tail; + volatile size_t used; + +public: + + void init() { + head = 0; + tail = 0; + used = 0; + } + + /** add one value to the buffer */ + void addBlocking(const Scalar value) { + + while (used == size) {os_delay_us(1000);} + + data[head] = value; + head = (head + 1) % size; + ++used; + } + + /** add one value to the buffer */ + void addIgnore(const Scalar value) { + + if (used == size) {return;} + + data[head] = value; + head = (head + 1) % size; + ++used; + } + + /** add multiple values to the buffer */ + void add(const Scalar* value, const size_t len) { + for (size_t i = 0; i < len; ++i) { + add(value[i]); + } + } + + /** anything available? */ + bool hasNext() const { + return used != 0; + } + + Scalar get() { + const Scalar res = data[tail]; + tail = (tail + 1) % size; + --used; + return res; + } + + const Scalar& getConst() { + const uint8_t idx = tail; + tail = (tail + 1) % size; + --used; + return data[idx]; + } + +// /** true if the buffer is currently empty */ +// bool isEmpty() const { +// return head == tail; +// } + + size_t getNumUsed() const { + return used; + } + +}; + +#endif // RINGBUFFER_H diff --git a/ext/audio/Audio.h b/ext/audio/Audio.h new file mode 100644 index 0000000..7bcd608 --- /dev/null +++ b/ext/audio/Audio.h @@ -0,0 +1,124 @@ +#ifndef AUDIO_H +#define AUDIO_H + +#include "../io/Timer0.h" +#include "../io/SoftSPI.h" +#include "../io/IO.h" +#include "../ESP.h" + +#include "../data/RingBuffer.h" +#include "../data/DoubleBuffer.h" + +class Audio; + +extern Audio audio; + +class Audio { + +private: + + static constexpr uint32_t Hz = 22050; + + RingBuffer bufPlayback; + DoubleBuffer bufRecord; + + Timer0 timer; + SoftSPI spi; + + + +public: + + Audio() { + // NOT CALLED + // CALL init() manually! + } + + + /** init the audio system */ + void init() { + + spi.init(); + + bufPlayback.init(); + bufRecord.init(); + + timer.attachInterrupt(&Audio::onInterrupt); + timer.fireLoopHz(Hz); + + } + + bool needsFlush() const { return bufRecord.needsFlush(); } + + /** get the recording buffer */ + DoubleBuffer& getRecord() {return bufRecord;} + + /** get the playback buffer */ + RingBuffer& getPlayback() {return bufPlayback;} + + void markFlushed() { bufRecord.markFlushed(); } + + +public: + + inline uint8_t playAndRecord8(const uint8_t out) { + + // start + spi.chipSelect(); + + // build output word for the DAC + const uint16_t out16 = (0 << 15) | (1 << 13) | (1 << 12) | (((uint16_t)out) << 4); + + // read 12-bit sample from the ADC while sending the word to the DAC + const uint16_t inp12 = (spi.readWriteWord(out16) & 0b0001111111111110) >> 1; + + //const uint16_t inp8 = (inp12>>0) - (2048+128); + //const uint16_t inp8 = (inp12>>2) - (512-128); + //const uint16_t inp8 = (inp12>>3) - (256-128); + const uint16_t inp8 = (inp12>>4); + + //const int16_t sinp12 = (int16_t)inp12 - 2048 - 256; + //const uint8_t inp8 = (sinp12 / 12) + 128; + + // done + spi.chipDeselect(); + return inp8; + + } + + inline void process() { + + // loop-back +// static volatile uint8_t inp; +// inp = playAndRecord8(inp); +// bufRecord.add(inp); + + // next sample to write [zero level (128) if none available] + const uint8_t out = (bufPlayback.getNumUsed() < 200) ? (128) : (bufPlayback.get()); + + // send output-sample and thereby read the input sample + const uint8_t inp = playAndRecord8(out); + + // append sample for writing + bufRecord.add(inp); + + } + + +// uint8_t rand() { +// static volatile uint8_t val = 131; +// val *= 17; +// val += 11; +// return val; +// } + +private: + + static inline void onInterrupt() { + //IO::toggleLED1(); + audio.process(); + } + +}; + +#endif // AUDIO_H diff --git a/ext/audio/AudioData.h b/ext/audio/AudioData.h new file mode 100644 index 0000000..573fa4c --- /dev/null +++ b/ext/audio/AudioData.h @@ -0,0 +1,41 @@ +#ifndef AUDIODATA_H +#define AUDIODATA_H + +#define min(a,b) ( (aavailable); + const uint8_t* ptr = this->data; + this->data += outBytes; + this->available -= outBytes; + return Data(ptr, outBytes); + } + + uint32_t getNumBytesAvailable() const { + return available; + } + + bool isEOF() const { + return available <= 0; + } + +}; + +#endif // AUDIODATA_H diff --git a/ext/audio/VS1003.h b/ext/audio/VS1003.h new file mode 100644 index 0000000..ce4268e --- /dev/null +++ b/ext/audio/VS1003.h @@ -0,0 +1,309 @@ +#ifndef VS1003_H +#define VS1003_H + +#include "io/fastGPIO.h" +#include "../io/SoftSPI.h" +//#include "IPlayable.h" +#include "AudioData.h" + +/** + * control a VS1003 audio chip attached to the SPI port + */ +class VS1003 { + +private: + + SoftSPI spi; + #define SPI_INIT() spi.init(); + #define SPI_WRITE_8(byte) spi.writeByte(byte) + #define SPI_WRITE_16(word) spi.writeWord(word) + #define SPI_READ_8(byte) spi.readByte(byte) + #define SPI_READ_16(word) spi.readWord(word) + #define SPI_READ_WRITE_8(byte) spi.readWriteByte(byte) + #define SPI_READ_WRITE_16(word) spi.readWriteWord(word) + //#define SPI_DELAY() os_delay_us(10) + #define SPI_DELAY() //os_delay_us(10) + + #define GET_DREQ() GPIO16_IN // PIN D0 + + + #define MP3_CTRL_DESELECT() spi.chipDeselect(); + #define MP3_CTRL_SELECT() spi.chipSelect(); + #define MP3_DATA_DESELECT() GPIO5_H + #define MP3_DATA_SELECT() GPIO5_L + + enum OP { + WRITE = 0x2, + READ = 0x3, + }; + + enum Register { + SCI_MODE = 0x0, + SCI_STATUS = 0x1, + SCI_BASS = 0x2, + SCI_CLOCKF = 0x3, + SCI_DECODE_TIME = 0x4, + SCI_AUDATA = 0x5, + SCI_WRAM = 0x6, + SCI_WRAMADDR = 0x7, + SCI_HDAT0 = 0x8, + SCI_HDAT1 = 0x9, + SCI_AIADDR = 0xa, + SCI_VOL = 0xb, + SCI_AICTRL0 = 0xc, + SCI_AICTRL1 = 0xd, + SCI_AICTRL2 = 0xe, + SCI_AICTRL3 = 0xf, + }; + + /** + * @brief Bitfield-combinable enum with all mode-flags that can be set for the VS1003. + */ + enum class Mode { + NONE = 0, + SM_DIFF = 1, + SM_SETTOZERO = 2, + SM_RESET = 4, + SM_OUTOFWAV = 8, + SM_POWERDOWN = 16, + SM_TESTS = 32, + SM_STREAM = 64, + SM_SETTOZERO2 = 128, + SM_DACT = 256, + SM_SDIORD = 512, + SM_SDISHARE = 1024, + SM_SDINEW = 2048, + SM_ADPCM = 4096, + SM_ADCPM_HP = 8192, + SM_LINE_IN = 16384, + }; + +public: + enum class RecordSource { + MICROPHONE, + LINE_IN + }; + + enum class PlayState { + STOPPED, + PLAYING, + RECORDING + }; + +public: + + /** initialize the hardware */ + void init() { + + SPI_INIT(); + + GPIO5_OUTPUT_SET; // Pin D1 - XDCS + GPIO16_INPUT_SET; // Pin D0 - DREQ + + this->state = PlayState::STOPPED; + + awaitDREQ(); // Wait for the VS1003 to finish boot + reset(); + + } + + /** + * @brief Set audio volume. + * @param Audio volume to set on both channels. + */ + void setVolume(const uint8_t vol) const { + const uint8_t aVol = 255 - vol; + const uint16_t values = (aVol << 0) | (aVol << 8); // left and right + writeRegister(SCI_VOL, values); + } + + uint8_t getVolume() const { + const uint16_t values = readRegister(SCI_VOL); + return (255 - (values & 0xFF)); + } + + void play(AudioData ad) { + this->playable = ad; + this->state = PlayState::PLAYING; + } + + void ICACHE_FLASH_ATTR record(RecordSource source) { + + state = PlayState::RECORDING; + writeRegister(Register::SCI_CLOCKF, 0x4430); /* 2.0x 12.288MHz */ + os_delay_us(100); + + writeRegister(Register::SCI_AICTRL0, 12); /* Div -> 12=8kHz 8=12kHz 6=16kHz */ + os_delay_us(100); + + writeRegister(Register::SCI_AICTRL1, 0); /* Auto gain */ + os_delay_us(100); + + setVolume(255); + + os_printf("mode: %d", readRegister(Register::SCI_MODE)); + + writeRegister(Register::SCI_MODE, readRegister(Register::SCI_MODE) | (int)Mode::SM_ADPCM | (int)Mode::SM_RESET); + os_delay_us(100); + + os_printf("mode: %d", readRegister(Register::SCI_MODE)); + + +// if(source == RecordSource::LINE_IN) { +// reset((Mode)((int)Mode::SM_LINE_IN | (int)Mode::SM_ADPCM)); +// } else { +// reset(Mode::SM_ADPCM); +// } + + awaitDREQ(); + os_delay_us(100); + + } + + PlayState ICACHE_FLASH_ATTR getState() const { + return this->state; + } + + + void ICACHE_FLASH_ATTR run() { + if(this->state == PlayState::PLAYING) { + if (!playable.isEOF()) { + const Data chunk = playable.read(32); + playChunk32(chunk.data, chunk.len); + } else { + this->state = PlayState::STOPPED; + uint8_t stopSequence[4] = {0}; + playChunk32(stopSequence, sizeof(stopSequence)); + } + + } + } + + uint32_t getRecordData(uint8_t* buffer, uint32_t maxLength) { + + uint32_t available = this->getNumAvailableRecordBytes(); + // os_printf("avail: %d\r\n", available); + + if (available < 256 || available >= 896) {return 0;} + + + int toRead = 256; + int read = 0; + while(read != toRead) { + const uint16_t data = readRegister(Register::SCI_HDAT0); + buffer[read] = (data >> 8); ++read; + buffer[read] = (data & 0xFF); ++read; + //os_delay_us(10); + } + + return toRead; + + +// for (int i = 0; i < 4; ++i) { +// os_printf("%d ", buffer[i]); +// } +// os_printf("\r\n"); + +// uint32_t len = min(maxLength, available); + +// for(int i = 0; i < len; ++i) { +// uint16_t data = readRegister(Register::SCI_HDAT0); +// buffer[i++] = (data >> 8); +// buffer[i] = (data & 0xFF); +// os_delay_us(5); +// } + + + + } + +private: + + uint32_t getNumAvailableRecordBytes() { + return (uint32_t)readRegister(Register::SCI_HDAT1) * sizeof(uint16_t); + } + + void ICACHE_FLASH_ATTR playChunk32(const uint8_t* buffer, const uint32_t len) const { + MP3_CTRL_DESELECT(); + MP3_DATA_SELECT(); + awaitDREQ(); + for(uint32_t i = 0; i < len; ++i) { + SPI_WRITE_8(buffer[i]); + } + MP3_DATA_DESELECT(); + } + + + /** + * @brief Write the given register. + * @param reg Register to write to. + * @param value Value to write into the given register. + */ + void writeRegister(const Register reg, const uint16_t value) const { + MP3_DATA_DESELECT(); + MP3_CTRL_SELECT(); + SPI_DELAY(); + SPI_WRITE_8(OP::WRITE); + SPI_WRITE_8(reg); + SPI_WRITE_16(value); // hi-byte -> low byte + SPI_DELAY(); + awaitDREQ(); + MP3_CTRL_DESELECT(); + } + + /** + * @brief Read the given register. + * @param reg Register to read. + * @return The value of the register with the given address. + */ + uint16_t readRegister(const Register reg) const { + MP3_DATA_DESELECT(); + MP3_CTRL_SELECT(); + SPI_DELAY(); + SPI_WRITE_8(OP::READ); + SPI_WRITE_8(reg); + const uint16_t res = SPI_READ_16(); + SPI_DELAY(); + awaitDREQ(); + MP3_CTRL_DESELECT(); + return res; + } + + /** + * @brief Wait for the VS1003 to set DREQ to high (it wants data). + */ + inline void awaitDREQ() const { + while(!GET_DREQ()) {;} + } + + /** + * @brief Reset the VS1003 chip. + * @param modeMask Additional mask to or into the mode that is written to the VS1003's register. + */ + void reset(Mode modeMask = Mode::NONE) const { + writeRegister(SCI_MODE, (2052 | (uint16_t)modeMask)); // TODO: remove SM_TESTS + os_delay_us(1); + awaitDREQ(); + writeRegister(SCI_CLOCKF, 0x9800); + setVolume(0); + os_delay_us(1); + + MP3_DATA_SELECT(); + SPI_WRITE_8(0); + SPI_WRITE_8(0); + SPI_WRITE_8(0); + SPI_WRITE_8(0); + MP3_DATA_DESELECT(); + + os_delay_us(100); + MP3_CTRL_DESELECT(); + } + +private: + //IPlayable* playable = nullptr; + AudioData playable; + PlayState state = PlayState::STOPPED; + +}; + +#endif // VS1003_H diff --git a/ext/audio/WaveHeader.h b/ext/audio/WaveHeader.h new file mode 100644 index 0000000..8d6d07a --- /dev/null +++ b/ext/audio/WaveHeader.h @@ -0,0 +1,40 @@ +#ifndef WAVE_HEADER_H +#define WAVE_HEADER_H + +namespace Wave { + + enum AudioFormat { + PCM = 0x01, + IMA_ADPCM = 0x11, + MP3 = 0x55, + }; + + struct Header { + + uint32_t chunkID; + uint32_t chunkSize; // file-size - 8 + char format[4] = {'W', 'A', 'V', 'E'}; + + char subChunk1ID[4] = {'f', 'm', 't', ' '}; + uint32_t subChunk1Size = 20; + + uint16_t audioFormat = AudioFormat::PCM; + uint16_t numChannels = 1; + uint32_t sampleRate = 22050; + uint32_t byteRate = sampleRate * 256 / 505; + uint16_t blockAlign = 0x100; + uint16_t bitsPerSample = 8; + + uint16_t byteExtraData = 2; + uint16_t extraData = 505; // samples-per-block. see byteRate + + char subChunk2ID[4] = {'f', 'a', 'c', 't'}; + uint32_t subChunk2Size = 4; + uint32_t numOfSamples = 0xFFFFFFFF; // play forever + + char subChunk3ID[4] = {'d', 'a', 't', 'a'}; + uint32_t subChunk3Size = 0xFFFFFFFF; // file-size - 60 + + }; + +#endif diff --git a/ext/rfid/MFRC522.h b/ext/rfid/MFRC522.h new file mode 100644 index 0000000..b3577b1 --- /dev/null +++ b/ext/rfid/MFRC522.h @@ -0,0 +1,74 @@ +#ifndef MFRC522_H +#define MFRC522_H + +#include "../../io/SoftSPI.h" + +/** + * RFID reader based on MFRC522 + * attached via SPI + * + * http://www.nxp.com/documents/data_sheet/MFRC522.pdf + * + */ +class MFRC522 { + + struct OP { + uint8_t addr : 7; // bits 0-6 + uint8_t rw : 1; // bit 7 [MSB] 1 = read, 0 = write + }; + + enum class Register { + + COMMAND_REG = 0x01, + INTERRUPTS_REG = 0x02, + ERROR_REG = 0x06, + STATUS1_REG = 0x07, + STATUS2_REG = 0x08, + FIFI_DATA_REG = 0x09, // access fifo buffer + FIFO_SIZE_REG = 0x0A, // number of available fifo data + MOD_REG = 0x11, + DEMOG_REG = 0x19, + + TEST_SEL_1_REG = 0x31, + TEST_SEL_2_REG = 0x32, + VERSION_REG = 0x37, // software version + + }; + + struct CommandReg { + uint8_t command : 4; + uint8_t power_down : 1; + uint8_t receiver_off : 1; + uint8_t reserved : 2; + }; + + struct InterruptsReg { + uint8_t timerInterruptEnable : 1; + uint8_t errorInterruptEnable : 1; + uint8_t loAlertInterruptEnable : 1; + uint8_t hiAlertInterruptEnable : 1; + uint8_t idleInterruptEnable : 1; + uint8_t rxInterruptEnable : 1; + uint8_t txInterruptEnable : 1; + uint8_t inverted : 1; + }; + +private: + + SoftSPI spi; + +public: + + MFRC522() { + ; + } + + /** init */ + void init() { + spi.init(); + } + + +}; + +#endif // MFRC522_H diff --git a/io/SPI.h b/io/SPI.h new file mode 100644 index 0000000..fa4bf1b --- /dev/null +++ b/io/SPI.h @@ -0,0 +1,312 @@ +#ifndef SPI_H +#define SPI_H + +// https://github.com/MetalPhreak/ESP8266_SPI_Driver/blob/master/include/driver/spi.h +// http://d.av.id.au/blog/esp8266-hardware-spi-driver-code-library/ + +// MTDI GPIO12 MISO (DIN) D6 +// MTCK GPIO13 MOSI (DOUT) D7 +// MTMS GPIO14 CLOCK D5 +// MTDO GPIO15 CS / SS D8 + +// http://www.14core.com/wp-content/uploads/2015/06/Node-MCU-Pin-Out-Diagram1.png + +extern "C" { + #include "eagle_soc.h" + #include "driver/spi_register.h" +} + + +class SPI { + + // user SPI + #define spi_no 1 + + #define SPI_CLK_USE_DIV 0 + #define SPI_CLK_80MHZ_NODIV 1 + + #define SPI_BYTE_ORDER_HIGH_TO_LOW 1 + #define SPI_BYTE_ORDER_LOW_TO_HIGH 0 + + //Define some default SPI clock settings + #define SPI_CLK_PREDIV 4 + #define SPI_CLK_CNTDIV 1 + #define SPI_CLK_FREQ CPU_CLK_FREQ/(SPI_CLK_PREDIV*SPI_CLK_CNTDIV) // 80 / 20 = 4 MHz + +public: + + SPI() { + init(); + } + +public: + + bool isBusy() const { + return READ_PERI_REG(SPI_CMD(spi_no)) & SPI_USR; + } + + void sendByte(const uint8_t byte) { + send(8, byte); + } + + void sendWord(const uint16_t word) { + send(16, word); + } + + uint8_t readByte() { + return read(8); + } + + uint16_t readWord() { + return read(16); + } + +private: + + void send(const uint8_t bits, const uint32_t val) { + + // wait for SPI to be free + while(isBusy()) {;} + + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI|SPI_USR_MISO|SPI_USR_COMMAND|SPI_USR_ADDR|SPI_USR_DUMMY); + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI); // MOSI only (write only) + + const uint32_t addr_bits = 0; + const uint32_t dout_bits = bits; + const uint32_t din_bits = 0; + const uint32_t dummy_bits = 0; + + WRITE_PERI_REG(SPI_USER1(spi_no), ((addr_bits-1)&SPI_USR_ADDR_BITLEN)< faster + //while (isBusy()) {;} + + } + + + + uint32_t read(const uint8_t bits) { + + // wait for SPI to be free + while(isBusy()) {;} + + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI|SPI_USR_MISO|SPI_USR_COMMAND|SPI_USR_ADDR|SPI_USR_DUMMY); + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MISO); // MISO only (read only) + + const uint32_t addr_bits = 0; + const uint32_t dout_bits = 0; + const uint32_t din_bits = bits; + const uint32_t dummy_bits = 0; + + WRITE_PERI_REG(SPI_USER1(spi_no), ((addr_bits-1)&SPI_USR_ADDR_BITLEN)<> (32-din_bits); //Assuming data in is written to MSB. TBC + //} else { + return READ_PERI_REG(SPI_W0(spi_no)); //Read in the same way as DOUT is sent. Note existing contents of SPI_W0 remain unless overwritten! + //} + + return 0; //something went wrong + + } + +private: + + void init() { + + spi_init_gpio(SPI_CLK_USE_DIV); + spi_clock(SPI_CLK_PREDIV, SPI_CLK_CNTDIV); + spi_tx_byte_order(SPI_BYTE_ORDER_HIGH_TO_LOW); + spi_rx_byte_order(SPI_BYTE_ORDER_HIGH_TO_LOW); + + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CS_SETUP|SPI_CS_HOLD); + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_FLASH_MODE); + + } + + void spi_init_gpio(const bool fullSpeed){ + + uint32 clock_div_flag = 0; + if (fullSpeed) { + clock_div_flag = 0x0001; + } + + WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105|(clock_div_flag<<9)); //Set bit 9 if 80MHz sysclock required + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2); //GPIO12 is HSPI MISO pin (Master Data In) + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2); //GPIO13 is HSPI MOSI pin (Master Data Out) + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2); //GPIO14 is HSPI CLK pin (Clock) + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2); //GPIO15 is HSPI CS pin (Chip Select / Slave Select) + + } + + /* + uint32 spi_transaction(uint8 spi_no, uint8 cmd_bits, uint16 cmd_data, uint32 addr_bits, uint32 addr_data, uint32 dout_bits, uint32 dout_data, + uint32 din_bits, uint32 dummy_bits){ + + if(spi_no > 1) return 0; //Check for a valid SPI + + //code for custom Chip Select as GPIO PIN here + + while(spi_busy(spi_no)); //wait for SPI to be ready + + //########## Enable SPI Functions ##########// + //disable MOSI, MISO, ADDR, COMMAND, DUMMY in case previously set. + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI|SPI_USR_MISO|SPI_USR_COMMAND|SPI_USR_ADDR|SPI_USR_DUMMY); + + //enable functions based on number of bits. 0 bits = disabled. + //This is rather inefficient but allows for a very generic function. + //CMD ADDR and MOSI are set below to save on an extra if statement. + // if(cmd_bits) {SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_COMMAND);} + // if(addr_bits) {SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_ADDR);} + if(din_bits) {SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MISO);} + if(dummy_bits) {SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_DUMMY);} + //########## END SECTION ##########// + + //########## Setup Bitlengths ##########// + WRITE_PERI_REG(SPI_USER1(spi_no), ((addr_bits-1)&SPI_USR_ADDR_BITLEN)<>8)&0xff) | ((command<<8)&0xff00); //swap byte order + WRITE_PERI_REG(SPI_USER2(spi_no), ((((cmd_bits-1)&SPI_USR_COMMAND_BITLEN)<>(32-(dout_bits - dout_extra_bits)))&dout_data)); + } else { + WRITE_PERI_REG(SPI_W0(spi_no), dout_data); + } + } + } + //########## END SECTION ##########// + + //########## Begin SPI Transaction ##########// + SET_PERI_REG_MASK(SPI_CMD(spi_no), SPI_USR); + //########## END SECTION ##########// + + //########## Return DIN data ##########// + if(din_bits) { + while(spi_busy(spi_no)); //wait for SPI transaction to complete + + if(READ_PERI_REG(SPI_USER(spi_no))&SPI_RD_BYTE_ORDER) { + return READ_PERI_REG(SPI_W0(spi_no)) >> (32-din_bits); //Assuming data in is written to MSB. TBC + } else { + return READ_PERI_REG(SPI_W0(spi_no)); //Read in the same way as DOUT is sent. Note existing contents of SPI_W0 remain unless overwritten! + } + + return 0; //something went wrong + } + //########## END SECTION ##########// + + //Transaction completed + return 1; //success + } + */ + + + + void spi_clock(const uint16 prediv, const uint8 cntdiv){ + + if ( (prediv==0) | (cntdiv==0) ) { + + WRITE_PERI_REG(SPI_CLOCK(spi_no), SPI_CLK_EQU_SYSCLK); // full speed + + } else { + + WRITE_PERI_REG(SPI_CLOCK(spi_no), + (((prediv-1)&SPI_CLKDIV_PRE)<>1)&SPI_CLKCNT_H)<