This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

I2S audio LINEIN data read

Hello there,Thanks for writing an application layer for SGTL5000. https://github.com/NordicPlayground/nRF52-teensy-sgtl5000-audio

I've configured the SGTL5000 to source the audio from the line-in and used the uint32_t drv_sgtl5000_start_mic_loopback(void) function to stream directly to the headphone-out and it's working great. https://github.com/NordicPlayground/nRF52-teensy-sgtl5000-audio/blob/master/drv_sgtl5000.c#L731-L746

I wanted to make this as an trigger based playing after the line-in data acquisition, so I tried to get the data and put in the tmp buffer

from

i2s_data_handler

else if (m_state == SGTL5000_STATE_RUNNING_LOOPBACK)
        {
            // Forward MIC to LINEOUT
            nrf_drv_i2s_buffers_t const next_buffers = {
                .p_rx_buffer = (uint32_t *)p_released->p_tx_buffer,
                .p_tx_buffer = (uint32_t *)p_released->p_rx_buffer,
            };
            APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(&next_buffers));
        }

to

uint32_t tone32[5000];



else if (m_state == SGTL5000_STATE_RUNNING_LOOPBACK)
        {
            rBuff = (uint32_t *)p_released->p_rx_buffer;
            tBuff = (uint32_t *)p_released->p_tx_buffer;
            if(smpl_idx < SAMPLE_LEN-1) {
            tone32[smpl_idx] = *rBuff;
            smpl_idx++;
            }
        }

to store the audio data in a buffer sample

 source the playing from this buffer after I hit a certain condition, but when I tried to do it, the headphone-out is not responded.

to play buffer

i2s_data_handler()
{
    ..
    // This is the normal standard I2S running state. We check module states here and not before to keep implementation a little simpler.
        if (m_state == SGTL5000_STATE_RUNNING)
        {
            // If we are forwarding events to the application, we let the "old" event handler take care of that
             nrf_drv_i2s_buffers_t const next_buffers = {
            //.p_rx_buffer = &m_external_i2s_buffer.rx_buffer[m_external_i2s_buffer.buffer_size_words/2],
            .p_tx_buffer = tone32,
        };
        APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(&next_buffers));
            NRF_LOG_INFO("rs");
             NRF_LOG_FLUSH();
            
            i2s_data_handler_old(p_released->p_rx_buffer, (uint32_t *)p_released->p_tx_buffer, m_external_i2s_buffer.buffer_size_words/2);
        }
        ...
}

uint32_t drv_sgtl5000_start(void)
{
    if (m_state == SGTL5000_STATE_IDLE)
    {
        m_state = SGTL5000_STATE_RUNNING;
        nrf_drv_i2s_buffers_t const initial_buffers = {
            .p_tx_buffer = tone32,
            //.p_rx_buffer = m_external_i2s_buffer.rx_buffer,
        };
        (void)nrf_drv_i2s_start(&initial_buffers, (m_external_i2s_buffer.buffer_size_words/2), 0);
        
        return NRF_SUCCESS;
    }
    
    return NRF_ERROR_INVALID_STATE;
}

Am I missing something in this?

here comes full details of the code

main.c

uint8_t data[8]; 
extern uint32_t tx_buff,rx_buff;
/************************************************************
*   Include support for SGTL5000. 
************************************************************/
#include "drv_sgtl5000.h"
#define AUDIO_FRAME_WORDS                   320
#define I2S_BUFFER_SIZE_WORDS               AUDIO_FRAME_WORDS * 2   // Double buffered - I2S lib will switch between using first and second half
static uint32_t  m_i2s_tx_buffer[I2S_BUFFER_SIZE_WORDS];
static uint32_t  m_i2s_rx_buffer[I2S_BUFFER_SIZE_WORDS];
    
/* Include car sample to demonstrate that sample can be played from application as well */
#define SAMPLE_LEN                  20000
extern uint8_t tonebuffer[20000];
//extern const uint8_t                car_sample[SAMPLE_LEN];
static uint8_t * p_sample           = (uint8_t *)tonebuffer;
static uint32_t sample_idx          = 0;


static bool i2s_sgtl5000_driver_evt_handler(drv_sgtl5000_evt_t * p_evt)
{
    bool ret = false;
    //NRF_LOG_INFO("i2s_sgtl5000_driver_evt_handler %d", p_evt->evt);

    switch (p_evt->evt)
    {
        case DRV_SGTL5000_EVT_I2S_RX_BUF_RECEIVED:
            {
                //NRF_LOG_INFO("i2s_sgtl5000_driver_evt_handler RX BUF RECEIVED");
                // TODO: Handle RX as desired - for this example, we dont use RX for anything
                //uint16_t * p_buffer  = (uint16_t *) p_evt->param.rx_buf_received.p_data_received;
            }
            break;
        case DRV_SGTL5000_EVT_I2S_TX_BUF_REQ:
            {
                //NRF_LOG_INFO("i2s_sgtl5000_driver_evt_handler TX BUF REQ");
                
                /* Play sample! 16kHz sample played on 32kHz frequency! If frequency is changed, this approach needs to change. */
                /* Playback of this 16kHz sample depends on I2S MCK, RATIO, Alignment, format, and channels! Needs to be DIV8, RATIO 128X, alignment LEFT, format I2S, channels LEFT. */
                NRF_LOG_INFO("rm");
                NRF_LOG_FLUSH();
                uint16_t * p_buffer  = (uint16_t *) p_evt->param.tx_buf_req.p_data_to_send;
                uint32_t i2s_buffer_size_words = p_evt->param.rx_buf_received.number_of_words;
                int16_t pcm_stream[i2s_buffer_size_words];  // int16_t - i2s_buffer_size_words size; means we only cover half of data_to_send_buffer, which is fine since we are only using LEFT channel
                
                /* Clear pcm buffer */
                memset(pcm_stream, 0, sizeof(pcm_stream));

                /* Check if playing the next part of the sample will exceed the sample size, if not, copy over part of sample to be played */
                if (sample_idx < SAMPLE_LEN)
                {
                    /* Copy sample bytes into pcm_stream (or remaining part of sample). This should fill up half the actual I2S transmit buffer. */
                    /* We only want half becuase the sample is a 16kHz sample, and we are running the SGTL500 at 32kHz; see DRV_SGTL5000_FS_31250HZ */
                    uint32_t bytes_to_copy = ((sample_idx + sizeof(pcm_stream)) < SAMPLE_LEN) ? sizeof(pcm_stream) : SAMPLE_LEN - sample_idx - 1;
                    memcpy(pcm_stream, &p_sample[sample_idx], bytes_to_copy);
                    sample_idx += bytes_to_copy;
                    ret = true;
                }
                else 
                {
                    /* End of buffer reached. */
                    sample_idx = 0;
                    ret = false;
                }
                
                /* Upsample the decompressed audio */
                /* i < i2s_buffer_size_words * 2 because we have a uint16_t buffer pointer */
                for (int i = 0, pcm_stream_idx = 0; i < i2s_buffer_size_words * 2; i += 2)
                {
                    for (int j = i; j < (i + 2); ++j)
                    {
                        p_buffer[j] = pcm_stream[pcm_stream_idx];
                    }
                    ++pcm_stream_idx;
                }
            }
            break;
    }

    return ret;
}


void clocks_start( void )
{
    NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
    NRF_CLOCK->TASKS_HFCLKSTART = 1;

    while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
}


void gpio_init( void )
{
    nrf_gpio_range_cfg_output(8, 15);
    bsp_board_init(BSP_INIT_LEDS);
}


static void i2s_init()
{
    // Enable audio
    drv_sgtl5000_init_t sgtl_drv_params;
    sgtl_drv_params.i2s_tx_buffer           = (void*)m_i2s_tx_buffer;
    sgtl_drv_params.i2s_rx_buffer           = (void*)m_i2s_rx_buffer;
    sgtl_drv_params.i2s_buffer_size_words   = I2S_BUFFER_SIZE_WORDS/2;
    sgtl_drv_params.i2s_evt_handler         = i2s_sgtl5000_driver_evt_handler;
    sgtl_drv_params.fs                      = DRV_SGTL5000_FS_31250HZ;

    m_i2s_tx_buffer[0] = 167;
    m_i2s_tx_buffer[I2S_BUFFER_SIZE_WORDS/2] = 167;
    NRF_LOG_INFO("size of  m_i2s_tx_buffer %d, %d", sizeof(m_i2s_tx_buffer) / sizeof(uint32_t), I2S_BUFFER_SIZE_WORDS);
    NRF_LOG_INFO("i2s_initial_tx_buffer addr1: %d, addr2: %d", m_i2s_tx_buffer, m_i2s_tx_buffer + I2S_BUFFER_SIZE_WORDS/2);
    NRF_LOG_INFO("i2s_initial_Rx_buffer addr1: %d, addr2: %d", m_i2s_rx_buffer, m_i2s_rx_buffer + I2S_BUFFER_SIZE_WORDS/2);
    
    drv_sgtl5000_init(&sgtl_drv_params);
    drv_sgtl5000_stop();
    NRF_LOG_INFO("Audio initialization done.");
    NRF_LOG_FLUSH();
    
   ///* Demonstrate playback */
   // drv_sgtl5000_start_sample_playback();
   // nrf_delay_ms(1000);
   // drv_sgtl5000_stop();
     /* Demonstrate Mic loopback */
    NRF_LOG_INFO("Loop in main and loopback MIC data.");
    drv_sgtl5000_start_mic_loopback();
    nrf_delay_ms(20000);
    drv_sgtl5000_stop();
    /* Demonstrate playback form application handler */
     nrf_delay_ms(2000);
    drv_sgtl5000_start();
    nrf_delay_ms(20000);
    drv_sgtl5000_stop();
    
    ///* Demonstrate Mic loopback */
    //NRF_LOG_INFO("Loop in main and loopback MIC data.");
    //drv_sgtl5000_start_mic_loopback();
}

int main(void)
 {
    ret_code_t err_code;

    gpio_init();

    err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEFAULT_BACKENDS_INIT();

    clocks_start();

    i2s_init();
    while (true)
    {
       
    }
}

drv_sgtl5000.c



/* Define audio sample length (has to match included audio sample file!), and include the sample itself. */
#define SAMPLE_LEN                  5000
//extern const uint8_t                car_sample[SAMPLE_LEN];
//static uint8_t * p_smpl             = (uint8_t *)car_sample;
static uint32_t smpl_idx            = 0;
uint32_t tone32[5000];
uint8_t tonebuffer[10000];
uint32_t       * rBuff; ///< Pointer to the buffer for received data.
uint32_t       * tBuff; ///< Pointer to the buffer with data to be sent.
/* Structure holding i2s buffers used for transmissions */
static struct
{
    uint32_t * tx_buffer;
    uint32_t * rx_buffer;
    uint32_t   buffer_size_words; 
} m_external_i2s_buffer;
     
/* Definition of TWI states */
static volatile enum
{ 
    SGTL5000_TWI_TRANSFER_IDLE,
    SGTL5000_TWI_TRANSFER_PENDING,
    SGTL5000_TWI_TRANSFER_SUCCESS,
    SGTL5000_TWI_TRANSFER_FAILED
} m_twi_transfer_state = SGTL5000_TWI_TRANSFER_IDLE;

/* Definiton of SGTL5000 driver states */
static enum
{
    SGTL5000_STATE_UNINITIALIZED,   /* Not initialized */
    SGTL5000_STATE_CONFIGURATION,   /* Providing MCLK and configuring via TWI, but not streaming */
    SGTL5000_STATE_IDLE,            /* Initialized, but not running */
    SGTL5000_STATE_RUNNING,         /* Actively streaming audio to/from application */
    SGTL5000_STATE_RUNNING_LOOPBACK,/* Actively running audio loopback (microphone -> speaker) */
    SGTL5000_STATE_RUNNING_SAMPLE,  /* Actively streaming sample */
} m_state = SGTL5000_STATE_UNINITIALIZED;

/* Static function definitions used in this file */
static void sgtl5000_mclk_enable(void);
static void sgtl5000_mclk_disable(void);
static uint32_t sgtl5000_register_read(uint16_t reg_addr, uint16_t * p_reg_data, bool blocking);
static uint32_t sgtl5000_register_write(uint16_t reg_addr, uint16_t reg_data, bool blocking);
static void sgtl5000_register_write_verify(uint16_t reg_addr, uint16_t reg_data, uint16_t ro_mask);


/* TWI event handler, will change TWI state based on TWI events */
static void twi_event_handler(nrf_drv_twi_evt_t const * p_event, void * p_context)
{    
    switch (p_event->type)
    {
        case NRF_DRV_TWI_EVT_DONE:
            m_twi_transfer_state = SGTL5000_TWI_TRANSFER_SUCCESS;
            break;
        
        case NRF_DRV_TWI_EVT_ADDRESS_NACK:
            m_twi_transfer_state = SGTL5000_TWI_TRANSFER_FAILED;
            break;
        
        case NRF_DRV_TWI_EVT_DATA_NACK:
            m_twi_transfer_state = SGTL5000_TWI_TRANSFER_FAILED;
            break;
    }
}

/* I2S event handler matching SDK v14.2 implementation - will be invoked from the SDK v15 I2S event handler in order to ensure compatibility */
static void i2s_data_handler_old(uint32_t const * p_data_received, uint32_t * p_data_to_send, uint16_t number_of_words)
{
    // Non-NULL value in 'p_data_received' indicates that a new portion of
    // data has been received and should be processed.
    if (p_data_received != NULL)
    {
        if (m_state != SGTL5000_STATE_RUNNING)
        {
            // I2S only running in order to provide clock
            return;
        }
        
        drv_sgtl5000_evt_t evt;
        
        evt.evt                                     = DRV_SGTL5000_EVT_I2S_RX_BUF_RECEIVED;
        evt.param.rx_buf_received.number_of_words   = number_of_words;
        evt.param.rx_buf_received.p_data_received   = p_data_received;
        
        m_i2s_evt_handler(&evt);
    }
    
    // Non-NULL value in 'p_data_to_send' indicates that the driver needs
    // a new portion of data to send.
    if (p_data_to_send != NULL)
    {
        if (m_state != SGTL5000_STATE_RUNNING)
        {
            // I2S only running in order to provide clock
            return;
        }
        
        drv_sgtl5000_evt_t evt;
        bool continue_running;
        
        // Request for I2S data to transmit
        evt.evt                              = DRV_SGTL5000_EVT_I2S_TX_BUF_REQ;
        evt.param.tx_buf_req.number_of_words = number_of_words;
        evt.param.tx_buf_req.p_data_to_send  = p_data_to_send;
        
        continue_running = m_i2s_evt_handler(&evt);
        if (!continue_running)
        {
            DRV_SGTL5000_EGU_INSTANCE->TASKS_TRIGGER[SGTL5000_EGU_TASK_STREAMING_STOP] = 1;  
        }
    }
}


/* I2S event handler. Based on the module state, will play sample, playback microphone data, or forward events to the application */
static void i2s_data_handler(nrf_drv_i2s_buffers_t const * p_released,
                         uint32_t                      status)
{
    if (!(status & NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED))
    {
        return;
    }
    
    if (p_released == NULL) 
    {
        // Regardless of module state, when p_released is NULL, we provide the next buffers (to keep implementation a little simpler)
        nrf_drv_i2s_buffers_t const next_buffers = {
            .p_rx_buffer = &m_external_i2s_buffer.rx_buffer[m_external_i2s_buffer.buffer_size_words/2],
            .p_tx_buffer = &m_external_i2s_buffer.tx_buffer[m_external_i2s_buffer.buffer_size_words/2],
        };
        APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(&next_buffers));
        
        i2s_data_handler_old(NULL, &m_external_i2s_buffer.tx_buffer[m_external_i2s_buffer.buffer_size_words/2], m_external_i2s_buffer.buffer_size_words/2);
    }
    else if (p_released->p_rx_buffer == NULL)
    {
        // If RX buffer is NULL, no data has been received, and we need to provide the next buffers. Nothing else done (to keep implementation a little simpler).
        nrf_drv_i2s_buffers_t const next_buffers = {
            .p_rx_buffer = &m_external_i2s_buffer.rx_buffer[m_external_i2s_buffer.buffer_size_words/2],
            .p_tx_buffer = &m_external_i2s_buffer.tx_buffer[m_external_i2s_buffer.buffer_size_words/2],
        };
        APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(&next_buffers));
        
        i2s_data_handler_old(NULL, &m_external_i2s_buffer.tx_buffer[m_external_i2s_buffer.buffer_size_words/2], m_external_i2s_buffer.buffer_size_words/2);
    }
    else
    {
        // This is the normal standard I2S running state. We check module states here and not before to keep implementation a little simpler.
        if (m_state == SGTL5000_STATE_RUNNING)
        {
            // If we are forwarding events to the application, we let the "old" event handler take care of that
             nrf_drv_i2s_buffers_t const next_buffers = {
            //.p_rx_buffer = &m_external_i2s_buffer.rx_buffer[m_external_i2s_buffer.buffer_size_words/2],
            .p_tx_buffer = tone32,
        };
        APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(&next_buffers));
            NRF_LOG_INFO("rs");
             NRF_LOG_FLUSH();
            
            i2s_data_handler_old(p_released->p_rx_buffer, (uint32_t *)p_released->p_tx_buffer, m_external_i2s_buffer.buffer_size_words/2);
        }
        else if (m_state == SGTL5000_STATE_RUNNING_LOOPBACK)
        {
            rBuff = (uint32_t *)p_released->p_rx_buffer;
            tBuff = (uint32_t *)p_released->p_tx_buffer;
            if(smpl_idx < SAMPLE_LEN-1) {
            
            tone32[smpl_idx] = *rBuff;
            smpl_idx++;
            }
            // Forward MIC to LINEOUT
            nrf_drv_i2s_buffers_t const next_buffers = {
                .p_rx_buffer = tBuff,
                .p_tx_buffer = rBuff,
             };
             NRF_LOG_INFO("data : %ld,%x , %x",smpl_idx,*rBuff,*tBuff);
             NRF_LOG_FLUSH();
            APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(&next_buffers));
        }
        //else if (m_state == SGTL5000_STATE_RUNNING_SAMPLE)
        //{
        //    /* Play sample! 16kHz sample played on 32kHz frequency! If frequency is changed, this approach needs to change. */
        //    APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(p_released));
            
        //    /* Make sure settings have not changed - as this sample playback highly depends on the I2S settings */
        //    //APP_ERROR_CHECK_BOOL(m_i2s_config.mck_setup == NRF_I2S_MCK_32MDIV8);     // If these change, please change this function as well
        //    //APP_ERROR_CHECK_BOOL(m_i2s_config.ratio == NRF_I2S_RATIO_128X);          // If these change, please change this function as well
        //    // Also depends on alignement, format, and channels!
            
        //    /* Extract buffer pointer for TX I2S buffer - uint16_t to match up with sample */
        //    uint16_t * p_buffer  = (uint16_t *) p_released->p_tx_buffer;
        //    /* Since the I2S buffer is double buffered, we are only looking at the requested half. And since channels is LEFT, we only look at half of this as well. */
        //    uint32_t i2s_buffer_size_words = m_external_i2s_buffer.buffer_size_words/2;
        //    /* For this requested half, we only need half the amount of sample values because the sample is 16kHz and we are running 32kHz. See NRF_I2S_MCK_32MDIV8 and RATIO. */
        //    int16_t pcm_stream[i2s_buffer_size_words];
            
        //    /* Clear pcm buffer */
        //    memset(pcm_stream, 0, sizeof(pcm_stream));

        //    /* Check if playing the next part of the sample will exceed the sample size, if not, copy over part of sample to be played */
        //    if (smpl_idx < SAMPLE_LEN)
        //    {
        //        /* Copy i2s_buffer_size_words * 2 number of bytes into pcm_stream (or remaining part of sample. This should fill up half the actual I2S transmit buffer. */
        //        /* We only want half becuase the sample is a 16kHz sample, and we are running the SGTL500 at 32kHz; see DRV_SGTL5000_FS_31250HZ */
        //        uint32_t bytes_to_copy = ((smpl_idx + sizeof(pcm_stream)) < SAMPLE_LEN) ? sizeof(pcm_stream) : SAMPLE_LEN - smpl_idx;
        //        memcpy(pcm_stream, &p_smpl[smpl_idx], bytes_to_copy);
        //        smpl_idx += bytes_to_copy;
        //    }
        //    else 
        //    {
        //        /* End of buffer reached. */
        //        smpl_idx = 0;
        //        DRV_SGTL5000_EGU_INSTANCE->TASKS_TRIGGER[SGTL5000_EGU_TASK_STREAMING_STOP] = 1;
        //    }
            
        //    /* Upsample the decompressed audio */
        //    /* i < i2s_buffer_size_words * 2 because we have a uint16_t buffer pointer */
        //    for (int i = 0, pcm_stream_idx = 0; i < i2s_buffer_size_words * 2; i += 2)
        //    {
        //        for (int j = i; j < (i + 2); ++j)
        //        {
        //            p_buffer[j] = pcm_stream[pcm_stream_idx];
        //        }
        //        ++pcm_stream_idx;
        //    }
        //}
        //else 
        //{
        //    // We do not handle this scenario
        //}
    }
}


/* EGU interrupt handler. This handler will take care of stopping the I2S peripheral when requested. */
void DRV_SGTL5000_EGU_IRQHandler(void)
{
    if (DRV_SGTL5000_EGU_INSTANCE->EVENTS_TRIGGERED[SGTL5000_EGU_TASK_STREAMING_STOP] != 0)
    {
        DRV_SGTL5000_EGU_INSTANCE->EVENTS_TRIGGERED[SGTL5000_EGU_TASK_STREAMING_STOP] = 0;
        nrf_drv_i2s_stop();
        m_state = SGTL5000_STATE_IDLE;
    }
}


/* Initialization function of this driver. Handles TWI and I2S initializations, and also sets up the Audio board to function properly. */
uint32_t drv_sgtl5000_init(drv_sgtl5000_init_t * p_params)
{
    uint32_t                err_code;
    uint16_t                chip_id;
    
    //NRF_LOG_INFO("drv_sgtl5000_init() ");
    
    if (current_int_priority_get() < DRV_SGTL5000_TWI_IRQPriority)
    {
        return NRF_ERROR_INVALID_STATE;
    }
    
    
    if (p_params->i2s_tx_buffer             == 0 ||
        p_params->i2s_rx_buffer             == 0 ||
        p_params->i2s_buffer_size_words     == 0 ||
        p_params->i2s_evt_handler           == 0 ||
        p_params->fs                        != DRV_SGTL5000_FS_31250HZ)
    {
        return NRF_ERROR_INVALID_PARAM;
    }
    
    DRV_SGTL5000_EGU_INSTANCE->INTENCLR = 0xFFFFFFFF;
    DRV_SGTL5000_EGU_INSTANCE->INTENSET = 0x0000FFFF;
    
    NVIC_ClearPendingIRQ(DRV_SGTL5000_EGU_IRQn);
    NVIC_SetPriority(DRV_SGTL5000_EGU_IRQn, DRV_SGTL5000_EGU_IRQPriority);
    NVIC_EnableIRQ(DRV_SGTL5000_EGU_IRQn);
    
    // Update configuration
    m_i2s_evt_handler                           = p_params->i2s_evt_handler;
    m_external_i2s_buffer.tx_buffer             = p_params->i2s_tx_buffer;
    m_external_i2s_buffer.rx_buffer             = p_params->i2s_rx_buffer;
    m_external_i2s_buffer.buffer_size_words     = p_params->i2s_buffer_size_words;
    
    // Initialize TWI interface 
    nrf_drv_twi_config_t twi_config = {
        .frequency          = DRV_SGTL5000_TWI_FREQ,
        .interrupt_priority = DRV_SGTL5000_TWI_IRQPriority,
        .scl                = DRV_SGTL5000_TWI_PIN_SCL,
        .sda                = DRV_SGTL5000_TWI_PIN_SDA};
    
    err_code = nrf_drv_twi_init(&m_twi_instance, &twi_config, twi_event_handler, 0);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    
    nrf_drv_twi_enable(&m_twi_instance);
    
    // Disable pull-up resistors on SCL and SDA (already mounted on audio board)
    nrf_gpio_cfg(
        DRV_SGTL5000_TWI_PIN_SCL, 
        NRF_GPIO_PIN_DIR_INPUT,
        NRF_GPIO_PIN_INPUT_CONNECT,
        NRF_GPIO_PIN_NOPULL,
        NRF_GPIO_PIN_S0D1,
        NRF_GPIO_PIN_NOSENSE);
    
    nrf_gpio_cfg(
        DRV_SGTL5000_TWI_PIN_SDA, 
        NRF_GPIO_PIN_DIR_INPUT,
        NRF_GPIO_PIN_INPUT_CONNECT,
        NRF_GPIO_PIN_NOPULL,
        NRF_GPIO_PIN_S0D1,
        NRF_GPIO_PIN_NOSENSE);
    
    // Initialize I2S 
    m_i2s_config.sck_pin      = DRV_SGTL5000_I2S_PIN_BCLK;
    m_i2s_config.lrck_pin     = DRV_SGTL5000_I2S_PIN_LRCLK;
    m_i2s_config.mck_pin      = DRV_SGTL5000_I2S_PIN_MCLK;
    m_i2s_config.sdout_pin    = DRV_SGTL5000_I2S_PIN_TX;
    m_i2s_config.sdin_pin     = DRV_SGTL5000_I2S_PIN_RX;
    m_i2s_config.irq_priority = DRV_SGTL5000_I2S_IRQPriority;
    m_i2s_config.mode         = NRF_I2S_MODE_MASTER;
    m_i2s_config.format       = NRF_I2S_FORMAT_I2S;
    m_i2s_config.alignment    = NRF_I2S_ALIGN_LEFT;
    m_i2s_config.sample_width = NRF_I2S_SWIDTH_16BIT;
    m_i2s_config.channels     = NRF_I2S_CHANNELS_LEFT;

    switch (p_params->fs)
    {
        case DRV_SGTL5000_FS_31250HZ:
            /* Please note that SGTL5000_STATE_RUNNING_SAMPLE relies on this setting, so if this changes, sample playback has to be changed as well */
            m_i2s_config.mck_setup    = NRF_I2S_MCK_32MDIV8; // MCLK = NRF_I2S_MCK_32MDIV8 = 4 MHz.
            m_i2s_config.ratio        = NRF_I2S_RATIO_128X;  // BCLK = NRF_I2S_RATIO_128X = 4 MHz / 128 = 31250 Hz.
            //m_i2s_config.mck_setup    = I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV4; // MCLK = NRF_I2S_MCK_32MDIV8 = 4 MHz.
            //m_i2s_config.ratio        = I2S_CONFIG_RATIO_RATIO_192X; 
            break;
        
        default:
            APP_ERROR_CHECK_BOOL(false);
            break;
    }
    
    
    err_code = nrf_drv_i2s_init(&m_i2s_config, i2s_data_handler);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    
    m_state  = SGTL5000_STATE_IDLE;
    m_volume = -25.f;
    
    sgtl5000_mclk_enable();
    
    // Read ID register
    for (int i = 0; i < 5; ++i)
    {
        err_code = sgtl5000_register_read(DRV_SGTL5000_REGISTER_ADDR_CHIP_ID, &chip_id, true);
        if (err_code == NRF_SUCCESS && (chip_id & 0xFF00) == 0xA000)
        {
            break;
        }
    }

    if (err_code != NRF_SUCCESS || (chip_id & 0xFF00) != 0xA000)
    {
        sgtl5000_mclk_disable();
        return NRF_ERROR_NOT_FOUND;
    }


    sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_ANA_POWER, 
                                        (CHIP_ANA_POWER_REFTOP_POWERUP_Powerup << CHIP_ANA_POWER_REFTOP_POWERUP_POS)
                                        , 0xFFFF);  // VDDD is externally driven with 1.8V (power up reference bias currents)
    sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_REF_CTRL, 
                                        (CHIP_REF_CTRL_VAG_VAL_1_575V << CHIP_REF_CTRL_VAG_VAL_POS) | 
                                        (CHIP_REF_CTRL_BIAS_CTRL_p12_5 << CHIP_REF_CTRL_BIAS_CTRL_POS)
                                        , 0xFFFF);  // VAG=1.575, normal ramp, +12.5% bias current
    sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_LINE_OUT_CTRL, 
                                        (CHIP_LINE_OUT_CTRL_OUT_CURRENT_0_54mA << CHIP_LINE_OUT_CTRL_OUT_CURRENT_POS) | 
                                        (CHIP_LINE_OUT_CTRL_LO_VAGCNTRL_1_650V << CHIP_LINE_OUT_CTRL_LO_VAGCNTRL_POS)
                                        , 0xFFFF);  // LO_VAGCNTRL=1.65V, OUT_CURRENT=0.54mA
    sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_SHORT_CTRL, 
                                        (CHIP_SHORT_CTRL_LVLADJR_125mA << CHIP_SHORT_CTRL_LVLADJR_POS) |
                                        (CHIP_SHORT_CTRL_LVLADJL_125mA << CHIP_SHORT_CTRL_LVLADJL_POS) |
                                        (CHIP_SHORT_CTRL_LVLADJC_250mA << CHIP_SHORT_CTRL_LVLADJC_POS) |
                                        (CHIP_SHORT_CTRL_MODE_LR_ShortDetectResetLatch << CHIP_SHORT_CTRL_MODE_LR_POS) |
                                        (CHIP_SHORT_CTRL_MODE_CM_ShortDetectAutoReset << CHIP_SHORT_CTRL_MODE_CM_POS)
                                        , 0xFFFF);  // allow up to 125mA
    sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_ANA_CTRL, 
                                        (CHIP_ANA_CTRL_MUTE_LO_Mute << CHIP_ANA_CTRL_MUTE_LO_POS) |
                                        (CHIP_ANA_CTRL_EN_ZCD_HP_Enabled << CHIP_ANA_CTRL_EN_ZCD_HP_POS) |
                                        (CHIP_ANA_CTRL_MUTE_HP_Mute << CHIP_ANA_CTRL_MUTE_HP_POS) |
                                        (CHIP_ANA_CTRL_SELECT_ADC_LINEIN << CHIP_ANA_CTRL_SELECT_ADC_POS) |
                                        (CHIP_ANA_CTRL_EN_ZCD_ADC_Enabled << CHIP_ANA_CTRL_EN_ZCD_ADC_POS) |
                                        (CHIP_ANA_CTRL_MUTE_ADC_Mute << CHIP_ANA_CTRL_MUTE_ADC_POS)
                                        , 0xFFFF);  // enable zero cross detectors
    sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_DIG_POWER, 
                                        (CHIP_DIG_POWER_ADC_POWERUP_Enable << CHIP_DIG_POWER_ADC_POWERUP_POS) |
                                        (CHIP_DIG_POWER_DAC_POWERUP_Enable << CHIP_DIG_POWER_DAC_POWERUP_POS) |
                                        (CHIP_DIG_POWER_DAP_POWERUP_Enable << CHIP_DIG_POWER_DAP_POWERUP_POS) |
                                        (CHIP_DIG_POWER_I2S_OUT_POWERUP_Enable << CHIP_DIG_POWER_I2S_OUT_POWERUP_POS) |
                                        (CHIP_DIG_POWER_I2S_IN_POWERUP_Enable << CHIP_DIG_POWER_I2S_IN_POWERUP_POS)
                                        , 0xFFFF);  // power up all digital stuff
    sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_ANA_POWER, 
                                        (CHIP_ANA_POWER_DAC_MONO_Stereo << CHIP_ANA_POWER_DAC_MONO_POS) |
                                        (CHIP_ANA_POWER_VAG_POWERUP_Powerup << CHIP_ANA_POWER_VAG_POWERUP_POS) |
                                        (CHIP_ANA_POWER_ADC_MONO_Mono << CHIP_ANA_POWER_ADC_MONO_POS) |
                                        (CHIP_ANA_POWER_REFTOP_POWERUP_Powerup << CHIP_ANA_POWER_REFTOP_POWERUP_POS) |
                                        (CHIP_ANA_POWER_HEADPHONE_POWERUP_Powerup << CHIP_ANA_POWER_HEADPHONE_POWERUP_POS) |
                                        (CHIP_ANA_POWER_DAC_POWERUP_Powerup << CHIP_ANA_POWER_DAC_POWERUP_POS) |
                                        (CHIP_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_Powerup << CHIP_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_POS) |
                                        (CHIP_ANA_POWER_ADC_POWERUP_Powerup << CHIP_ANA_POWER_ADC_POWERUP_POS) |
                                        (CHIP_ANA_POWER_LINEOUT_POWERUP_Powerup << CHIP_ANA_POWER_LINEOUT_POWERUP_POS)
                                        , 0xFFFF);  // original 0x40FF -> power up: lineout, hp, adc, dac
    sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_LINE_OUT_VOL, 
                                        (CHIP_LINE_OUT_CTRL_OUT_CURRENT_0_54mA << CHIP_LINE_OUT_CTRL_OUT_CURRENT_POS) |
                                        (CHIP_LINE_OUT_CTRL_LO_VAGCNTRL_1_175V << CHIP_LINE_OUT_CTRL_LO_VAGCNTRL_POS)
                                        , 0xFFFF);  // default approx 1.3 volts peak-to-peak
    switch (p_params->fs)
    {
        case DRV_SGTL5000_FS_31250HZ:
            sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_CLK_CTRL, 0x0004, 0xFFFF);  // sys_fs = 32 kHz, rate_mode = sys_fs, mclk_freq = Use PLL
            break;
        default:
            APP_ERROR_CHECK_BOOL(false);
            break;
    }
    sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_I2S_CTRL, 
                                        (CHIP_I2S_CTRL_SCLKFREQ_32Fs << CHIP_I2S_CTRL_SCLKFREQ_POS) |
                                        (CHIP_I2S_CTRL_DLEN_16bits << CHIP_I2S_CTRL_DLEN_POS)
                                        , 0xFFFF);  // SCLK=32*Fs, 16bit, I2S format, Slave mode
    sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_SSS_CTRL, 
                                        (CHIP_SSS_CTRL_DAP_SELECT_I2S_IN << CHIP_SSS_CTRL_DAP_SELECT_POS) |
                                        (CHIP_SSS_CTRL_DAC_SELECT_DAP << CHIP_SSS_CTRL_DAC_SELECT_POS) |
                                        (CHIP_SSS_CTRL_I2S_SELECT_ADC << CHIP_SSS_CTRL_I2S_SELECT_POS)
                                        , 0xFFFF);  // ADC - DAP, DAP - DAC, ADC - I2Sdout
    sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_ADCDAC_CTRL, 0x0000, 0x030F);  // disable dac mute
    sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_DAC_VOL, 0x3C3C, 0xFFFF);  // digital gain, 0dB
    sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_ANA_HP_CTRL, 
                                        (AUDIO_ANA_HP_CTRL_HP_VOL << CHIP_ANA_HP_CTRL_HP_VOL_LEFT_POS) |
                                        (AUDIO_ANA_HP_CTRL_HP_VOL << CHIP_ANA_HP_CTRL_HP_VOL_RIGHT_POS) 
                                        , 0xFFFF);  // set analog gain 0x18 - 0dB. 0x0 +12dB.
    sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_ANA_CTRL, 
                                        (CHIP_ANA_CTRL_EN_ZCD_HP_Disabled << CHIP_ANA_CTRL_EN_ZCD_HP_POS) |
                                        (CHIP_ANA_CTRL_SELECT_ADC_LINEIN << CHIP_ANA_CTRL_SELECT_ADC_POS) |
                                        (CHIP_ANA_CTRL_EN_ZCD_ADC_Enabled << CHIP_ANA_CTRL_EN_ZCD_ADC_POS)
                                        , 0xFFFF);  // enable zero cross detectors. Unmute HP
    
    // MIC input - uncommment this section, and comment out LINEIN section if MIC input is desired
    #if (AUDIO_INPUT_MIC == 1)
        sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_MIC_CTRL, 
                                            (CHIP_MIC_CTRL_GAIN_0dB << CHIP_MIC_CTRL_GAIN_POS) |
                                            (CHIP_MIC_CTRL_BIAS_VOLT_3_00v << CHIP_MIC_CTRL_BIAS_VOLT_POS) |
                                            (CHIP_MIC_CTRL_BIAS_RESISTOR_2k << CHIP_MIC_CTRL_BIAS_RESISTOR_POS)
                                            , 0xFFFF);
        sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_ANA_ADC_CTRL, 
                                            (CHIP_ANA_ADC_CTRL_ADC_VOL_M6DB_NoChange << CHIP_ANA_ADC_CTRL_ADC_VOL_M6DB_POS) |
                                            (AUDIO_ANA_ADC_CTRL_ADC_VOL_MIC << CHIP_ANA_ADC_CTRL_ADC_VOL_RIGHT_POS) |
                                            (AUDIO_ANA_ADC_CTRL_ADC_VOL_MIC << CHIP_ANA_ADC_CTRL_ADC_VOL_LEFT_POS)
                                            , 0xFFFF);    // Volume control.
        sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_ANA_CTRL, 
                                        (CHIP_ANA_CTRL_SELECT_HP_DAC << CHIP_ANA_CTRL_SELECT_HP_POS) |
                                        (CHIP_ANA_CTRL_SELECT_ADC_Microphone << CHIP_ANA_CTRL_SELECT_ADC_POS)
                                        , 0xFFFF);
    #endif //(AUDIO_INPUT_MIC == 1)

    // LINEIN input - uncommment this section, and comment out MIC section if LINEIN input is desired
    // Please note that this section has not bee tested recently - might require some changes in dB settings, etc
    #if (AUDIO_INPUT_LINEIN == 1)
        sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_ANA_ADC_CTRL, 
                                            (CHIP_ANA_ADC_CTRL_ADC_VOL_M6DB_NoChange << CHIP_ANA_ADC_CTRL_ADC_VOL_M6DB_POS) |
                                            (AUDIO_ANA_ADC_CTRL_ADC_VOL_LINEIN << CHIP_ANA_ADC_CTRL_ADC_VOL_RIGHT_POS) |
                                            (AUDIO_ANA_ADC_CTRL_ADC_VOL_LINEIN << CHIP_ANA_ADC_CTRL_ADC_VOL_LEFT_POS)
                                            , 0xFFFF);    // Volume control.
        sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_ANA_CTRL, 
                                        (CHIP_ANA_CTRL_EN_ZCD_HP_Enabled << CHIP_ANA_CTRL_EN_ZCD_HP_POS) |
                                        (CHIP_ANA_CTRL_SELECT_HP_DAC << CHIP_ANA_CTRL_SELECT_HP_POS) |
                                        (CHIP_ANA_CTRL_SELECT_ADC_LINEIN << CHIP_ANA_CTRL_SELECT_ADC_POS) |
                                        (CHIP_ANA_CTRL_EN_ZCD_ADC_Enabled << CHIP_ANA_CTRL_EN_ZCD_ADC_POS)
                                        , 0xFFFF);
    #endif //(AUDIO_INPUT_LINEIN == 1)
    
    sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_DAP_CONTROL, (CHIP_DAP_CONTROL_DAP_EN_Enable << CHIP_DAP_CONTROL_DAP_EN_POS) , 0xFFFF);


    sgtl5000_mclk_disable();
    m_state = SGTL5000_STATE_IDLE;

    return NRF_SUCCESS;
}


/* Ensures that MCLK is high enough for TWI to function properly with audio board. */
static bool sgtl5000_mclk_high_enough_for_twi(void)
{
    if (m_i2s_config.mck_setup == I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV4 ||
        m_i2s_config.mck_setup == I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV3 ||
        m_i2s_config.mck_setup == I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV2)
    {
        return true;
    }
    
    return false;
}


/* Enables MCLK in order to do TWI configurations of audio board. */
static void sgtl5000_mclk_enable(void)
{
    uint32_t             err_code;
    nrf_drv_i2s_config_t i2s_config;
    
    // Initialize I2S interface with MCLK >= 8 MHz in order to provide MCLK for initial SGTL5000 register access
    memcpy(&i2s_config, &m_i2s_config, sizeof(m_i2s_config));
    
    i2s_config.sdout_pin = NRF_DRV_I2S_PIN_NOT_USED;
    i2s_config.sdin_pin  = NRF_DRV_I2S_PIN_NOT_USED;
    i2s_config.mck_setup = NRF_I2S_MCK_32MDIV2;  // 16 MHz
    
    err_code = nrf_drv_i2s_init(&i2s_config, i2s_data_handler);
    if (err_code == NRF_ERROR_INVALID_STATE)
    {
        nrf_drv_i2s_uninit();
        err_code = nrf_drv_i2s_init(&i2s_config, i2s_data_handler);
    }
    
    APP_ERROR_CHECK(err_code);
    
    nrf_drv_i2s_buffers_t const initial_buffers = {
        .p_tx_buffer = m_external_i2s_buffer.tx_buffer,
        .p_rx_buffer = m_external_i2s_buffer.rx_buffer,
    };
    err_code = nrf_drv_i2s_start(&initial_buffers, (m_external_i2s_buffer.buffer_size_words/2), 0);
    APP_ERROR_CHECK(err_code);
}


/* Disables MCLK. */
static void sgtl5000_mclk_disable(void)
{
    uint32_t err_code;
    // Initialize I2S interface with all pins again
    nrf_drv_i2s_stop();
    nrf_drv_i2s_uninit();
    err_code = nrf_drv_i2s_init(&m_i2s_config, i2s_data_handler);
    APP_ERROR_CHECK(err_code);
}


/* Reads register over TWI from audio board. */
static uint32_t sgtl5000_register_read(uint16_t reg_addr, uint16_t * p_reg_data, bool blocking)
{
    nrf_drv_twi_xfer_desc_t twi_xfer;
    uint32_t                twi_flags;
    uint32_t                err_code;
    uint8_t                 reg_addr_buf[2];
    uint8_t                 reg_data_buf[2];
    
    reg_addr_buf[0] = (reg_addr >> 8) & 0xFF;
    reg_addr_buf[1] = (reg_addr)      & 0xFF;
    twi_flags       = NRF_DRV_TWI_FLAG_REPEATED_XFER;
    
    twi_xfer.address          = DRV_SGTL5000_TWI_ADDR;
    twi_xfer.type             = NRF_DRV_TWI_XFER_TXRX;
    twi_xfer.primary_length   = sizeof(reg_addr_buf);
    twi_xfer.p_primary_buf    = reg_addr_buf;
    twi_xfer.secondary_length = sizeof(reg_data_buf);
    twi_xfer.p_secondary_buf  = reg_data_buf;
    
    memset(reg_data_buf, 0, sizeof(reg_data_buf));
    
    m_twi_transfer_state = SGTL5000_TWI_TRANSFER_PENDING;
    
    err_code = nrf_drv_twi_xfer(&m_twi_instance, &twi_xfer, twi_flags);
    
    if (err_code != NRF_SUCCESS)
    {
        sgtl5000_mclk_disable();
        m_twi_transfer_state = SGTL5000_TWI_TRANSFER_PENDING;
        return err_code;
    }
    
    while (blocking && (m_twi_transfer_state == SGTL5000_TWI_TRANSFER_PENDING))
    {
        __WFE();
    }
    
    if (err_code != NRF_SUCCESS || m_twi_transfer_state != SGTL5000_TWI_TRANSFER_SUCCESS)
    {
        m_twi_transfer_state = SGTL5000_TWI_TRANSFER_IDLE;
        return err_code;
    }
    m_twi_transfer_state = SGTL5000_TWI_TRANSFER_IDLE;
    
    *p_reg_data = (reg_data_buf[0] << 8 | reg_data_buf[1]);
    
    return NRF_SUCCESS;
}


/* Writes register over TWI of audio board. */
static uint32_t sgtl5000_register_write(uint16_t reg_addr, uint16_t reg_data, bool blocking)
{
    nrf_drv_twi_xfer_desc_t twi_xfer;
    uint32_t                twi_flags;
    uint32_t                err_code;
    uint8_t                 write_buf[4];
    
    write_buf[0] = (reg_addr >> 8) & 0xFF;
    write_buf[1] = (reg_addr)      & 0xFF;
    write_buf[2] = (reg_data >> 8) & 0xFF;
    write_buf[3] = (reg_data)      & 0xFF;
    twi_flags    = 0;
    
    twi_xfer.address          = DRV_SGTL5000_TWI_ADDR;
    twi_xfer.type             = NRF_DRV_TWI_XFER_TX;
    twi_xfer.primary_length   = sizeof(write_buf);
    twi_xfer.p_primary_buf    = write_buf;
    
    m_twi_transfer_state = SGTL5000_TWI_TRANSFER_PENDING;
    
    err_code = nrf_drv_twi_xfer(&m_twi_instance, &twi_xfer, twi_flags);
    
    if (err_code != NRF_SUCCESS)
    {
        m_twi_transfer_state = SGTL5000_TWI_TRANSFER_PENDING;
        return err_code;
    }
    
    while (blocking && (m_twi_transfer_state == SGTL5000_TWI_TRANSFER_PENDING))
    {
        __WFE();
    }
    
    if (err_code != NRF_SUCCESS || m_twi_transfer_state != SGTL5000_TWI_TRANSFER_SUCCESS)
    {
        m_twi_transfer_state = SGTL5000_TWI_TRANSFER_IDLE;
        return err_code;
    }
    m_twi_transfer_state = SGTL5000_TWI_TRANSFER_IDLE;
    
    return NRF_SUCCESS;
}


/* Writes and verifies register over TWI of audio board. */
static void sgtl5000_register_write_verify(uint16_t reg_addr, uint16_t reg_data, uint16_t ro_mask)
{
    uint16_t read_value;
    
    //NRF_LOG_INFO("Writing 0x%04x to register 0x%04x ", reg_data, reg_addr);
    do
    {
        nrf_delay_us(50);
        sgtl5000_register_write(reg_addr, reg_data, true);
        nrf_delay_us(50);
        sgtl5000_register_read(reg_addr, &read_value, true);
    } while ((read_value & ro_mask) != reg_data);
}


/* Starts the I2S peripheral - which will communicate with the audio board and forward I2S events to the application. */
uint32_t drv_sgtl5000_start(void)
{
    if (m_state == SGTL5000_STATE_IDLE)
    {
        m_state = SGTL5000_STATE_RUNNING;
        nrf_drv_i2s_buffers_t const initial_buffers = {
            .p_tx_buffer = tone32,
            //.p_rx_buffer = m_external_i2s_buffer.rx_buffer,
        };
        (void)nrf_drv_i2s_start(&initial_buffers, (m_external_i2s_buffer.buffer_size_words/2), 0);
        
        return NRF_SUCCESS;
    }
    
    return NRF_ERROR_INVALID_STATE;
}


/* Starts the I2S peripheral, forwards MIC input to Speaker. No events are forwarded to the application. */
uint32_t drv_sgtl5000_start_mic_loopback(void)
{
    if (m_state == SGTL5000_STATE_IDLE)
    {
        m_state = SGTL5000_STATE_RUNNING_LOOPBACK;
        nrf_drv_i2s_buffers_t const initial_buffers = {
            .p_tx_buffer = m_external_i2s_buffer.tx_buffer,
            .p_rx_buffer = m_external_i2s_buffer.rx_buffer,
        };
        (void)nrf_drv_i2s_start(&initial_buffers, (m_external_i2s_buffer.buffer_size_words/2), 0);
        
        return NRF_SUCCESS;
    }
    
    return NRF_ERROR_INVALID_STATE;
}


/* Starts the I2S peripheral, plays an audio sample, then stops the peripheral. No events are forwarded to the application. */
uint32_t drv_sgtl5000_start_sample_playback(void)
{
    if (m_state == SGTL5000_STATE_IDLE)
    {
        m_state = SGTL5000_STATE_RUNNING_SAMPLE;
        nrf_drv_i2s_buffers_t const initial_buffers = {
            .p_tx_buffer = m_external_i2s_buffer.tx_buffer,
            .p_rx_buffer = m_external_i2s_buffer.rx_buffer,
        };
        (void)nrf_drv_i2s_start(&initial_buffers, (m_external_i2s_buffer.buffer_size_words/2), 0);
        
        return NRF_SUCCESS;
    }
    
    return NRF_ERROR_INVALID_STATE;
}


/* Stops the I2S peripheral */
uint32_t drv_sgtl5000_stop(void)
{
    //if (m_state == SGTL5000_STATE_RUNNING ||
    //    m_state == SGTL5000_STATE_RUNNING_SAMPLE)
    //{
        nrf_drv_i2s_stop();
        m_state = SGTL5000_STATE_IDLE;
        
        //return NRF_SUCCESS;
    //}
    
    return NRF_ERROR_INVALID_STATE;
}


/* drv_sgtl5000_volume_set has not been tested nor verified working */
uint32_t drv_sgtl5000_volume_set(float volume_db)
{
    //bool    start_mclk;
    float   volume_float;
    uint8_t volume_right;
    uint8_t volume_left;
    
    if (m_state != SGTL5000_STATE_IDLE && !sgtl5000_mclk_high_enough_for_twi())
    {
        // Need fast MCLK to read/write configuration registers.
        // Cannot do this while streaming audio with 2 MHz MCLK
        return NRF_ERROR_INVALID_STATE;
    }
    
    // Valid range for analog amplifier: -51.5 to +12 dB in .5 dB steps
    if (volume_db > 12.f ||
        volume_db < -51.5f)
    {
        return NRF_ERROR_INVALID_PARAM;
    }
    
    m_volume = volume_db;
    
    // Value 0x00 = 12 dB (max)
    // Value 0x7F = -51.5 dB (min)
    
    volume_float = volume_db + 51.5f;
    APP_ERROR_CHECK_BOOL(volume_float >= 0.f);
    
    volume_right = (uint8_t) 0x7F - ((volume_float / 63.5f) * 127.f);
    volume_left  = volume_right;
    
    APP_ERROR_CHECK_BOOL(volume_right <= 0x7F);
    APP_ERROR_CHECK_BOOL(volume_left <= 0x7F);

    char str[100];
    
    sprintf(str, "Setting volume to %f (0x%02x) ", volume_db, volume_right);
    //NRF_LOG_INFO(str);
    
    if (m_state == SGTL5000_STATE_IDLE)
    {
        sgtl5000_mclk_enable();
        sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_ANA_HP_CTRL, ((volume_right << 8) | volume_left), 0xFFFF);
        sgtl5000_mclk_disable();
    }
    else
    {
        sgtl5000_register_write_verify(DRV_SGTL5000_REGISTER_ADDR_CHIP_ANA_HP_CTRL, ((volume_right << 8) | volume_left), 0xFFFF);
    }
    
    //NRF_LOG_INFO("Volume setting success");
    
    return NRF_SUCCESS;
}


/* drv_sgtl5000_volume_get has not been tested nor verified working */
uint32_t drv_sgtl5000_volume_get(float * p_volume_db)
{
    if (m_state == SGTL5000_STATE_UNINITIALIZED)
    {
        return NRF_ERROR_INVALID_STATE;
    }
    *p_volume_db = m_volume;
    
    return NRF_SUCCESS;
}

Parents Reply Children
No Data
Related