Esp32 i2s parallel uses a single back buffer (#793)

This commit is contained in:
Michael Miller
2024-04-04 09:30:29 -07:00
committed by GitHub
parent 539433a433
commit a56929531e
2 changed files with 229 additions and 58 deletions

View File

@@ -85,6 +85,16 @@ esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t sample_rate, bool parallel_
#define I2S_DMA_SILENCE_BLOCK_COUNT_FRONT 2 // two front
#define I2S_DMA_SILENCE_BLOCK_COUNT_BACK 1 // one back, required for non parallel
// compatibility shim between versions of the IDF
// note that I2S_NUM_MAX is an enum element, so we check for
// existence of the new SOC_I2S_NUM
//
#if defined(SOC_I2S_NUM)
#define NEO_I2S_COUNT (SOC_I2S_NUM)
#else
#define NEO_I2S_COUNT (I2S_NUM_MAX)
#endif
typedef struct
{
i2s_dev_t* bus;
@@ -106,14 +116,14 @@ typedef struct
#define I2s_Is_Sending 2
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
// (SOC_I2S_NUM == 2)
static i2s_bus_t I2S[SOC_I2S_NUM] =
// (NEO_I2S_COUNT == 2)
static i2s_bus_t I2S[NEO_I2S_COUNT] =
{
{&I2S0, -1, -1, -1, -1, NULL, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, I2s_Is_Idle},
{&I2S1, -1, -1, -1, -1, NULL, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, I2s_Is_Idle}
};
#else
static i2s_bus_t I2S[SOC_I2S_NUM] =
static i2s_bus_t I2S[NEO_I2S_COUNT] =
{
{&I2S0, -1, -1, -1, -1, NULL, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, I2s_Is_Idle}
};
@@ -135,7 +145,7 @@ inline void dmaItemInit(lldesc_t* item, uint8_t* posData, size_t sizeData, lldes
bool i2sInitDmaItems(uint8_t bus_num, uint8_t* data, size_t dataSize, bool parallel_mode, size_t bytes_per_sample)
{
if (bus_num >= SOC_I2S_NUM)
if (bus_num >= NEO_I2S_COUNT)
{
return false;
}
@@ -206,7 +216,7 @@ bool i2sInitDmaItems(uint8_t bus_num, uint8_t* data, size_t dataSize, bool paral
bool i2sDeinitDmaItems(uint8_t bus_num)
{
if (bus_num >= SOC_I2S_NUM)
if (bus_num >= NEO_I2S_COUNT)
{
return false;
}
@@ -224,7 +234,7 @@ esp_err_t i2sSetClock(uint8_t bus_num,
uint8_t bck,
uint8_t bits)
{
if (bus_num >= SOC_I2S_NUM || div_a > 63 || div_b > 63 || bck > 63)
if (bus_num >= NEO_I2S_COUNT || div_a > 63 || div_b > 63 || bck > 63)
{
return ESP_FAIL;
}
@@ -273,7 +283,7 @@ void i2sSetPins(uint8_t bus_num,
int8_t busSampleSize,
bool invert)
{
if (bus_num >= SOC_I2S_NUM)
if (bus_num >= NEO_I2S_COUNT)
{
return;
}
@@ -361,7 +371,7 @@ void i2sSetClkWsPins(uint8_t bus_num,
bool invertWs)
{
if (bus_num >= SOC_I2S_NUM)
if (bus_num >= NEO_I2S_COUNT)
{
return;
}
@@ -392,7 +402,7 @@ void i2sSetClkWsPins(uint8_t bus_num,
bool i2sWriteDone(uint8_t bus_num)
{
if (bus_num >= SOC_I2S_NUM)
if (bus_num >= NEO_I2S_COUNT)
{
return false;
}
@@ -410,7 +420,7 @@ void i2sInit(uint8_t bus_num,
uint8_t* data,
size_t dataSize)
{
if (bus_num >= SOC_I2S_NUM)
if (bus_num >= NEO_I2S_COUNT)
{
return;
}
@@ -425,7 +435,7 @@ void i2sInit(uint8_t bus_num,
}
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
// (SOC_I2S_NUM == 2)
// (NEO_I2S_COUNT == 2)
if (bus_num)
{
periph_module_enable(PERIPH_I2S1_MODULE);
@@ -550,7 +560,7 @@ void i2sInit(uint8_t bus_num,
int i2sIntSource;
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
// (SOC_I2S_NUM == 2)
// (NEO_I2S_COUNT == 2)
if (bus_num == 1)
{
i2sIntSource = ETS_I2S1_INTR_SOURCE;
@@ -712,7 +722,7 @@ esp_err_t i2sSetSampleRate(uint8_t bus_num,
bool parallel_mode,
size_t bytes_per_sample)
{
if (bus_num >= SOC_I2S_NUM)
if (bus_num >= NEO_I2S_COUNT)
{
return ESP_FAIL;
}
@@ -789,7 +799,7 @@ void IRAM_ATTR i2sDmaISR(void* arg)
bool i2sWrite(uint8_t bus_num)
{
if (bus_num >= SOC_I2S_NUM)
if (bus_num >= NEO_I2S_COUNT)
{
return false;
}
@@ -918,7 +928,7 @@ void DumpI2s_fifo_conf(const char* label, i2s_dev_t* bus)
bool i2sDump(uint8_t bus_num)
{
if (bus_num >= SOC_I2S_NUM)
if (bus_num >= NEO_I2S_COUNT)
{
return false;
}
@@ -1014,7 +1024,7 @@ bool i2sGetClks(uint8_t bus_num,
uint8_t* clkm_div_b,
uint8_t* clkm_div_a)
{
if (bus_num >= SOC_I2S_NUM)
if (bus_num >= NEO_I2S_COUNT)
{
return false;
}

View File

@@ -307,6 +307,140 @@ public:
}
};
//
// Implementation of a Single Buffered version of a I2sContext
// Manages the underlying I2S details including the buffer
// This creates only a actively sending back buffer,
// Note that the back buffer must be DMA memory, a limited resource
//
// T_MUXMAP - NeoEspI2sMuxMap - tracking class for mux state
//
template<typename T_MUXMAP>
class NeoEspI2sMonoBuffContext
{
public:
const static size_t DmaBitsPerPixelBit = 4;
size_t I2sBufferSize; // total size of I2sBuffer
uint8_t* I2sBuffer; // holds the DMA buffer that is referenced by I2sBufDesc
T_MUXMAP MuxMap;
// as a static instance, all members get initialized to zero
// and the constructor is called at inconsistent time to other globals
// so its not useful to have or rely on,
// but without it presence they get zeroed far too late
NeoEspI2sMonoBuffContext()
//:
//I2sBufferSize(0),
//I2sBuffer(nullptr),
//I2sEditBuffer(nullptr),
//MuxMap()
{
}
void Construct(const uint8_t busNumber, uint32_t i2sSampleRate)
{
// construct only once on first time called
if (I2sBuffer == nullptr)
{
// MuxMap.MaxBusDataSize = max size in bytes of a single channel
// DmaBitsPerPixelBit = how many dma bits/byte are needed for each source (pixel) bit/byte
// T_MUXMAP::MuxBusDataSize = the true size of data for selected mux mode (not exposed size as i2s0 only supports 16bit mode)
I2sBufferSize = MuxMap.MaxBusDataSize * 8 * DmaBitsPerPixelBit * T_MUXMAP::MuxBusDataSize;
// must have a 4 byte aligned buffer for i2s
uint32_t alignment = I2sBufferSize % 4;
if (alignment)
{
I2sBufferSize += 4 - alignment;
}
size_t dmaBlockCount = (I2sBufferSize + I2S_DMA_MAX_DATA_LEN - 1) / I2S_DMA_MAX_DATA_LEN;
I2sBuffer = static_cast<uint8_t*>(heap_caps_malloc(I2sBufferSize, MALLOC_CAP_DMA));
if (I2sBuffer == nullptr)
{
log_e("send buffer memory allocation failure (size %u)",
I2sBufferSize);
}
memset(I2sBuffer, 0x00, I2sBufferSize);
i2sInit(busNumber,
true,
T_MUXMAP::MuxBusDataSize,
i2sSampleRate,
#if defined(CONFIG_IDF_TARGET_ESP32S2)
// using these modes on ESP32S2 actually allows it to function
// in both x8 and x16
I2S_CHAN_STEREO,
I2S_FIFO_16BIT_DUAL,
#else
// but they won't work on ESP32 in parallel mode, but these will
I2S_CHAN_RIGHT_TO_LEFT,
I2S_FIFO_16BIT_SINGLE,
#endif
dmaBlockCount,
I2sBuffer,
I2sBufferSize);
}
}
void Destruct(const uint8_t busNumber)
{
if (I2sBuffer == nullptr)
{
return;
}
i2sSetPins(busNumber, -1, -1, -1, false);
i2sDeinit(busNumber);
heap_caps_free(I2sBuffer);
I2sBufferSize = 0;
I2sBuffer = nullptr;
MuxMap.Reset();
}
void StartWrite(uint8_t i2sBusNumber)
{
if (MuxMap.IsAllMuxBusesUpdated())
{
MuxMap.ResetMuxBusesUpdated();
i2sWrite(i2sBusNumber);
}
}
void FillBuffers(const uint8_t* data,
size_t sizeData,
uint8_t muxId,
uint8_t i2sBusNumber)
{
// wait for not actively sending data
while (!i2sWriteDone(i2sBusNumber))
{
yield();
}
// to keep the inner loops for EncodeIntoDma smaller
// they will just OR in their values
// so the buffer must be cleared first
if (MuxMap.IsNoMuxBusesUpdate())
{
// clear all the data in preperation for each mux channel to add
memset(I2sBuffer, 0x00, I2sBufferSize);
}
MuxMap.EncodeIntoDma(I2sBuffer,
data,
sizeData,
muxId);
MuxMap.MarkMuxBusUpdated(muxId);
}
};
//
// Implementation of a Double Buffered version of a I2sContext
// Manages the underlying I2S details including the buffer(s)
@@ -317,8 +451,8 @@ public:
//
// T_MUXMAP - NeoEspI2sMuxMap - tracking class for mux state
//
template<typename T_MUXMAP>
class NeoEspI2sDblBuffContext
template<typename T_MUXMAP>
class NeoEspI2sDblBuffContext
{
public:
const static size_t DmaBitsPerPixelBit = 4;
@@ -332,12 +466,12 @@ public:
// and the constructor is called at inconsistent time to other globals
// so its not useful to have or rely on,
// but without it presence they get zeroed far too late
NeoEspI2sDblBuffContext()
// //:
// //I2sBufferSize(0),
// //I2sBuffer(nullptr),
// //I2sEditBuffer(nullptr),
// //MuxMap()
NeoEspI2sDblBuffContext()
//:
//I2sBufferSize(0),
//I2sBuffer(nullptr),
//I2sEditBuffer(nullptr),
//MuxMap()
{
}
@@ -381,12 +515,12 @@ public:
T_MUXMAP::MuxBusDataSize,
i2sSampleRate,
#if defined(CONFIG_IDF_TARGET_ESP32S2)
// using these modes on ESP32S2 actually allows it to function
// in both x8 and x16
// using these modes on ESP32S2 actually allows it to function
// in both x8 and x16
I2S_CHAN_STEREO,
I2S_FIFO_16BIT_DUAL,
#else
// but they won't work on ESP32 in parallel mode, but these will
// but they won't work on ESP32 in parallel mode, but these will
I2S_CHAN_RIGHT_TO_LEFT,
I2S_FIFO_16BIT_SINGLE,
#endif
@@ -415,8 +549,50 @@ public:
MuxMap.Reset();
}
void StartWrite(uint8_t i2sBusNumber)
{
if (MuxMap.IsAllMuxBusesUpdated())
{
MuxMap.ResetMuxBusesUpdated();
// wait for not actively sending data
while (!i2sWriteDone(i2sBusNumber))
{
yield();
}
// copy edit buffer to sending buffer
memcpy(I2sBuffer, I2sEditBuffer, I2sBufferSize);
i2sWrite(i2sBusNumber);
}
}
void FillBuffers(const uint8_t* data,
size_t sizeData,
uint8_t muxId,
uint8_t i2sBusNumber)
{
// to keep the inner loops for EncodeIntoDma smaller
// they will just OR in their values
// so the buffer must be cleared first
if (MuxMap.IsNoMuxBusesUpdate())
{
// clear all the data in preperation for each mux channel to add
memset(I2sEditBuffer, 0x00, I2sBufferSize);
}
MuxMap.EncodeIntoDma(I2sEditBuffer,
data,
sizeData,
muxId);
MuxMap.MarkMuxBusUpdated(muxId);
}
};
//
// Implementation of the low level interface into i2s mux bus
//
@@ -462,19 +638,7 @@ public:
void StartWrite()
{
if (s_context.MuxMap.IsAllMuxBusesUpdated())
{
s_context.MuxMap.ResetMuxBusesUpdated();
// wait for not actively sending data
while (!IsWriteDone())
{
yield();
}
// copy edit buffer to sending buffer
memcpy(s_context.I2sBuffer, s_context.I2sEditBuffer, s_context.I2sBufferSize);
i2sWrite(T_BUS::I2sBusNumber);
}
s_context.StartWrite(T_BUS::I2sBusNumber);
}
bool IsWriteDone() const
@@ -484,18 +648,7 @@ public:
void FillBuffers(const uint8_t* data, size_t sizeData)
{
if (s_context.MuxMap.IsNoMuxBusesUpdate())
{
// clear all the data in preperation for each mux channel to add
memset(s_context.I2sEditBuffer, 0x00, s_context.I2sBufferSize);
}
s_context.MuxMap.EncodeIntoDma(s_context.I2sEditBuffer,
data,
sizeData,
_muxId );
s_context.MuxMap.MarkMuxBusUpdated(_muxId);
s_context.FillBuffers(data, sizeData, _muxId, T_BUS::I2sBusNumber);
}
void MarkUpdated()
@@ -598,17 +751,23 @@ private:
#if defined(CONFIG_IDF_TARGET_ESP32S2)
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux16Bus;
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux16Bus;
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0DblMux8Bus;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s0DblMux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8DblWs2812xMethod;
#else
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux16Bus;
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux16Bus;
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux8Bus;
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux16Bus;
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux8Bus;
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux16Bus;
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1DblMux8Bus;
#endif
@@ -654,6 +813,8 @@ typedef NeoEsp32I2s0X16Sk6812Method NeoEsp32I2s0X16Lc8812Method;
#if !defined(CONFIG_IDF_TARGET_ESP32S2)
// I2s1x8
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1DblMux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8DblWs2812xMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Ws2812xMethod;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2805, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Ws2805Method;
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Sk6812Method;