added data structures

added audio support
added rfid support
added spi support
This commit is contained in:
2017-03-12 13:02:06 +01:00
parent cea1d5d164
commit d028b79325
12 changed files with 1373 additions and 0 deletions

124
ext/audio/Audio.h Normal file
View 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
View 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
View 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
View 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
View 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