Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

Speeding up application to send 24bit I2S data at 16 kHz via BLE

Hi,

I'm trying to send I2S data (~ 16 kHz, 24 bits) via BLE using the nRF52 DK and the nRF5 SDK 17.1.0. After I get the data from the I2S driver (by specification, they are in 32bit format), I add them to very simple ring buffer. The problem is, that this ring buffer becomes full too fast, overwriting older data (which has not been sent yet), which subsequently results in data loss.

Is there a way to speed up my application to avoid this or give higher priority to the application to be able to process the data faster (I guess the main problem is my loop which is casting the 32bit data into 24bit samples)?

Here is my applications logic together with the respective code:

global variables:

/*----------------- I2S -----------------*/
#define I2S_DATA_BLOCK_WORDS    32

static bool i2s_active = false;
static bool i2s_transfer = false;

/*----------------- BUFFERS -----------------*/
#define RNG_BUF_SIZE            (I2S_DATA_BLOCK_WORDS*4*60)
static uint32_t rng_buffer[RNG_BUF_SIZE];
static uint16_t rb_write_ptr = 0;
static uint16_t rb_read_ptr = 0;

static uint8_t m_array[243] = {0};

main():

int main(void)
{
    bool erase_bonds;
    uint32_t err_code;

    // Initialize.
    log_init();
    timers_init();
    buttons_leds_init(&erase_bonds);
    power_management_init();

    // Initialize TWI/ I2C & Setup ADC
    twi_adc_configuration();

    // Initialize i2s
    err_code = i2s_init();
    if (err_code == NRF_SUCCESS)
    {
        NRF_LOG_INFO("I2S successfully initialized.");
    }
    else
    {
        NRF_LOG_INFO("Error initializing I2S.");
    }

    // Initialize BLE
    ble_stack_init();
    gap_params_init();
    gatt_init();
    services_init();
    advertising_init();
    conn_params_init();
    peer_manager_init();

    // Start advertising
    advertising_start(erase_bonds);

    for (;;)
    {
        if (i2s_transfer)
        {
            if (i2s_active != true)
            {
                err_code = start_i2s();

                if (err_code == NRF_SUCCESS)
                {
                    i2s_active = true;
                    NRF_LOG_INFO("I2S started.\n");
                } 
                else
                {
                    NRF_LOG_INFO("Error starting I2S.\n");
                }
            }

            // set back err_code
            err_code = NULL;
            if (rb_read_ptr < rb_write_ptr)
            {
                // fill up an array, which is later sent via BLE
                for (int i = 0; i <= 243; i+=3) 
                {
                    if (rb_read_ptr > RNG_BUF_SIZE)
                    {
                        rb_read_ptr = 0;
                        read_cnt++;
                        NRF_LOG_INFO("reading finished %d", read_cnt);
                    }

                    uint32_t const * p_word = NULL;
                    uint8_t sample[3];
      
                    // cast 32bit sample to 24bit sample
                    p_word = &rng_buffer[rb_read_ptr];
                    sample[0] = ((uint8_t const *)p_word)[2];
                    sample[1] = ((uint8_t const *)p_word)[1];
                    sample[2] = ((uint8_t const *)p_word)[0];

                    rb_read_ptr++;

                    copy 24bit sample to the array
                    memcpy(&m_array[i], &sample, sizeof(sample));
                }

                // when m_array is full, send data via BLE
                err_code = ble_aas_value_update(&m_aas, &m_array);
                if (err_code == NRF_SUCCESS)
                {
                    pkg_cnt++;
                    sample_no+=81;
                }
                if ((err_code != NRF_SUCCESS) &&
                    (err_code != NRF_ERROR_INVALID_STATE) &&
                    (err_code != NRF_ERROR_RESOURCES) &&
                    (err_code != NRF_ERROR_BUSY) &&
                    (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
                   )
                {
                    APP_ERROR_CHECK(err_code);
                }
                else if (err_code == NRF_ERROR_RESOURCES)
                {
                    // if ble stack is full, wait until more space is available
                    // ble_ready will become true, when BLE_GATTS_EVT_HVN_TX_COMPLETE event is received
                    ble_ready = false;
                    while (!ble_ready)
                    {
                        idle_state_handle();
                    }
                    
                    // retry
                    err_code = ble_aas_value_update(&m_aas, &m_array);
                    if (err_code == NRF_SUCCESS)
                    {
                        pkg_cnt++;
                        sample_no+=81;
                    }
                    if ((err_code != NRF_SUCCESS) &&
                        (err_code != NRF_ERROR_INVALID_STATE) &&
                        (err_code != NRF_ERROR_RESOURCES) &&
                        (err_code != NRF_ERROR_BUSY) &&
                        (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
                       )
                    {
                        APP_ERROR_CHECK(err_code);
                    }
                }
            }
        }

        if ((!i2s_transfer) &&
            (i2s_active)
           )
        {
            nrf_drv_i2s_stop();
            i2s_active = false;
            NRF_LOG_INFO("package added to ble stack: %d", pkg_cn);
            NRF_LOG_INFO("samples sent: %d", sample_no);
        }

        idle_state_handle();
        NRF_LOG_FLUSH();
    }

    bsp_board_leds_off();
}

the i2s data handler and the function which adds the received 32bit samples to the ring buffer:

static void write_rng_buffer(uint32_t const * p_block)
{
    if ((rb_write_ptr+I2S_DATA_BLOCK_WORDS) > RNG_BUF_SIZE)
    {
        rb_write_ptr = 0;
        write_cnt++;
        NRF_LOG_INFO("writing finished %d", write_cnt);
    }

    memcpy(&rng_buffer[rb_write_ptr], p_block, (I2S_DATA_BLOCK_WORDS*4));
    rb_write_ptr+=I2S_DATA_BLOCK_WORDS;
}

static void data_handler(nrf_drv_i2s_buffers_t const * p_released,
                         uint32_t                      status)
{
    // 'nrf_drv_i2s_next_buffers_set' is called directly from the handler
    // each time next buffers are requested, so data corruption is not
    // expected.
    ASSERT(p_released);

    // When the handler is called after the transfer has been stopped
    // (no next buffers are needed, only the used buffers are to be
    // released), there is nothing to do.
    if (!(status & NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED))
    {
        return;
    }

    // First call of this handler occurs right after the transfer is started.
    // No data has been transferred yet at this point, so there is nothing to
    // check. Only the buffers for the next part of the transfer should be
    // provided.
    if (!p_released->p_rx_buffer)
    {
        // .p_tx_buffer = m_buffer_tx[1] changed to .p_tx_buffer = NULL, since we only receive data
        nrf_drv_i2s_buffers_t const next_buffers = {
            .p_rx_buffer = m_buffer_rx[1],
            .p_tx_buffer = NULL,
        };
        APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(&next_buffers));

    }
    else
    {
        write_rng_buffer(p_released->p_rx_buffer);

        // The driver has just finished accessing the buffers pointed by
        // 'p_released'. They can be used for the next part of the transfer
        // that will be scheduled now.
        APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(p_released));
    }
}

any help and hint is appreciated!

Parents
  • I was checking the CPU usage by enabling NRF_PWR_MGMT_CONFIG_LOG_ENABLED and NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED. The output in the debug window can be seen below. It seems the cpu usage never exceeds 4 %, so  I guess there has to be a way to speed this process up?

    <info> app_timer: RTC: initialized.
    <info> pwr_mgmt: Init
    TWI device detected at address 0x18.
     Accessing Page no : 00 
     Error in register 1. Write = 01 but Read = 00
     Accessing Page no : 01 
     Accessing Page no : 00 <info> I2S: Initialized.
    <info> app: I2S successfully initialized.
    <info> app: Fast advertising.
    <info> pwr_mgmt: CPU Usage: 47%
    <debug> nrf_ble_gatt: Requesting to update ATT MTU to 247 bytes on connection 0x0.
    <debug> nrf_ble_gatt: Updating data length to 251 on connection 0x0.
    <info> app: PHY update requested.
    <info> app: Connected.
    <info> pwr_mgmt: CPU Usage: 0%
    <debug> nrf_ble_gatt: ATT MTU updated to 247 bytes on connection 0x0 (response).
    <debug> nrf_ble_gatt: Data length updated to 251 on connection 0x0.
    <debug> nrf_ble_gatt: max_rx_octets: 251
    <debug> nrf_ble_gatt: max_tx_octets: 251
    <debug> nrf_ble_gatt: max_rx_time: 2120
    <debug> nrf_ble_gatt: max_tx_time: 2120
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> I2S: Started.
    <info> app: I2S started.
    
    <info> pwr_mgmt: CPU Usage: 4%
    <info> pwr_mgmt: CPU Usage: 4%
    <info> pwr_mgmt: CPU Usage: 5%
    <info> pwr_mgmt: CPU Usage: 4%
    <info> pwr_mgmt: CPU Usage: 3%
    <info> pwr_mgmt: CPU Usage: 5%
    <info> pwr_mgmt: CPU Usage: 3%
    <info> pwr_mgmt: CPU Usage: 2%
    <info> pwr_mgmt: CPU Usage: 2%
    <info> pwr_mgmt: CPU Usage: 2%
    <info> I2S: Stopped.
    <info> app: package added to ble stack: 281
    <info> app: samples sent: 60408
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%

Reply
  • I was checking the CPU usage by enabling NRF_PWR_MGMT_CONFIG_LOG_ENABLED and NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED. The output in the debug window can be seen below. It seems the cpu usage never exceeds 4 %, so  I guess there has to be a way to speed this process up?

    <info> app_timer: RTC: initialized.
    <info> pwr_mgmt: Init
    TWI device detected at address 0x18.
     Accessing Page no : 00 
     Error in register 1. Write = 01 but Read = 00
     Accessing Page no : 01 
     Accessing Page no : 00 <info> I2S: Initialized.
    <info> app: I2S successfully initialized.
    <info> app: Fast advertising.
    <info> pwr_mgmt: CPU Usage: 47%
    <debug> nrf_ble_gatt: Requesting to update ATT MTU to 247 bytes on connection 0x0.
    <debug> nrf_ble_gatt: Updating data length to 251 on connection 0x0.
    <info> app: PHY update requested.
    <info> app: Connected.
    <info> pwr_mgmt: CPU Usage: 0%
    <debug> nrf_ble_gatt: ATT MTU updated to 247 bytes on connection 0x0 (response).
    <debug> nrf_ble_gatt: Data length updated to 251 on connection 0x0.
    <debug> nrf_ble_gatt: max_rx_octets: 251
    <debug> nrf_ble_gatt: max_tx_octets: 251
    <debug> nrf_ble_gatt: max_rx_time: 2120
    <debug> nrf_ble_gatt: max_tx_time: 2120
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> I2S: Started.
    <info> app: I2S started.
    
    <info> pwr_mgmt: CPU Usage: 4%
    <info> pwr_mgmt: CPU Usage: 4%
    <info> pwr_mgmt: CPU Usage: 5%
    <info> pwr_mgmt: CPU Usage: 4%
    <info> pwr_mgmt: CPU Usage: 3%
    <info> pwr_mgmt: CPU Usage: 5%
    <info> pwr_mgmt: CPU Usage: 3%
    <info> pwr_mgmt: CPU Usage: 2%
    <info> pwr_mgmt: CPU Usage: 2%
    <info> pwr_mgmt: CPU Usage: 2%
    <info> I2S: Stopped.
    <info> app: package added to ble stack: 281
    <info> app: samples sent: 60408
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%
    <info> pwr_mgmt: CPU Usage: 0%

Children
No Data
Related