SAI2 for direct communication between 2 Daisy boards.

Hello guys.
Does anyone know if it’s possible to transmit (and receive) a stereo signal from one daisy seed to another without an external codec, simply through SAI2.

For testing I modified daisy seeds ConfigureAudio()

void DaisySeed::ConfigureAudio()
{
    // SAI1 -- Peripheral
    // Configure
    SaiHandle::Config sai_config;
    sai_config.periph          = SaiHandle::Config::Peripheral::SAI_1;
    sai_config.sr              = SaiHandle::Config::SampleRate::SAI_48KHZ;
    sai_config.bit_depth       = SaiHandle::Config::BitDepth::SAI_24BIT;
    sai_config.a_sync          = SaiHandle::Config::Sync::MASTER;
    sai_config.b_sync          = SaiHandle::Config::Sync::SLAVE;
    sai_config.pin_config.fs   = {DSY_GPIOE, 4};
    sai_config.pin_config.mclk = {DSY_GPIOE, 2};
    sai_config.pin_config.sck  = {DSY_GPIOE, 5};

    // Device-based Init
    switch(CheckBoardVersion())
    {
        case BoardVersion::DAISY_SEED_1_1:
        {
            // Data Line Directions
            sai_config.a_dir         = SaiHandle::Config::Direction::RECEIVE;
            sai_config.pin_config.sa = {DSY_GPIOE, 6};
            sai_config.b_dir         = SaiHandle::Config::Direction::TRANSMIT;
            sai_config.pin_config.sb = {DSY_GPIOE, 3};
            I2CHandle::Config i2c_config;
            i2c_config.mode           = I2CHandle::Config::Mode::I2C_MASTER;
            i2c_config.periph         = I2CHandle::Config::Peripheral::I2C_2;
            i2c_config.speed          = I2CHandle::Config::Speed::I2C_400KHZ;
            i2c_config.pin_config.scl = {DSY_GPIOH, 4};
            i2c_config.pin_config.sda = {DSY_GPIOB, 11};
            I2CHandle i2c_handle;
            i2c_handle.Init(i2c_config);
            Wm8731::Config codec_cfg;
            codec_cfg.Defaults();
            Wm8731 codec;
            codec.Init(codec_cfg, i2c_handle);
        }
        break;
        case BoardVersion::DAISY_SEED_2_DFM:
        {
            // Data Line Directions
            sai_config.a_dir         = SaiHandle::Config::Direction::TRANSMIT;
            sai_config.pin_config.sa = {DSY_GPIOE, 6};
            sai_config.b_dir         = SaiHandle::Config::Direction::RECEIVE;
            sai_config.pin_config.sb = {DSY_GPIOE, 3};
            /** PCM3060 disable deemphasis pin */
            GPIO deemp;
            deemp.Init(Pin(PORTB, 11), GPIO::Mode::OUTPUT);
            deemp.Write(0);
        }
        break;
        case BoardVersion::DAISY_SEED:
        default:
        {
            // Data Line Directions
            sai_config.a_dir         = SaiHandle::Config::Direction::TRANSMIT;
            sai_config.pin_config.sa = {DSY_GPIOE, 6};
            sai_config.b_dir         = SaiHandle::Config::Direction::RECEIVE;
            sai_config.pin_config.sb = {DSY_GPIOE, 3};
            dsy_gpio_pin codec_reset_pin;
            codec_reset_pin = {DSY_GPIOB, 11};
            Ak4556::Init(codec_reset_pin);
        }
        break;
    }

    sai_1_handle_.Init(sai_config);

    // SAI2 Config
    SaiHandle::Config sai_config2;
    sai_config2.periph = SaiHandle::Config::Peripheral::SAI_2;
    sai_config2.sr = SaiHandle::Config::SampleRate::SAI_48KHZ;
    sai_config2.bit_depth = SaiHandle::Config::BitDepth::SAI_24BIT;
    sai_config2.pin_config.fs = {DSY_GPIOG, 9};
    sai_config2.pin_config.mclk = {DSY_GPIOA, 1};;
    sai_config2.pin_config.sck = {DSY_GPIOA, 2};
    sai_config2.pin_config.sa = {DSY_GPIOD, 11};
    sai_config2.pin_config.sb = {DSY_GPIOA, 0};

    sai_config2.a_sync = SaiHandle::Config::Sync::MASTER;
    sai_config2.b_sync = SaiHandle::Config::Sync::SLAVE;

    // Data Line Directions
    sai_config2.a_dir = SaiHandle::Config::Direction::TRANSMIT;
    sai_config2.b_dir = SaiHandle::Config::Direction::RECEIVE;
    
    // SAI2 Handle
    SaiHandle sai_2_handle_;
    sai_2_handle_.Init(sai_config2);
    
    // Audio
    AudioHandle::Config audio_config;
    audio_config.blocksize  = 48;
    audio_config.samplerate = SaiHandle::Config::SampleRate::SAI_48KHZ;
    audio_config.postgain   = 1.f;
    // audio_handle.Init(audio_config, sai_1_handle_);
    audio_handle.Init(audio_config, sai_1_handle_, sai_2_handle_);

}
    sai_config2.a_sync = SaiHandle::Config::Sync::MASTER;
    sai_config2.b_sync = SaiHandle::Config::Sync::SLAVE;

    // Data Line Directions
    sai_config2.a_dir = SaiHandle::Config::Direction::TRANSMIT;
    sai_config2.b_dir = SaiHandle::Config::Direction::RECEIVE;

For A and B direction and sync I’m simply swapped for a master and slave daisy.

I’m trying to send audio like so master daisy

void AudioCallback(AudioHandle::InputBuffer  in,
                   AudioHandle::OutputBuffer out,
                   size_t                                size)
{
    float osc_out;

    //Fill the block with samples
    for(size_t i = 0; i < size; i += 2)
    {
        osc_out = osc.Process();

        // Output to codec
        out[0][i] = osc_out;
        out[1][i] = osc_out;

        // Output to SAI2 channels
        out[2][i] = osc_out;
        out[3][i] = osc_out;
    }
}

And receive on slave daisy

void AudioCallback(AudioHandle::InputBuffer  in,
                   AudioHandle::OutputBuffer out,
                   size_t                                size)
{
    float osc_out;

    //Fill the block with samples
    for(size_t i = 0; i < size; i += 2)
    {
        osc_out = osc.Process();

        // Output to codec
        out[0][i] = in[2][i];
        out[1][i] = in[3][i];
    }
}

But seems it’s the wrong way of doing this.
Any ideas how to do it properly?

What is the result when you try it?

I can hear some high-pitched sound, which looks like sine waves but with different phases.
And I can see that all the necessary signals are here.

SAI2 SCK
SAI2 FS
SAI2 SD A (which is only used)
SAI2 MCLK

Basically I did it similarly as in daisy patch

but with swapping this setting for the second MCU:

    sai_config[1].a_sync          = SaiHandle::Config::Sync::SLAVE;
    sai_config[1].b_sync          = SaiHandle::Config::Sync::MASTER;
    sai_config[1].a_dir           = SaiHandle::Config::Direction::TRANSMIT;
    sai_config[1].b_dir           = SaiHandle::Config::Direction::RECEIVE;

Something like that should work. I’d just make sure that indexing is correct on the in and out arrays. Is +2 correct, or is that only for interleaved stereo?

I’d look at the Patch example code (e.g. patch/vco/ex_vco.cpp)) for how to handle four audio channels.

I’ve found a way to send a stereo signal from the master to the slave using SAI2. According to the Daisy pinout, only Block B can act as the master and generate the clock signals. That’s why I configured audio to flow from master MCU Block B (seed::D25) to slave MCU Block A (seed::D26). If necessary, you can also route a stereo signal from the slave MCU back to the master via Block B → Block A.
But seems there no way to send 2 stereo signals from b to a.

Did you correct the callbacks?