I’m getting audio corruption in my firmware when I declare large dma buffers (using the Daisy macros) and use them for i2c. The audio output sounds broken (like the data is being overwritten). I’ve recreated this in some test code, and although the audio sounds different, it still sounds very broken. This code is just sending arbitrary data to and from an RPi Pico.
In the test example, if I comment out the code which does the i2c, then pass-through audio sounds correct. EDIT- Also, just removing DMA_BUFFER_MEM_SECTION, so the data is allocate normally, the problem goes away and the output audio is correct.
The strange thing is, in my main firmware the audio is corrupted just by defining the buffer as DMA_BUFFER_MEM_SECTION, I don’t even have to do any DMA over i2c for the problem to exhibit itself! Which suggests maybe the linker has overlapped memory somewhere?
Is it valid to create dma buffers of this size? The actual i2c transactions work, and the correct data is sent, it just screws up the audio. Any help much appreciated as I’m completely stumped!
#include <cstdint>
#include "daisy_seed.h"
daisy::DaisySeed hw;
////////////////////////////
static void i2c_dma_callback(void *data, daisy::I2CHandle::Result result);
class i2c_bus
{
public:
struct DmaResult
{
volatile bool m_complete = false;
volatile bool m_success = false;
};
i2c_bus()
{
daisy::I2CHandle::Config i2c_config;
i2c_config.periph = daisy::I2CHandle::Config::Peripheral::I2C_1;
i2c_config.pin_config.scl = daisy::seed::D11;
i2c_config.pin_config.sda = daisy::seed::D12;
i2c_config.mode = daisy::I2CHandle::Config::Mode::I2C_MASTER;
i2c_config.speed = daisy::I2CHandle::Config::Speed::I2C_1MHZ;
m_i2c.Init(i2c_config);
}
bool read_data(uint8_t i2c_address, uint8_t *data, uint16_t size, uint32_t timeout)
{
const daisy::I2CHandle::Result res = m_i2c.ReceiveBlocking(i2c_address, data, size, timeout);
return res == daisy::I2CHandle::Result::OK;
}
bool read_data_dma(uint8_t i2c_address, uint8_t *data, uint16_t size, uint32_t timeout, volatile DmaResult &dma_result)
{
const daisy::I2CHandle::Result res =
m_i2c.ReceiveDma(i2c_address, data, size, i2c_dma_callback, (void *)(&dma_result));
return res == daisy::I2CHandle::Result::OK;
}
bool write_data(uint8_t i2c_address, uint8_t *data, uint16_t size, uint32_t timeout)
{
const daisy::I2CHandle::Result res = m_i2c.TransmitBlocking(i2c_address, data, size, timeout);
return res == daisy::I2CHandle::Result::OK;
}
bool write_data_dma(uint8_t i2c_address, uint8_t *data, uint16_t size, uint32_t timeout, volatile DmaResult &dma_result)
{
const daisy::I2CHandle::Result res =
m_i2c.TransmitDma(i2c_address, data, size, i2c_dma_callback, (void *)(&dma_result));
return res == daisy::I2CHandle::Result::OK;
}
private:
daisy::I2CHandle m_i2c;
};
static void i2c_dma_callback(void *data, daisy::I2CHandle::Result result)
{
i2c_bus::DmaResult *dma_result = reinterpret_cast<i2c_bus::DmaResult *>(data);
dma_result->m_complete = true;
dma_result->m_success = result == daisy::I2CHandle::Result::OK;
}
////////////////////////////
constexpr uint8_t i2c_address = 16;
constexpr uint32_t other_data_size = 32 * 1024;
DSY_SDRAM_BSS uint8_t other_data[other_data_size];
constexpr uint32_t dma_data_block_size = 96 * 512;
DMA_BUFFER_MEM_SECTION uint8_t dma_data_block[dma_data_block_size];
constexpr uint8_t request_data_command = 16;
constexpr uint8_t send_data_command = 17;
bool test_receive(i2c_bus &i2c, uint32_t timeout)
{
uint8_t command = uint8_t(request_data_command);
bool success = i2c.write_data(i2c_address, &command, sizeof(command), timeout);
if (!success)
{
daisy::DaisySeed::PrintLine("Failed receive command");
return false;
}
volatile i2c_bus::DmaResult dma_result_receive;
success = i2c.read_data_dma(i2c_address, dma_data_block, dma_data_block_size, timeout, dma_result_receive);
if (success == false)
{
daisy::DaisySeed::PrintLine("Failed receive data");
return false;
}
else
{
while (dma_result_receive.m_complete == false)
{
}
}
daisy::DaisySeed::PrintLine("test_receive() success!");
return true;
}
bool test_send(i2c_bus &i2c, uint32_t timeout)
{
uint8_t command = uint8_t(send_data_command);
bool success = i2c.write_data(i2c_address, &command, sizeof(command), timeout);
if (success == false)
{
daisy::DaisySeed::PrintLine("Failed send command");
return false;
}
volatile i2c_bus::DmaResult dma_result_send;
success = i2c.write_data_dma(i2c_address, dma_data_block, dma_data_block_size, timeout, dma_result_send);
if (success == false)
{
daisy::DaisySeed::PrintLine("Failed send data");
return false;
}
else
{
volatile bool *done = &dma_result_send.m_complete;
while (*done == false)
{
}
}
daisy::DaisySeed::PrintLine("test_send() success!");
return true;
}
void audio_callback(const float *in, float *out, size_t size)
{
for (size_t s = 0; s < size; ++s)
{
out[s] = in[s];
}
}
int main(void)
{
hw.Init();
hw.SetAudioBlockSize(48); // number of samples handled per callback
hw.SetAudioSampleRate(daisy::SaiHandle::Config::SampleRate::SAI_48KHZ);
hw.StartLog(true);
i2c_bus i2c;
const uint32_t timeout = 1000;
hw.StartAudio(audio_callback);
while (1)
{
while (test_send(i2c, timeout) == false)
{
}
// hw.DelayMs(100);
while (test_receive(i2c, timeout) == false)
{
}
}
}