mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-19 23:22:16 +03:00
Add I2S class support (#7874)
Fixes #427 Adds a basic I2S class based off of the Arduino-SAMD core. The raw i2s_xxx functions are still a better way to use I2S due to their flexibility, but this will allow basic Arduino sketches to work.
This commit is contained in:
parent
9fc5afd5fd
commit
e99df4fe1a
@ -24,7 +24,7 @@
|
||||
#include "osapi.h"
|
||||
#include "ets_sys.h"
|
||||
#include "i2s_reg.h"
|
||||
#include "i2s.h"
|
||||
#include "core_esp8266_i2s.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
@ -194,11 +194,11 @@ static void ICACHE_RAM_ATTR i2s_slc_isr(void) {
|
||||
}
|
||||
|
||||
void i2s_set_callback(void (*callback) (void)) {
|
||||
tx->callback = callback;
|
||||
if (tx) tx->callback = callback;
|
||||
}
|
||||
|
||||
void i2s_rx_set_callback(void (*callback) (void)) {
|
||||
rx->callback = callback;
|
||||
if (rx) rx->callback = callback;
|
||||
}
|
||||
|
||||
static bool _alloc_channel(i2s_state_t *ch) {
|
||||
@ -343,7 +343,7 @@ bool i2s_write_lr(int16_t left, int16_t right){
|
||||
|
||||
// writes a buffer of frames into the DMA memory, returns the amount of frames written
|
||||
// A frame is just a int16_t for mono, for stereo a frame is two int16_t, one for each channel.
|
||||
static uint16_t _i2s_write_buffer(int16_t *frames, uint16_t frame_count, bool mono, bool nb) {
|
||||
static uint16_t _i2s_write_buffer(const int16_t *frames, uint16_t frame_count, bool mono, bool nb) {
|
||||
uint16_t frames_written=0;
|
||||
|
||||
while(frame_count>0) {
|
||||
@ -401,13 +401,13 @@ static uint16_t _i2s_write_buffer(int16_t *frames, uint16_t frame_count, bool mo
|
||||
return frames_written;
|
||||
}
|
||||
|
||||
uint16_t i2s_write_buffer_mono_nb(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, true); }
|
||||
uint16_t i2s_write_buffer_mono_nb(const int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, true); }
|
||||
|
||||
uint16_t i2s_write_buffer_mono(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, false); }
|
||||
uint16_t i2s_write_buffer_mono(const int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, false); }
|
||||
|
||||
uint16_t i2s_write_buffer_nb(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, true); }
|
||||
uint16_t i2s_write_buffer_nb(const int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, true); }
|
||||
|
||||
uint16_t i2s_write_buffer(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, false); }
|
||||
uint16_t i2s_write_buffer(const int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, false); }
|
||||
|
||||
bool i2s_read_sample(int16_t *left, int16_t *right, bool blocking) {
|
||||
if (!rx) {
|
||||
|
81
cores/esp8266/core_esp8266_i2s.h
Normal file
81
cores/esp8266/core_esp8266_i2s.h
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
i2s.h - Software I2S library for esp8266
|
||||
|
||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef I2S_h
|
||||
#define I2S_h
|
||||
|
||||
#define I2S_HAS_BEGIN_RXTX_DRIVE_CLOCKS 1
|
||||
|
||||
/*
|
||||
How does this work? Basically, to get sound, you need to:
|
||||
- Connect an I2S codec to the I2S pins on the ESP.
|
||||
- Start up a thread that's going to do the sound output
|
||||
- Call i2s_set_bits() if you want to enable 24-bit mode
|
||||
- Call i2s_begin()
|
||||
- Call i2s_set_rate() with the sample rate you want.
|
||||
- Generate sound and call i2s_write_sample() with 32-bit samples.
|
||||
The 32bit samples basically are 2 16-bit signed values (the analog values for
|
||||
the left and right channel) concatenated as (Rout<<16)+Lout
|
||||
|
||||
i2s_write_sample will block when you're sending data too quickly, so you can just
|
||||
generate and push data as fast as you can and i2s_write_sample will regulate the
|
||||
speed.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool i2s_set_bits(int bits); // Set bits per sample, only 16 or 24 supported. Call before begin.
|
||||
// Note that in 24 bit mode each sample must be left-aligned (i.e. 0x00000000 .. 0xffffff00) as the
|
||||
// hardware shifts starting at bit 31, not bit 23.
|
||||
|
||||
void i2s_begin(); // Enable TX only, for compatibility
|
||||
bool i2s_rxtx_begin(bool enableRx, bool enableTx); // Allow TX and/or RX, returns false on OOM error
|
||||
bool i2s_rxtxdrive_begin(bool enableRx, bool enableTx, bool driveRxClocks, bool driveTxClocks);
|
||||
void i2s_end();
|
||||
void i2s_set_rate(uint32_t rate);//Sample Rate in Hz (ex 44100, 48000)
|
||||
void i2s_set_dividers(uint8_t div1, uint8_t div2);//Direct control over output rate
|
||||
float i2s_get_real_rate();//The actual Sample Rate on output
|
||||
bool i2s_write_sample(uint32_t sample);//32bit sample with channels being upper and lower 16 bits (blocking when DMA is full)
|
||||
bool i2s_write_sample_nb(uint32_t sample);//same as above but does not block when DMA is full and returns false instead
|
||||
bool i2s_write_lr(int16_t left, int16_t right);//combines both channels and calls i2s_write_sample with the result
|
||||
bool i2s_read_sample(int16_t *left, int16_t *right, bool blocking); // RX data returned in both 16-bit outputs.
|
||||
bool i2s_is_full();//returns true if DMA is full and can not take more bytes (overflow)
|
||||
bool i2s_is_empty();//returns true if DMA is empty (underflow)
|
||||
bool i2s_rx_is_full();
|
||||
bool i2s_rx_is_empty();
|
||||
uint16_t i2s_available();// returns the number of samples than can be written before blocking
|
||||
uint16_t i2s_rx_available();// returns the number of samples than can be written before blocking
|
||||
void i2s_set_callback(void (*callback) (void));
|
||||
void i2s_rx_set_callback(void (*callback) (void));
|
||||
|
||||
// writes a buffer of frames into the DMA memory, returns the amount of frames written
|
||||
// A frame is just a int16_t for mono, for stereo a frame is two int16_t, one for each channel.
|
||||
uint16_t i2s_write_buffer_mono(const int16_t *frames, uint16_t frame_count);
|
||||
uint16_t i2s_write_buffer_mono_nb(const int16_t *frames, uint16_t frame_count);
|
||||
uint16_t i2s_write_buffer(const int16_t *frames, uint16_t frame_count);
|
||||
uint16_t i2s_write_buffer_nb(const int16_t *frames, uint16_t frame_count);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,81 +1,12 @@
|
||||
/*
|
||||
i2s.h - Software I2S library for esp8266
|
||||
// This include file is a hack to ensure backward compatibility with
|
||||
// pre 3.0.0 versions of the core. There was a *lowercase* "i2s.h"
|
||||
// header which was in this directory, now renamed to "core_esp82i66s.h"
|
||||
// But, the I2S class has a header, "I2S.h" in uppercase. On Linux
|
||||
// the two names are different, but on Windows it's case-insensitive
|
||||
// so the names conflict.
|
||||
//
|
||||
// Avoid the issue by preserving the old i2s.h file and have it redirect
|
||||
// to I2S.h which will give the ESP8266-specific functions as well as
|
||||
// the generic I2S class.
|
||||
|
||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef I2S_h
|
||||
#define I2S_h
|
||||
|
||||
#define I2S_HAS_BEGIN_RXTX_DRIVE_CLOCKS 1
|
||||
|
||||
/*
|
||||
How does this work? Basically, to get sound, you need to:
|
||||
- Connect an I2S codec to the I2S pins on the ESP.
|
||||
- Start up a thread that's going to do the sound output
|
||||
- Call i2s_set_bits() if you want to enable 24-bit mode
|
||||
- Call i2s_begin()
|
||||
- Call i2s_set_rate() with the sample rate you want.
|
||||
- Generate sound and call i2s_write_sample() with 32-bit samples.
|
||||
The 32bit samples basically are 2 16-bit signed values (the analog values for
|
||||
the left and right channel) concatenated as (Rout<<16)+Lout
|
||||
|
||||
i2s_write_sample will block when you're sending data too quickly, so you can just
|
||||
generate and push data as fast as you can and i2s_write_sample will regulate the
|
||||
speed.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool i2s_set_bits(int bits); // Set bits per sample, only 16 or 24 supported. Call before begin.
|
||||
// Note that in 24 bit mode each sample must be left-aligned (i.e. 0x00000000 .. 0xffffff00) as the
|
||||
// hardware shifts starting at bit 31, not bit 23.
|
||||
|
||||
void i2s_begin(); // Enable TX only, for compatibility
|
||||
bool i2s_rxtx_begin(bool enableRx, bool enableTx); // Allow TX and/or RX, returns false on OOM error
|
||||
bool i2s_rxtxdrive_begin(bool enableRx, bool enableTx, bool driveRxClocks, bool driveTxClocks);
|
||||
void i2s_end();
|
||||
void i2s_set_rate(uint32_t rate);//Sample Rate in Hz (ex 44100, 48000)
|
||||
void i2s_set_dividers(uint8_t div1, uint8_t div2);//Direct control over output rate
|
||||
float i2s_get_real_rate();//The actual Sample Rate on output
|
||||
bool i2s_write_sample(uint32_t sample);//32bit sample with channels being upper and lower 16 bits (blocking when DMA is full)
|
||||
bool i2s_write_sample_nb(uint32_t sample);//same as above but does not block when DMA is full and returns false instead
|
||||
bool i2s_write_lr(int16_t left, int16_t right);//combines both channels and calls i2s_write_sample with the result
|
||||
bool i2s_read_sample(int16_t *left, int16_t *right, bool blocking); // RX data returned in both 16-bit outputs.
|
||||
bool i2s_is_full();//returns true if DMA is full and can not take more bytes (overflow)
|
||||
bool i2s_is_empty();//returns true if DMA is empty (underflow)
|
||||
bool i2s_rx_is_full();
|
||||
bool i2s_rx_is_empty();
|
||||
uint16_t i2s_available();// returns the number of samples than can be written before blocking
|
||||
uint16_t i2s_rx_available();// returns the number of samples than can be written before blocking
|
||||
void i2s_set_callback(void (*callback) (void));
|
||||
void i2s_rx_set_callback(void (*callback) (void));
|
||||
|
||||
// writes a buffer of frames into the DMA memory, returns the amount of frames written
|
||||
// A frame is just a int16_t for mono, for stereo a frame is two int16_t, one for each channel.
|
||||
uint16_t i2s_write_buffer_mono(int16_t *frames, uint16_t frame_count);
|
||||
uint16_t i2s_write_buffer_mono_nb(int16_t *frames, uint16_t frame_count);
|
||||
uint16_t i2s_write_buffer(int16_t *frames, uint16_t frame_count);
|
||||
uint16_t i2s_write_buffer_nb(int16_t *frames, uint16_t frame_count);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#include "../../libraries/I2S/src/I2S.h"
|
||||
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
This example reads audio data from an Invensense's ICS43432 I2S microphone
|
||||
breakout board, and prints out the samples to the Serial console. The
|
||||
Serial Plotter built into the Arduino IDE can be used to plot the audio
|
||||
data (Tools -> Serial Plotter)
|
||||
created 17 November 2016
|
||||
by Sandeep Mistry
|
||||
*/
|
||||
|
||||
#include <I2S.h>
|
||||
|
||||
void setup() {
|
||||
// Open serial communications and wait for port to open:
|
||||
// A baud rate of 115200 is used instead of 9600 for a faster data rate
|
||||
// on non-native USB ports
|
||||
Serial.begin(115200);
|
||||
while (!Serial) {
|
||||
; // wait for serial port to connect. Needed for native USB port only
|
||||
}
|
||||
|
||||
// start I2S at 8 kHz with 24-bits per sample
|
||||
if (!I2S.begin(I2S_PHILIPS_MODE, 8000, 24)) {
|
||||
Serial.println("Failed to initialize I2S!");
|
||||
while (1); // do nothing
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// read a sample
|
||||
int sample = I2S.read();
|
||||
|
||||
if (sample) {
|
||||
// if it's non-zero print value to serial
|
||||
Serial.println(sample);
|
||||
}
|
||||
}
|
46
libraries/I2S/examples/SimpleTone/SimpleTone.ino
Normal file
46
libraries/I2S/examples/SimpleTone/SimpleTone.ino
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
This example generates a square wave based tone at a specified frequency
|
||||
and sample rate. Then outputs the data using the I2S interface to a
|
||||
MAX08357 I2S Amp Breakout board.
|
||||
|
||||
created 17 November 2016
|
||||
by Sandeep Mistry
|
||||
modified for ESP8266 by Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
*/
|
||||
|
||||
#include <I2S.h>
|
||||
|
||||
const int frequency = 440; // frequency of square wave in Hz
|
||||
const int amplitude = 500; // amplitude of square wave
|
||||
const int sampleRate = 8000; // sample rate in Hz
|
||||
|
||||
const int halfWavelength = (sampleRate / frequency); // half wavelength of square wave
|
||||
|
||||
short sample = amplitude; // current sample value
|
||||
int count = 0;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("I2S simple tone");
|
||||
|
||||
// start I2S at the sample rate with 16-bits per sample
|
||||
if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, 16)) {
|
||||
Serial.println("Failed to initialize I2S!");
|
||||
while (1); // do nothing
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (count % halfWavelength == 0) {
|
||||
// invert the sample every half wavelength count multiple to generate square wave
|
||||
sample = -1 * sample;
|
||||
}
|
||||
|
||||
// write the same sample twice, once for left and once for the right channel
|
||||
I2S.write(sample);
|
||||
I2S.write(sample);
|
||||
|
||||
// increment the counter for the next sample
|
||||
count++;
|
||||
}
|
||||
|
23
libraries/I2S/keywords.txt
Normal file
23
libraries/I2S/keywords.txt
Normal file
@ -0,0 +1,23 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map I2S
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
I2S KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
begin KEYWORD2
|
||||
end KEYWORD2
|
||||
|
||||
onReceive KEYWORD2
|
||||
onTransmit KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
I2S_PHILIPS_MODE LITERAL1
|
9
libraries/I2S/library.properties
Normal file
9
libraries/I2S/library.properties
Normal file
@ -0,0 +1,9 @@
|
||||
name=I2S
|
||||
version=1.0
|
||||
author=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Enables the communication with devices that use the Inter-IC Sound (I2S) Bus. Specific implementation for ESP8266, based off of SAMD.
|
||||
paragraph=
|
||||
category=Communication
|
||||
url=http://www.arduino.cc/en/Reference/I2S
|
||||
architectures=esp8266
|
211
libraries/I2S/src/I2S.cpp
Normal file
211
libraries/I2S/src/I2S.cpp
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
Based off of ArduinoCore-SAMD I2S interface. Modified for the
|
||||
ESP8266 by Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "I2S.h"
|
||||
|
||||
I2SClass::I2SClass(bool enableTransmit, bool enableRecv, bool driveClocks) {
|
||||
_enableTx = enableTransmit;
|
||||
_enableRx = enableRecv;
|
||||
_driveClk = driveClocks;
|
||||
_running = false;
|
||||
_onTransmit = nullptr;
|
||||
_onReceive = nullptr;
|
||||
_havePeeked = 0;
|
||||
_peekedData = 0;
|
||||
_bps = 0;
|
||||
_writtenHalf = false;
|
||||
}
|
||||
|
||||
int I2SClass::begin(i2s_mode_t mode, long sampleRate, int bitsPerSample) {
|
||||
if ( _running || (mode != I2S_PHILIPS_MODE) || ( (bitsPerSample != 16) && (bitsPerSample != 24) ) ) {
|
||||
return 0;
|
||||
}
|
||||
if (!i2s_rxtxdrive_begin(_enableRx, _enableTx, _driveClk, _driveClk)) {
|
||||
return 0;
|
||||
}
|
||||
i2s_set_rate(sampleRate);
|
||||
i2s_set_callback(_onTransmit);
|
||||
i2s_rx_set_callback(_onReceive);
|
||||
_bps = bitsPerSample;
|
||||
_running = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void I2SClass::end() {
|
||||
if (_running) {
|
||||
i2s_end();
|
||||
}
|
||||
i2s_set_callback(nullptr);
|
||||
i2s_rx_set_callback(nullptr);
|
||||
_running = false;
|
||||
}
|
||||
|
||||
void I2SClass::onTransmit(void(*fcn)(void)) {
|
||||
i2s_set_callback(fcn);
|
||||
_onTransmit = fcn;
|
||||
}
|
||||
|
||||
void I2SClass::onReceive(void(*fcn)(void)) {
|
||||
i2s_rx_set_callback(fcn);
|
||||
_onReceive = fcn;
|
||||
}
|
||||
|
||||
int I2SClass::available() {
|
||||
if (!_running) return 0;
|
||||
return i2s_rx_available();
|
||||
}
|
||||
|
||||
int I2SClass::availableForWrite() {
|
||||
if (!_running) return 0;
|
||||
return i2s_available();
|
||||
}
|
||||
|
||||
void I2SClass::flush() {
|
||||
/* No-op */
|
||||
}
|
||||
|
||||
int I2SClass::read() {
|
||||
if (!_running) return -1;
|
||||
// Always just read from the peeked value to simplify operation
|
||||
if (!_havePeeked) {
|
||||
peek();
|
||||
}
|
||||
if (_havePeeked) {
|
||||
if (_bps == 16) {
|
||||
_havePeeked--;
|
||||
int ret = _peekedData;
|
||||
_peekedData >>= 16;
|
||||
return ret;
|
||||
} else /* _bps == 24 */ {
|
||||
_havePeeked = 0;
|
||||
return _peekedData;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int I2SClass::peek() {
|
||||
if (!_running) return -1;
|
||||
if (_havePeeked) {
|
||||
if (_bps == 16) {
|
||||
int16_t sample = (int16_t)_peekedData; // Will extends sign on return
|
||||
return sample;
|
||||
} else {
|
||||
return _peekedData;
|
||||
}
|
||||
}
|
||||
int16_t l, r;
|
||||
i2s_read_sample(&l, &r, true);
|
||||
_peekedData = ((int)l << 16) | (0xffff & (int)r);
|
||||
_havePeeked = 2; // We now have 2 16-bit quantities which can also be used as 1 32-bit(24-bit)
|
||||
if (_bps == 16) {
|
||||
return r;
|
||||
} else {
|
||||
return _peekedData;
|
||||
}
|
||||
}
|
||||
|
||||
int I2SClass::read(void *buffer, size_t size) {
|
||||
if (!_running) return -1;
|
||||
int cnt = 0;
|
||||
|
||||
if ( ((_bps == 24) && (size % 4)) || ((_bps == 16) && (size % 2)) || (size < 2) ) {
|
||||
return 0; // Invalid, can only read in units of samples
|
||||
}
|
||||
// Make sure any peeked data is consumed first
|
||||
if (_havePeeked) {
|
||||
if (_bps == 16) {
|
||||
while (_havePeeked && size) {
|
||||
uint16_t *p = (uint16_t *)buffer;
|
||||
*(p++) = _peekedData;
|
||||
_peekedData >>= 16;
|
||||
_havePeeked--;
|
||||
buffer = (void *)p;
|
||||
size -= 2;
|
||||
cnt += 2;
|
||||
}
|
||||
} else {
|
||||
uint32_t *p = (uint32_t *)buffer;
|
||||
*(p++) = _peekedData;
|
||||
buffer = (void *)p;
|
||||
size -= 4;
|
||||
cnt += 4;
|
||||
}
|
||||
}
|
||||
// Now just non-blocking read up to the remaining size
|
||||
int16_t l, r;
|
||||
int16_t *p = (int16_t *)buffer;
|
||||
while (size && i2s_read_sample(&l, &r, false)) {
|
||||
*(p++) = l;
|
||||
size--;
|
||||
cnt++;
|
||||
if (size) {
|
||||
*(p++) = r;
|
||||
size--;
|
||||
cnt++;
|
||||
} else {
|
||||
// We read a simple we can't return, stuff it in the peeked data
|
||||
_havePeeked = 1;
|
||||
_peekedData = r;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
size_t I2SClass::write(uint8_t s) {
|
||||
if (!_running) return 0;
|
||||
return write((int32_t)s);
|
||||
}
|
||||
|
||||
size_t I2SClass::write(const uint8_t *buffer, size_t size) {
|
||||
return write((const void *)buffer, size);
|
||||
}
|
||||
|
||||
size_t I2SClass::write(int32_t s) {
|
||||
if (!_running) return 0;
|
||||
// Because our HW really wants 32b writes, store any 16b writes until another
|
||||
// 16b write comes in and then send the combined write down.
|
||||
if (_bps == 16) {
|
||||
if (_writtenHalf) {
|
||||
_writtenData <<= 16;
|
||||
_writtenData |= 0xffff & s;
|
||||
_writtenHalf = false;
|
||||
return i2s_write_sample(_writtenData) ? 1 : 0;
|
||||
} else {
|
||||
_writtenHalf = true;
|
||||
_writtenData = s & 0xffff;
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
return i2s_write_sample((uint32_t)s) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
// SAMD core has this as non-blocking
|
||||
size_t I2SClass::write(const void *buffer, size_t size) {
|
||||
if (!_running) return 0;
|
||||
return i2s_write_buffer_nb((int16_t *)buffer, size / 2);
|
||||
}
|
||||
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_I2S)
|
||||
I2SClass I2S;
|
||||
#endif
|
||||
|
95
libraries/I2S/src/I2S.h
Normal file
95
libraries/I2S/src/I2S.h
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
Based off of ArduinoCore-SAMD I2S interface. Modified for the
|
||||
ESP8266 by Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _I2S_H_INCLUDED
|
||||
#define _I2S_H_INCLUDED
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <core_esp8266_i2s.h>
|
||||
|
||||
typedef enum {
|
||||
I2S_PHILIPS_MODE // Only mode allowed for now by the core
|
||||
} i2s_mode_t;
|
||||
|
||||
class I2SClass : public Stream
|
||||
{
|
||||
public:
|
||||
// By default only transmit and drive the clock pins
|
||||
I2SClass(bool enableTransmit = true, bool enableRecv = false,
|
||||
bool driveClocks = true);
|
||||
|
||||
// Only 16 and 24 bitsPerSample are allowed by the hardware
|
||||
// 24-bit is MSB-aligned, with 0x00 in the lowest byte of each element.
|
||||
int begin(i2s_mode_t mode, long sampleRate, int bitsPerSample);
|
||||
void end();
|
||||
|
||||
// from Stream
|
||||
virtual int available();
|
||||
virtual int read(); // Blocking, will wait for incoming data
|
||||
virtual int peek(); // Blocking, will wait for incoming data
|
||||
virtual void flush();
|
||||
|
||||
// from Print (see notes on write() methods below)
|
||||
virtual size_t write(uint8_t);
|
||||
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||
virtual int availableForWrite();
|
||||
|
||||
// Read up to size samples from the I2S device. Non-blocking, will read
|
||||
// from 0...size samples and return the count read. Be sure your app handles
|
||||
// the partial read case (i.e. yield()ing and trying to read more).
|
||||
int read(void* buffer, size_t size);
|
||||
|
||||
// Write a single sample to the I2S device. Blocking until write succeeds
|
||||
size_t write(int32_t);
|
||||
// Write up to size samples to the I2S device. Non-blocking, will write
|
||||
// from 0...size samples and return that count. Be sure your app handles
|
||||
// partial writes (i.e. by yield()ing and then retrying to write the
|
||||
// remaining data.
|
||||
size_t write(const void *buffer, size_t size);
|
||||
|
||||
// Note that these callback are called from **INTERRUPT CONTEXT** and hence
|
||||
// must be both stored in IRAM and not perform anything that's not legal in
|
||||
// an interrupt
|
||||
void onTransmit(void(*)(void));
|
||||
void onReceive(void(*)(void));
|
||||
|
||||
private:
|
||||
int _bps;
|
||||
bool _running;
|
||||
bool _enableTx;
|
||||
bool _enableRx;
|
||||
bool _driveClk;
|
||||
void (*_onTransmit)(void);
|
||||
void (*_onReceive)(void);
|
||||
// Support for peek() on read path
|
||||
uint32_t _peekedData;
|
||||
int _havePeeked;
|
||||
// Support for ::write(x) on 16b wuantities
|
||||
uint32_t _writtenData;
|
||||
bool _writtenHalf;
|
||||
};
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_I2S)
|
||||
extern I2SClass I2S;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user