Zephyr I2S driver MCLK and i2s_state

Working with the nRF52840 and nRF Connect SDK 2.2.0 and developing for hardware with a codec that requires a clock supplied via the MCLK pin from the I2S0 peripheral.

We're using the Zephyr I2S peripheral driver:
https://developer.nordicsemi.com/nRF_Connect_SDK/doc/2.2.0/zephyr/hardware/peripherals/audio/i2s.html

It's working except there doesn't seem to be any way to set up or enable the MCLK signal on the I2S0 peripheral through the I2S API.  The MCLK pin does get assigned correctly from DTS, but when calling the API function i2s_trigger(), where the configuration is applied and the I2S0 peripheral started, MCLK is disabled.  Note that by writing directly to the CONFIG.MCKFREQ and CONFIG.MCKEN registers after the i2s_trigger() call MCLK turns on and runs correctly, and everything else works well through the API.

Is there a way to configure and enable the I2S0 MCLK through the Zephyr I2S API or another Zephyr API?

Second question - the Zephyr I2S API documentation (link above) and header file define the "enum i2s_state" with the states "I2S_STATE_NOT_READY", "I2S_STATE_READY", "I2S_STATE_RUNNING", ... etc.  The API documentation for the API functions says things like "If the interface is in READY state the number of bytes..." and "This trigger can be used in ERROR state only..." etc.

However, we don't see a way in the API to query what the present interface state is.  The enum "i2s_state" doesn't appear anywhere in the API header except for its declaration.

How is the I2S interface state queried?

Thanks & Regards,

Walt

Parents
  • Hello,

    I agree with both your observations.

    The i2s api does not have any configuration option for MCLK. So if you need it you should configure CONFIG.MCKFREQ and CONFIG.MCKEN registers as you already do, but I would suggest doing it before calling i2s_trigger(), else I suspect you will miss the MCLK on the first i2s transfer (though likely not noticable). I guess an alternative could be to use the i2s_nrfx/nrfx_i2s api directly, which should have the option to configure MCLK.

    Regarding the state(s), it looks like these are internal states that are not exposed (though you can encounter them due to error code when calling the i2s api).

    Kenneth

  • Hi Kenneth,

    Thanks for your prompt reply.  I appreciate it, though I was afraid of that being the answer!

    I agree with the idea behind your suggestion to update the registers before i2s_trigger() is called, and the cons of doing it after (hence my hope of finding another solution.)

    Unfortunately, the Zephyr I2S driver appears to use something like a "Lazy Initialization" design pattern.  The I2S0 peripheral registers are not set after the call to i2s_configure(), but instead on the call to i2s_trigger().  Therefore, attempting to set the MCK registers beforehand gets wiped out when the trigger call overwrites the registers to disable it.

    (I'm noting that for the information of anyone viewing this ticket later.)

    The nrfx driver may be necessary in this case.

    Best,

    Walt

  • I made an internal jira case so it may be looked into for future. I will try to update if there is any updates from that case in future.

    Kenneth

Reply Children
  • Hi wkpolara, dit you get this working? I'm facing the exact same issue at the moment

  • Hi,

    There are a couple of options: 

    1. Use the NRFX I2S driver instead.
    2. Enable the MCLK directly when using the Zephyr I2S (chip dependent), right after triggering I2S

    For #2 what I got working was a not very pretty and kind of a hack.  If you are not using the 52840 you'll need to check the registers for the chip  you are using

        err = i2s_trigger(st_i2s_dev_tx_ptr, I2S_DIR_BOTH, I2S_TRIGGER_START);
        if (err != 0)
        {
            LOG_ERR("Failed to trigger codec err %d", err);
            return e_status_failed;
        }
    
        // The Zephyr I2S driver doesn't (seem to) support the MCLK pin of the Nordic peripheral.  This pin is not
        // part of the defined I2S bus interface, but provides a master clock signal to codecs that need
        // one.  The exsiting hardware uses it to provide at clock to the codec at 4 MHz.
        //
        // Additionally, the I2S configuration doesn't seem to get applied until i2s_trigger() is called.
    
        if (NRF_I2S0->CONFIG.MCKFREQ != 0x20000000)
        {
            NRF_I2S0->CONFIG.MCKFREQ = 0x20000000;
            LOG_INF("I2S CONFIG.MCKFREQ set.");
        }
    
        if (NRF_I2S0->CONFIG.MCKEN != 1)
        {
            NRF_I2S0->CONFIG.MCKEN = 1;
            LOG_INF("I2S CONFIG.MCKEN set.");
        } 
    

    PS - you'll need the header

    #include <nrfx_i2s.h>

Related