mirror of
https://github.com/ggerganov/ggwave.git
synced 2026-02-06 16:47:59 +08:00
esp8266-rx : initial audio input working
This commit is contained in:
248
examples/esp8266-rx/esp8266-rx.ino
Normal file
248
examples/esp8266-rx/esp8266-rx.ino
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
basic sound input
|
||||
ref: https://github.com/joextodd/listener
|
||||
*/
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
|
||||
extern "C" {
|
||||
#include "user_interface.h"
|
||||
#include "i2s_reg.h"
|
||||
#include "slc_register.h"
|
||||
#include "esp8266_peri.h"
|
||||
void rom_i2c_writeReg_Mask(int, int, int, int, int, int);
|
||||
}
|
||||
|
||||
// #define DEBUG
|
||||
|
||||
#define I2S_CLK_FREQ 160000000 // Hz
|
||||
#define I2S_24BIT 3 // I2S 24 bit half data
|
||||
#define I2S_LEFT 2 // I2S RX Left channel
|
||||
|
||||
#define I2SI_DATA 12 // I2S data on GPIO12
|
||||
#define I2SI_BCK 13 // I2S clk on GPIO13
|
||||
#define I2SI_WS 14 // I2S select on GPIO14
|
||||
|
||||
#define SLC_BUF_CNT 8 // Number of buffers in the I2S circular buffer
|
||||
#define SLC_BUF_LEN 64 // Length of one buffer, in 32-bit words.
|
||||
|
||||
/**
|
||||
* Convert I2S data.
|
||||
* Data is 18 bit signed, MSBit first, two's complement.
|
||||
* Note: We can only send 31 cycles from ESP8266 so we only
|
||||
* shift by 13 instead of 14.
|
||||
* The 240200 is a magic calibration number I haven't figured
|
||||
* out yet.
|
||||
*/
|
||||
#define convert(sample) (((int32_t)(sample) >> 13) - 240200)
|
||||
|
||||
typedef struct {
|
||||
uint32_t blocksize : 12;
|
||||
uint32_t datalen : 12;
|
||||
uint32_t unused : 5;
|
||||
uint32_t sub_sof : 1;
|
||||
uint32_t eof : 1;
|
||||
volatile uint32_t owner : 1;
|
||||
|
||||
uint32_t *buf_ptr;
|
||||
uint32_t *next_link_ptr;
|
||||
} sdio_queue_t;
|
||||
|
||||
static sdio_queue_t i2s_slc_items[SLC_BUF_CNT]; // I2S DMA buffer descriptors
|
||||
static uint32_t *i2s_slc_buf_pntr[SLC_BUF_CNT]; // Pointer to the I2S DMA buffer data
|
||||
static volatile uint32_t rx_buf_cnt = 0;
|
||||
static volatile uint32_t rx_buf_idx = 0;
|
||||
static volatile bool rx_buf_flag = false;
|
||||
|
||||
void i2s_init();
|
||||
void slc_init();
|
||||
void i2s_set_rate(uint32_t rate);
|
||||
void slc_isr(void *para);
|
||||
|
||||
/* Main -----------------------------------------------------------------------*/
|
||||
void
|
||||
setup()
|
||||
{
|
||||
rx_buf_cnt = 0;
|
||||
|
||||
pinMode(I2SI_WS, OUTPUT);
|
||||
pinMode(I2SI_BCK, OUTPUT);
|
||||
pinMode(I2SI_DATA, INPUT);
|
||||
|
||||
WiFi.forceSleepBegin();
|
||||
delay(500);
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
slc_init();
|
||||
i2s_init();
|
||||
}
|
||||
|
||||
void
|
||||
loop()
|
||||
{
|
||||
int32_t value;
|
||||
char withScale[256];
|
||||
|
||||
if (rx_buf_flag) {
|
||||
for (int x = 0; x < SLC_BUF_LEN; x++) {
|
||||
if (i2s_slc_buf_pntr[rx_buf_idx][x] > 0) {
|
||||
#ifdef DEBUG
|
||||
Serial.print(i2s_slc_buf_pntr[rx_buf_idx][x], BIN);
|
||||
Serial.println("");
|
||||
#else
|
||||
value = convert(i2s_slc_buf_pntr[rx_buf_idx][x]);
|
||||
sprintf(withScale, "-1 %f 1", (float)value / 4096.0f);
|
||||
Serial.println(withScale);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
rx_buf_flag = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Function definitions -------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Initialise I2S as a RX master.
|
||||
*/
|
||||
void
|
||||
i2s_init()
|
||||
{
|
||||
// Config RX pin function
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_I2SI_DATA);
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_I2SI_BCK);
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, FUNC_I2SI_WS);
|
||||
|
||||
// Enable a 160MHz clock
|
||||
I2S_CLK_ENABLE();
|
||||
|
||||
// Reset I2S
|
||||
I2SC &= ~(I2SRST);
|
||||
I2SC |= I2SRST;
|
||||
I2SC &= ~(I2SRST);
|
||||
|
||||
// Reset DMA
|
||||
I2SFC &= ~(I2SDE | (I2SRXFMM << I2SRXFM));
|
||||
|
||||
// Enable DMA
|
||||
I2SFC |= I2SDE | (I2S_24BIT << I2SRXFM);
|
||||
|
||||
// Set RX single channel (left)
|
||||
I2SCC &= ~((I2STXCMM << I2STXCM) | (I2SRXCMM << I2SRXCM));
|
||||
I2SCC |= (I2S_LEFT << I2SRXCM);
|
||||
i2s_set_rate(16667);
|
||||
|
||||
// Set RX data to be received
|
||||
I2SRXEN = SLC_BUF_LEN;
|
||||
|
||||
// Bits mode
|
||||
I2SC |= (15 << I2SBM);
|
||||
|
||||
// Start receiver
|
||||
I2SC |= I2SRXS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set I2S clock.
|
||||
* I2S bits mode only has space for 15 extra bits,
|
||||
* 31 in total. The
|
||||
*/
|
||||
void
|
||||
i2s_set_rate(uint32_t rate)
|
||||
{
|
||||
uint32_t i2s_clock_div = (I2S_CLK_FREQ / (rate * 31 * 2)) & I2SCDM;
|
||||
uint32_t i2s_bck_div = (I2S_CLK_FREQ / (rate * i2s_clock_div * 31 * 2)) & I2SBDM;
|
||||
|
||||
#ifdef DEBUG
|
||||
Serial.printf("Rate %u Div %u Bck %u Freq %u\n",
|
||||
rate, i2s_clock_div, i2s_bck_div, I2S_CLK_FREQ / (i2s_clock_div * i2s_bck_div * 31 * 2));
|
||||
#endif
|
||||
|
||||
// RX master mode, RX MSB shift, right first, msb right
|
||||
I2SC &= ~(I2STSM | I2SRSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD));
|
||||
I2SC |= I2SRF | I2SMR | I2SRMS | (i2s_bck_div << I2SBD) | (i2s_clock_div << I2SCD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the SLC module for DMA operation.
|
||||
* Counter intuitively, we use the TXLINK here to
|
||||
* receive data.
|
||||
*/
|
||||
void
|
||||
slc_init()
|
||||
{
|
||||
for (int x = 0; x < SLC_BUF_CNT; x++) {
|
||||
i2s_slc_buf_pntr[x] = (uint32_t *)malloc(SLC_BUF_LEN * 4);
|
||||
for (int y = 0; y < SLC_BUF_LEN; y++) i2s_slc_buf_pntr[x][y] = 0;
|
||||
|
||||
i2s_slc_items[x].unused = 0;
|
||||
i2s_slc_items[x].owner = 1;
|
||||
i2s_slc_items[x].eof = 0;
|
||||
i2s_slc_items[x].sub_sof = 0;
|
||||
i2s_slc_items[x].datalen = SLC_BUF_LEN * 4;
|
||||
i2s_slc_items[x].blocksize = SLC_BUF_LEN * 4;
|
||||
i2s_slc_items[x].buf_ptr = (uint32_t *)&i2s_slc_buf_pntr[x][0];
|
||||
i2s_slc_items[x].next_link_ptr = (uint32_t *)((x < (SLC_BUF_CNT - 1)) ? (&i2s_slc_items[x + 1]) : (&i2s_slc_items[0]));
|
||||
}
|
||||
|
||||
// Reset DMA
|
||||
ETS_SLC_INTR_DISABLE();
|
||||
SLCC0 |= SLCRXLR | SLCTXLR;
|
||||
SLCC0 &= ~(SLCRXLR | SLCTXLR);
|
||||
SLCIC = 0xFFFFFFFF;
|
||||
|
||||
// Configure DMA
|
||||
SLCC0 &= ~(SLCMM << SLCM); // Clear DMA MODE
|
||||
SLCC0 |= (1 << SLCM); // Set DMA MODE to 1
|
||||
SLCRXDC |= SLCBINR | SLCBTNR; // Enable INFOR_NO_REPLACE and TOKEN_NO_REPLACE
|
||||
|
||||
// Feed DMA the 1st buffer desc addr
|
||||
SLCTXL &= ~(SLCTXLAM << SLCTXLA);
|
||||
SLCTXL |= (uint32_t)&i2s_slc_items[0] << SLCTXLA;
|
||||
|
||||
ETS_SLC_INTR_ATTACH(slc_isr, NULL);
|
||||
|
||||
// Enable EOF interrupt
|
||||
SLCIE = SLCITXEOF;
|
||||
ETS_SLC_INTR_ENABLE();
|
||||
|
||||
// Start transmission
|
||||
SLCTXL |= SLCTXLS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when SLC has finished writing
|
||||
* to one of the buffers.
|
||||
*/
|
||||
void ICACHE_RAM_ATTR
|
||||
slc_isr(void *para)
|
||||
{
|
||||
uint32_t status;
|
||||
|
||||
status = SLCIS;
|
||||
SLCIC = 0xFFFFFFFF;
|
||||
|
||||
if (status == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (status & SLCITXEOF) {
|
||||
// We have received a frame
|
||||
ETS_SLC_INTR_DISABLE();
|
||||
sdio_queue_t *finished = (sdio_queue_t*)SLCTXEDA;
|
||||
|
||||
finished->eof = 0;
|
||||
finished->owner = 1;
|
||||
finished->datalen = 0;
|
||||
|
||||
for (int i = 0; i < SLC_BUF_CNT; i++) {
|
||||
if (finished == &i2s_slc_items[i]) {
|
||||
rx_buf_idx = i;
|
||||
}
|
||||
}
|
||||
rx_buf_cnt++;
|
||||
rx_buf_flag = true;
|
||||
ETS_SLC_INTR_ENABLE();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user