added data structures
added audio support added rfid support added spi support
This commit is contained in:
62
data/Bitstream.h
Normal file
62
data/Bitstream.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef BITSTREAM_H
|
||||
#define BITSTREAM_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
template <int maxBytes> 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
|
||||
70
data/DoubleBuffer.h
Normal file
70
data/DoubleBuffer.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifndef DOUBLEBUFFER_H
|
||||
#define DOUBLEBUFFER_H
|
||||
|
||||
template <typename Scalar, int size> 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
|
||||
59
data/LinearBuffer.h
Normal file
59
data/LinearBuffer.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef LINEARBUFFER_H
|
||||
#define LINEARBUFFER_H
|
||||
|
||||
template <typename Scalar, int _size> 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
|
||||
79
data/RingBuffer.h
Normal file
79
data/RingBuffer.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#ifndef RINGBUFFER_H
|
||||
#define RINGBUFFER_H
|
||||
|
||||
template <typename Scalar, int size> 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
|
||||
124
ext/audio/Audio.h
Normal file
124
ext/audio/Audio.h
Normal file
@@ -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<uint8_t, 1500> bufPlayback;
|
||||
DoubleBuffer<uint8_t, 512> 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<uint8_t, 512>& getRecord() {return bufRecord;}
|
||||
|
||||
/** get the playback buffer */
|
||||
RingBuffer<uint8_t, 1500>& 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
|
||||
41
ext/audio/AudioData.h
Normal file
41
ext/audio/AudioData.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef AUDIODATA_H
|
||||
#define AUDIODATA_H
|
||||
|
||||
#define min(a,b) ( (a<b) ? (a) : (b) )
|
||||
|
||||
struct Data {
|
||||
const uint8_t* data;
|
||||
const uint32_t len;
|
||||
Data(const uint8_t* data, const uint32_t len) : data(data), len(len) {;}
|
||||
};
|
||||
|
||||
struct AudioData {
|
||||
|
||||
const uint8_t* data;
|
||||
uint32_t available;
|
||||
|
||||
AudioData() : data(nullptr), available(0) {;}
|
||||
|
||||
AudioData(const uint8_t* data, const uint32_t len) : data(data), available(len) {
|
||||
|
||||
}
|
||||
|
||||
Data read(const uint32_t bytes) {
|
||||
const uint32_t outBytes = min(bytes, this->available);
|
||||
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
|
||||
309
ext/audio/VS1003.h
Normal file
309
ext/audio/VS1003.h
Normal file
@@ -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
|
||||
40
ext/audio/WaveHeader.h
Normal file
40
ext/audio/WaveHeader.h
Normal file
@@ -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
|
||||
74
ext/rfid/MFRC522.h
Normal file
74
ext/rfid/MFRC522.h
Normal file
@@ -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
|
||||
312
io/SPI.h
Normal file
312
io/SPI.h
Normal file
@@ -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)<<SPI_USR_ADDR_BITLEN_S | //Number of bits in Address
|
||||
((dout_bits-1)&SPI_USR_MOSI_BITLEN)<<SPI_USR_MOSI_BITLEN_S | //Number of bits to Send
|
||||
((din_bits-1)&SPI_USR_MISO_BITLEN)<<SPI_USR_MISO_BITLEN_S | //Number of bits to receive
|
||||
((dummy_bits-1)&SPI_USR_DUMMY_CYCLELEN)<<SPI_USR_DUMMY_CYCLELEN_S); //Number of Dummy bits to insert
|
||||
|
||||
//if (READ_PERI_REG(SPI_USER(spi_no))&SPI_WR_BYTE_ORDER) {
|
||||
WRITE_PERI_REG(SPI_W0(spi_no), val);
|
||||
//}
|
||||
|
||||
// start transfer
|
||||
SET_PERI_REG_MASK(SPI_CMD(spi_no), SPI_USR);
|
||||
|
||||
// do NOT wait for the transfer to finish! -> 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)<<SPI_USR_ADDR_BITLEN_S | //Number of bits in Address
|
||||
((dout_bits-1)&SPI_USR_MOSI_BITLEN)<<SPI_USR_MOSI_BITLEN_S | //Number of bits to Send
|
||||
((din_bits-1)&SPI_USR_MISO_BITLEN)<<SPI_USR_MISO_BITLEN_S | //Number of bits to receive
|
||||
((dummy_bits-1)&SPI_USR_DUMMY_CYCLELEN)<<SPI_USR_DUMMY_CYCLELEN_S); //Number of Dummy bits to insert
|
||||
|
||||
SET_PERI_REG_MASK(SPI_CMD(spi_no), SPI_USR);
|
||||
|
||||
// wait for the reading to finish
|
||||
while (isBusy()) {;}
|
||||
|
||||
//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
|
||||
|
||||
}
|
||||
|
||||
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)<<SPI_USR_ADDR_BITLEN_S | //Number of bits in Address
|
||||
((dout_bits-1)&SPI_USR_MOSI_BITLEN)<<SPI_USR_MOSI_BITLEN_S | //Number of bits to Send
|
||||
((din_bits-1)&SPI_USR_MISO_BITLEN)<<SPI_USR_MISO_BITLEN_S | //Number of bits to receive
|
||||
((dummy_bits-1)&SPI_USR_DUMMY_CYCLELEN)<<SPI_USR_DUMMY_CYCLELEN_S); //Number of Dummy bits to insert
|
||||
//########## END SECTION ##########//
|
||||
|
||||
//########## Setup Command Data ##########//
|
||||
if(cmd_bits) {
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_COMMAND); //enable COMMAND function in SPI module
|
||||
uint16 command = cmd_data << (16-cmd_bits); //align command data to high bits
|
||||
command = ((command>>8)&0xff) | ((command<<8)&0xff00); //swap byte order
|
||||
WRITE_PERI_REG(SPI_USER2(spi_no), ((((cmd_bits-1)&SPI_USR_COMMAND_BITLEN)<<SPI_USR_COMMAND_BITLEN_S) | command&SPI_USR_COMMAND_VALUE));
|
||||
}
|
||||
//########## END SECTION ##########//
|
||||
|
||||
//########## Setup Address Data ##########//
|
||||
if(addr_bits){
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_ADDR); //enable ADDRess function in SPI module
|
||||
WRITE_PERI_REG(SPI_ADDR(spi_no), addr_data<<(32-addr_bits)); //align address data to high bits
|
||||
}
|
||||
|
||||
|
||||
//########## END SECTION ##########//
|
||||
|
||||
//########## Setup DOUT data ##########//
|
||||
if(dout_bits) {
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI); //enable MOSI function in SPI module
|
||||
//copy data to W0
|
||||
if(READ_PERI_REG(SPI_USER(spi_no))&SPI_WR_BYTE_ORDER) {
|
||||
WRITE_PERI_REG(SPI_W0(spi_no), dout_data<<(32-dout_bits));
|
||||
} else {
|
||||
|
||||
uint8 dout_extra_bits = dout_bits%8;
|
||||
|
||||
if(dout_extra_bits){
|
||||
//if your data isn't a byte multiple (8/16/24/32 bits)and you don't have SPI_WR_BYTE_ORDER set, you need this to move the non-8bit remainder to the MSBs
|
||||
//not sure if there's even a use case for this, but it's here if you need it...
|
||||
//for example, 0xDA4 12 bits without SPI_WR_BYTE_ORDER would usually be output as if it were 0x0DA4,
|
||||
//of which 0xA4, and then 0x0 would be shifted out (first 8 bits of low byte, then 4 MSB bits of high byte - ie reverse byte order).
|
||||
//The code below shifts it out as 0xA4 followed by 0xD as you might require.
|
||||
WRITE_PERI_REG(SPI_W0(spi_no), ((0xFFFFFFFF<<(dout_bits - dout_extra_bits)&dout_data)<<(8-dout_extra_bits) | (0xFFFFFFFF>>(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)<<SPI_CLKDIV_PRE_S)|
|
||||
(((cntdiv-1)&SPI_CLKCNT_N)<<SPI_CLKCNT_N_S)|
|
||||
(((cntdiv>>1)&SPI_CLKCNT_H)<<SPI_CLKCNT_H_S)|
|
||||
((0&SPI_CLKCNT_L)<<SPI_CLKCNT_L_S));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void spi_mode(const uint8 spi_cpha, const uint8 spi_cpol) {
|
||||
|
||||
if(!spi_cpha == !spi_cpol) {
|
||||
CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_OUT_EDGE);
|
||||
} else {
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_OUT_EDGE);
|
||||
}
|
||||
|
||||
if (spi_cpol) {
|
||||
SET_PERI_REG_MASK(SPI_PIN(spi_no), SPI_IDLE_EDGE);
|
||||
} else {
|
||||
CLEAR_PERI_REG_MASK(SPI_PIN(spi_no), SPI_IDLE_EDGE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void spi_tx_byte_order(const uint8 byte_order){
|
||||
if (byte_order){
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_WR_BYTE_ORDER);
|
||||
} else {
|
||||
CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_WR_BYTE_ORDER);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void spi_rx_byte_order(const uint8 byte_order){
|
||||
if (byte_order){
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_RD_BYTE_ORDER);
|
||||
} else {
|
||||
CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_RD_BYTE_ORDER);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#undef spi_no
|
||||
|
||||
#endif // SPI_H
|
||||
4
io/SPIDefines.h
Normal file
4
io/SPIDefines.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#ifndef SPIDEFINES_H
|
||||
#define SPIDEFINES_H
|
||||
|
||||
#endif // SPIDEFINES_H
|
||||
199
io/SoftSPI.h
Normal file
199
io/SoftSPI.h
Normal file
@@ -0,0 +1,199 @@
|
||||
#ifndef SOFTSPI_H
|
||||
#define SOFTSPI_H
|
||||
|
||||
//#include "IO.h"
|
||||
#include "fastGPIO.h"
|
||||
|
||||
class SoftSPI {
|
||||
|
||||
// MTDI GPIO12 MISO (DIN) D6
|
||||
// MTCK GPIO13 MOSI (DOUT) D7
|
||||
// MTMS GPIO14 CLOCK D5
|
||||
// MTDO GPIO15 CS / SS D8
|
||||
|
||||
// #define PIN_CLK 14
|
||||
// #define PIN_MISO 12
|
||||
// #define PIN_MOSI 13
|
||||
|
||||
// #define BIT(nr) (1 << 0)
|
||||
|
||||
public:
|
||||
|
||||
SoftSPI() {
|
||||
// NOT CALLED!
|
||||
// CALL INIT() manually!
|
||||
}
|
||||
|
||||
void init() const {
|
||||
GPIO12_INPUT_SET; GPIO12_INPUT_PULLUP_UNSET; // D6: MISO
|
||||
GPIO13_OUTPUT_SET; // D7: MOSI
|
||||
GPIO14_OUTPUT_SET; // D5: CLK
|
||||
GPIO15_OUTPUT_SET; // D8: CS
|
||||
}
|
||||
|
||||
inline void chipSelect() const {
|
||||
clkLo();
|
||||
wait();
|
||||
csLo();
|
||||
waitLong();
|
||||
}
|
||||
|
||||
inline void chipDeselect() const {
|
||||
clkLo();
|
||||
wait();
|
||||
csHi();
|
||||
wait();
|
||||
|
||||
}
|
||||
|
||||
inline void csLo() const { GPIO15_L; } // D8
|
||||
inline void csHi() const { GPIO15_H; } // D8
|
||||
|
||||
inline void clkHi() const { GPIO14_H; } // D5
|
||||
inline void clkLo() const { GPIO14_L; } // D5
|
||||
|
||||
inline bool getMISO() const {return GPIO12_IN;} // D6
|
||||
|
||||
|
||||
/** read 16 bits */
|
||||
inline uint16_t readWord() const {
|
||||
return
|
||||
(readBit() << 15) |
|
||||
(readBit() << 14) |
|
||||
(readBit() << 13) |
|
||||
(readBit() << 12) |
|
||||
(readBit() << 11) |
|
||||
(readBit() << 10) |
|
||||
(readBit() << 9) |
|
||||
(readBit() << 8) |
|
||||
(readBit() << 7) |
|
||||
(readBit() << 6) |
|
||||
(readBit() << 5) |
|
||||
(readBit() << 4) |
|
||||
(readBit() << 3) |
|
||||
(readBit() << 2) |
|
||||
(readBit() << 1) |
|
||||
(readBit() << 0);
|
||||
}
|
||||
|
||||
/** read 8 bits */
|
||||
inline uint16_t readByte() const {
|
||||
return
|
||||
(readBit() << 7) |
|
||||
(readBit() << 6) |
|
||||
(readBit() << 5) |
|
||||
(readBit() << 4) |
|
||||
(readBit() << 3) |
|
||||
(readBit() << 2) |
|
||||
(readBit() << 1) |
|
||||
(readBit() << 0);
|
||||
}
|
||||
|
||||
inline void writeWord(const uint16_t word) const {
|
||||
writeBit(word & BIT(15));
|
||||
writeBit(word & BIT(14));
|
||||
writeBit(word & BIT(13));
|
||||
writeBit(word & BIT(12));
|
||||
writeBit(word & BIT(11));
|
||||
writeBit(word & BIT(10));
|
||||
writeBit(word & BIT( 9));
|
||||
writeBit(word & BIT( 8));
|
||||
writeBit(word & BIT( 7));
|
||||
writeBit(word & BIT( 6));
|
||||
writeBit(word & BIT( 5));
|
||||
writeBit(word & BIT( 4));
|
||||
writeBit(word & BIT( 3));
|
||||
writeBit(word & BIT( 2));
|
||||
writeBit(word & BIT( 1));
|
||||
writeBit(word & BIT( 0));
|
||||
}
|
||||
|
||||
inline void writeByte(const uint8_t byte) const {
|
||||
writeBit(byte & BIT( 7));
|
||||
writeBit(byte & BIT( 6));
|
||||
writeBit(byte & BIT( 5));
|
||||
writeBit(byte & BIT( 4));
|
||||
writeBit(byte & BIT( 3));
|
||||
writeBit(byte & BIT( 2));
|
||||
writeBit(byte & BIT( 1));
|
||||
writeBit(byte & BIT( 0));
|
||||
}
|
||||
|
||||
inline uint16 readWriteWord(const uint16_t word) const {
|
||||
return
|
||||
(readWriteBit(word & BIT(15)) << 15) |
|
||||
(readWriteBit(word & BIT(14)) << 14) |
|
||||
(readWriteBit(word & BIT(13)) << 13) |
|
||||
(readWriteBit(word & BIT(12)) << 12) |
|
||||
(readWriteBit(word & BIT(11)) << 11) |
|
||||
(readWriteBit(word & BIT(10)) << 10) |
|
||||
(readWriteBit(word & BIT( 9)) << 9) |
|
||||
(readWriteBit(word & BIT( 8)) << 8) |
|
||||
(readWriteBit(word & BIT( 7)) << 7) |
|
||||
(readWriteBit(word & BIT( 6)) << 6) |
|
||||
(readWriteBit(word & BIT( 5)) << 5) |
|
||||
(readWriteBit(word & BIT( 4)) << 4) |
|
||||
(readWriteBit(word & BIT( 3)) << 3) |
|
||||
(readWriteBit(word & BIT( 2)) << 2) |
|
||||
(readWriteBit(word & BIT( 1)) << 1) |
|
||||
(readWriteBit(word & BIT( 0)) << 0);
|
||||
}
|
||||
|
||||
inline uint8 readWriteByte(const uint8_t byte) const {
|
||||
return
|
||||
(readWriteBit(byte & BIT( 7)) << 7) |
|
||||
(readWriteBit(byte & BIT( 6)) << 6) |
|
||||
(readWriteBit(byte & BIT( 5)) << 5) |
|
||||
(readWriteBit(byte & BIT( 4)) << 4) |
|
||||
(readWriteBit(byte & BIT( 3)) << 3) |
|
||||
(readWriteBit(byte & BIT( 2)) << 2) |
|
||||
(readWriteBit(byte & BIT( 1)) << 1) |
|
||||
(readWriteBit(byte & BIT( 0)) << 0);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
||||
inline uint8_t readWriteBit(const bool out) const {
|
||||
if(out) {GPIO13_H;} else {GPIO13_L;}
|
||||
wait();
|
||||
clkHi();
|
||||
wait();
|
||||
const bool inp = getMISO();
|
||||
wait();
|
||||
clkLo();
|
||||
return (inp) ? 1 : 0;
|
||||
}
|
||||
|
||||
/** write one bit to the bus */
|
||||
inline void writeBit(const bool out) const {
|
||||
if(out) {GPIO13_H;} else {GPIO13_L;}
|
||||
wait();
|
||||
clkHi();
|
||||
wait();
|
||||
clkLo();
|
||||
}
|
||||
|
||||
/** read one bit from the bus */
|
||||
inline uint8_t readBit() const {
|
||||
clkHi();
|
||||
wait();
|
||||
const bool val = getMISO();
|
||||
wait();
|
||||
clkLo();
|
||||
wait();
|
||||
return (val) ? 1 : 0;
|
||||
}
|
||||
|
||||
inline void wait() const {
|
||||
__asm__ __volatile__("nop");
|
||||
}
|
||||
|
||||
inline void waitLong() const {
|
||||
os_delay_us(1);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // SOFTSPI_H
|
||||
Reference in New Issue
Block a user