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 "osapi.h"
|
||||||
#include "ets_sys.h"
|
#include "ets_sys.h"
|
||||||
#include "i2s_reg.h"
|
#include "i2s_reg.h"
|
||||||
#include "i2s.h"
|
#include "core_esp8266_i2s.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
@ -194,11 +194,11 @@ static void ICACHE_RAM_ATTR i2s_slc_isr(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void i2s_set_callback(void (*callback) (void)) {
|
void i2s_set_callback(void (*callback) (void)) {
|
||||||
tx->callback = callback;
|
if (tx) tx->callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void i2s_rx_set_callback(void (*callback) (void)) {
|
void i2s_rx_set_callback(void (*callback) (void)) {
|
||||||
rx->callback = callback;
|
if (rx) rx->callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _alloc_channel(i2s_state_t *ch) {
|
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
|
// 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.
|
// 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;
|
uint16_t frames_written=0;
|
||||||
|
|
||||||
while(frame_count>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;
|
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) {
|
bool i2s_read_sample(int16_t *left, int16_t *right, bool blocking) {
|
||||||
if (!rx) {
|
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 @@
|
|||||||
/*
|
// This include file is a hack to ensure backward compatibility with
|
||||||
i2s.h - Software I2S library for esp8266
|
// 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.
|
#include "../../libraries/I2S/src/I2S.h"
|
||||||
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
|
|
||||||
|
@ -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