Skip to content

Multiple fixes for core_esp8266_i2s #2590

Closed
@gadgetmind

Description

@gadgetmind

Hardware

Hardware: Wemos D1 Mini
Core Version: 2.3.0

Description

I'm very new to ESP8266 and I've been playing with I2S output for a week or so with this project as a starting point.

https://github.com/bbx10/SFX-I2S-web-trigger

Initially it was all rather delicate with frequent crashes and audio corruption but I've now got it solid. I'd generate a pull request but TBH I'm no expert (understatement!) with these and would prefer it if others could check my changes and see how best to incorporate them.

Issues:

  1. Memory leak.
    i2s_slc_begin allocates an array of buffers but i2s_slc_end didn't free them.
    I've added -
    for (x=0; x<SLC_BUF_CNT; x++) free(i2s_slc_buf_pntr[x]);
    to the end of i2s_slc_end.

  2. Crashes due to ISR not being in RAM.
    i2s_slc_isr and i2s_slc_queue_next_item both need the ICACHE_RAM_ATTR attribute. Adding these fixed the instability. I think the ETS_SLC_INTR_DISABLE(); (calls ets_isr_unmask) is OK but this is all new to me.

  3. Audio issues due to the way the clock is updated in i2s_set_rate.
    When changing the sample rate, the I2S output would get into a strange state with lots of audio distortion. Fixing this took hours of experimentation as I wasn't sure what they problem was. I finally found that holding the transmitter in reset during the update fixed it. So the "guts" of i2s_set_rate are now -
    // Need to hold transmitter in reset while changing clock bits as it can otherwise get into a weird state.
    // We also need to use a temporary variable to ensure we don't do a lot of read/modify/write operations.
    uint32_t i2sc_temp = I2SC;
    i2sc_temp |= (I2STXR); // Hold transmitter in reset
    I2SC = i2sc_temp;
    // Clear out and then rewrite clock bits
    i2sc_temp &= ~((I2SBDM << I2SBD) | (I2SCDM << I2SCD));
    i2sc_temp |= ((i2s_bck_div-1) << I2SBD) | ((i2s_clock_div-1) << I2SCD) ;
    I2SC = i2sc_temp;
    i2sc_temp &= ~(I2STXR); // Release reset
    I2SC = i2sc_temp;

Setting I2STXR at the same time as writing the new clock bits didn't work for me and the above sequence is the shortest that I found that's (so far!) 100% reliable.

Note that some of the bits that were configured here now aren't. I'm considering letting the user set these without editing this code so I've moved them to near the end of I2s_begin just before the call to i2s_set_rate.

// This initialisation moved here rather than in i2s_set_rate
//!trans master, !bits mod, rece slave mod, rece msb shift, right first, msb right
I2SC &= ~(I2STSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD));
I2SC |= I2SRF | I2SMR | I2SRSM | I2SRMS;

I'm tempted to use a temporary here too as it will be in register and so more efficient than the read-modify-write of a volatile, but it's working for me now so I'm disinclined to touch it!

I think making changes (1) and (2) should be uncontentious but someone might want to play samples at different rates to check (3). Note that the web trigger code calls i2s_begin and i2s_end for each sample so sees the memory leak pretty quickly. I changed it to only call i2s_begin as startup and change the rate for each sample, so we seeing the audio corruption more often. With my fixes, both approaches should work just fine.

GM

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions