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
  • While debugging ,

    when i read this (uint32_t *)p_released->p_rx_buffer;

    (uint32_t *)p_released->p_tx_buffer; it gives address of the I2s TX / RX buffer

    and when i put *(uint32_t *)p_released->p_rx_buffer; *(uint32_t *)p_released->p_tx_buffer;

    it shows me some 32 bit value and it changes according to the wave i given ,as input.

    problem i faced is . i have stored the 32 bit values in a buffer and after complete the record . i want to play the audio. so i try to play the audio from that buffer but unfortunately i won't play.

    I was stuck in some where

    may be way i read / write  the i2s is wrong?

  • I won't be able to digg into this, but you write that "I was stuck in some where" so it seems there is room for more debugging in order to understand what actually happens (what is "stuck" where?). Unfortunately I do not have any more specific advice.

  • Hi Einar,

    Can you help me in reading the i2s line data

    or the step that i followed in the i2s data read is correct?

  • Unfortunately I do not know the code, nor do I have the HW, and the example is provided as is. But I suggest you debug properly and describe here what you have found. With some luck, some community members may have some experience with this.

  • Hi Einar,

    I have solved that problem

    uint32_t * p_buffer  = (uint32_t *)p_released->p_rx_buffer;

    basically this pointer p_buffer return the value with array so the size of i2s buffer size;

    like i read the p_buffer as

    p_buffer[buffer_size];

Related