Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

esp8266-rx #71

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/esp8266-rx/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# esp8266-rx

WIP in progress

![IMG_0619](https://user-images.githubusercontent.com/1991296/180480260-16da8904-dceb-40fe-8bfd-70be6097b931.jpg)
248 changes: 248 additions & 0 deletions examples/esp8266-rx/esp8266-rx.ino
Original file line number Diff line number Diff line change
@@ -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();
}
}