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

Sending I2S data via BLE using queue

Hi,

I am writing an application that reads data from the I2S interface, adds it to a queue and sends it via BLE (the code I have implemented so far is added at the end of this post).

In a previous thread (https://devzone.nordicsemi.com/f/nordic-q-a/87656/create-custom-event-to-trigger-updating-data-in-gatt-table), I have reached a state where I am able to send arbitrary data via BLE. I also know that reading data from the I2S works. However, integrating those parts into one application causes the application to run into an error (all 4 LEDs on my nRF52 DK are on).

EDIT: For clarification: the nRF is advertising and I can connect to it from nRF Connect for Desktop and Mobile. The problem starts when I enable notifications and the code, thereby, enters the if-clause in line 46 in my main function (i2s_transfer becomes true).

The code is based on the ble_app_template and the I2S loopback example. I'm using the nRF52 DK, nRF5 SDK 17.1.0 with S132 and Segger emStudio 5.42a.

Could there be too many interrupts that call the data_handler so that the rest of the code cannot be executed (I'm sampling with ~ 44 kHz, so the buffer of 256 bytes will be filled quickly)? I'm also thinking that the allocated RAM could be insufficient (I attach my section placement macros below).

Can anybody suggest a fix that I can try or an alternative implementation to achieve what I have mentioned above? Any help is appreciated.

CODE:

The section placement macros:

FLASH_PH_START=0x0
FLASH_PH_SIZE=0x80000
RAM_PH_START=0x20000000
RAM_PH_SIZE=0x10000
FLASH_START=0x26000
FLASH_SIZE=0x5a000
RAM_START=0x20002ad8
RAM_SIZE=0xd528

Here are the relevant declarations:

#define I2S_DATA_BLOCK_WORDS    256

static uint32_t m_buffer_rx[2][I2S_DATA_BLOCK_WORDS];
static uint32_t m_buffer_tx[2][I2S_DATA_BLOCK_WORDS];

NRF_QUEUE_DEF(uint8_t, m_queue, 3072, NRF_QUEUE_MODE_OVERFLOW);

uint8_t m_array[244] = {0};

Here is my main function:

/**@brief Function for application main entry.
 */
int main(void)
{
    bool erase_bonds;
    uint8_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 execution.
    NRF_LOG_INFO("Template example started.");

    // Start advertising
    advertising_start(erase_bonds);

    // Enter main loop.
    for (;;)
    {
        if (i2s_transfer) 
        {
            // start i2s
            err_code = start_i2s();
            if (err_code == NRF_SUCCESS) 
            {
                NRF_LOG_INFO("I2S started");
            }
            else
            {
                NRF_LOG_INFO("Failed starting I2S");
            }

            if (nrf_queue_utilization_get(&m_queue) >= 244)
            {
                // get data from the queue
                err_code = nrf_queue_read(&m_queue, &m_array, sizeof(m_array));
                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);
                }

                // send data via BLE
                err_code = ble_aas_value_update(&m_aas, &m_array);
                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 (err_code == NRF_ERROR_RESOURCES)
                {
                    NRF_LOG_DEBUG("error resources");
                    if (ble_ready != true)
                    {
                        idle_state_handle();
                    }
                    ble_ready = false;
                }
            }
        }
    }   
     
    nrf_drv_i2s_stop();

    NRF_LOG_FLUSH();

    bsp_board_leds_off();
}

My start_i2s function:

int start_i2s()
{
    uint32_t err_code = NRF_SUCCESS;

    nrf_drv_i2s_buffers_t const initial_buffers = {
        .p_tx_buffer = NULL,
        .p_rx_buffer = m_buffer_rx[0],
    };

    err_code = nrf_drv_i2s_start(&initial_buffers, I2S_DATA_BLOCK_WORDS, 0);
    APP_ERROR_CHECK(err_code);

    return err_code;
}

and my data handler:

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
    {
        uint16_t i;
        uint32_t const * p_word = NULL;
        uint8_t sample[3];

        for (i = 0; i < I2S_DATA_BLOCK_WORDS; ++i)
        {
            p_word = &mp_block_to_check[i];
            //decompose 32-bit sample and discard msb
            sample[0] = ((uint8_t const *)p_word)[0];
            sample[1] = ((uint8_t const *)p_word)[1];
            sample[2] = ((uint8_t const *)p_word)[2];
            printf("\n %2x%2x%2x", sample[0], sample[1], sample[2]);

            // copy data to the queue
            ret_code_t err_code;
            err_code = nrf_queue_write(&m_queue, &sample, sizeof(sample));
            NRF_LOG_DEBUG("data added to queue.");
        }
       
        // 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));

    }
}

  • Hello,

    I have been looking at your case. I will get back to you later.

    Thanks.

    Best Regards,

    Kazi Afroza Sultana

  • Hello,

     Could you please debug into the preprocessor symbols and check the log to see where the error is?  You can send the log to us. 

    Thanks.

    Best Regards,

    Kazi Afroza Sultana

  • Hi Kazi, thank you for your reply. I'm quite new to Segger emStudio and programming the nRF - is there a tutorial on how to debug into the preprocessor symbols and where can I access the proprocessor logs? I didn't find any information about that on the internet.

    For clarification: the nRF is advertising and I can connect to it. The problem starts when I enable notifications and the code, thereby, enters the if-clause in line 46 in my main function (i2s_transfer becomes true).

  • Hi,

    I found the option to Build -> Show Build Log in Segger emStudio. I'm posting the content of the log below. Is that what you were asking for? Did you also see my update below? Could you provide any hints how to solve this?

    Building 'ble_app_template_pca10040_s132' from solution 'ble_app_template_pca10040_s132' in configuration 'Debug'
    1> Assembling 'thumb_crt0.s'
    2> Compiling 'nrf_log_backend_rtt.c'
    1> Compiling 'nrf_log_backend_serial.c'
    1> Compiling 'nrf_log_backend_uart.c'
    2> Compiling 'nrf_log_default_backends.c'
    2> Compiling 'nrf_log_frontend.c'
    1> Compiling 'nrf_log_str_formatter.c'
    1> Compiling 'app_button.c'
    2> Compiling 'app_error.c'
    1> Compiling 'app_error_handler_gcc.c'
    2> Compiling 'app_error_weak.c'
    1> Compiling 'app_scheduler.c'
    1> Compiling 'app_timer2.c'
    1> Compiling 'app_util_platform.c'
    2> Compiling 'crc16.c'
    1> Compiling 'drv_rtc.c'
    1> Compiling 'fds.c'
    2> Compiling 'hardfault_implementation.c'
    1> Compiling 'nrf_assert.c'
    2> Compiling 'nrf_atfifo.c'
    2> Compiling 'nrf_atflags.c'
    2> Compiling 'nrf_atomic.c'
    2> Compiling 'nrf_balloc.c'
    1> Compiling 'nrf_fprintf.c'
    2> Compiling 'nrf_fprintf_format.c'
    2> Compiling 'nrf_fstorage.c'
    2> Compiling 'nrf_fstorage_sd.c'
    1> Compiling 'nrf_memobj.c'
    1> Compiling 'nrf_pwr_mgmt.c'
    1> Compiling 'nrf_ringbuf.c'
    1> Compiling 'nrf_section_iter.c'
    2> Compiling 'nrf_sortlist.c'
    1> Compiling 'nrf_strerror.c'
    2> Compiling 'sensorsim.c'
    2> Compiling 'nrf_queue.c'
    2> Assembling 'ses_startup_nrf52.s'
    2> Assembling 'ses_startup_nrf_common.s'
    1> Compiling 'system_nrf52.c'
    2> Compiling 'boards.c'
    1> Compiling 'nrf_drv_clock.c'
    2> Compiling 'nrf_drv_uart.c'
    2> Compiling 'nrfx_atomic.c'
    1> Compiling 'nrfx_clock.c'
    2> Compiling 'nrfx_gpiote.c'
    1> Compiling 'nrfx_prs.c'
    1> Compiling 'nrfx_uart.c'
    1> Compiling 'nrfx_uarte.c'
    1> Compiling 'nrfx_i2s.c'
    2> Compiling 'nrfx_twi.c'
    1> Compiling 'nrf_drv_twi.c'
    2> Compiling 'bsp.c'
    2> Compiling 'bsp_btn_ble.c'
    1> Compiling 'main.c'
    1> /home/engineer/nRF-Dev/nRF5_SDK_17.1.0_ddde560/examples/ble_peripheral/surag_sense/main.c: In function ‘notification_timeout_handler’:
    1> /home/engineer/nRF-Dev/nRF5_SDK_17.1.0_ddde560/examples/ble_peripheral/surag_sense/main.c:266:45: warning: passing argument 2 of ‘ble_aas_value_update’ from incompatible pointer type [-Wincompatible-pointer-types]
    1> In file included from /home/engineer/nRF-Dev/nRF5_SDK_17.1.0_ddde560/examples/ble_peripheral/surag_sense/main.c:94:
    1> ./ble_aas.h:99:60: note: expected ‘uint8_t *’ {aka ‘unsigned char *’} but argument is of type ‘uint8_t (*)[244]’ {aka ‘unsigned char (*)[244]’}
    1> /home/engineer/nRF-Dev/nRF5_SDK_17.1.0_ddde560/examples/ble_peripheral/surag_sense/main.c: In function ‘main’:
    1> /home/engineer/nRF-Dev/nRF5_SDK_17.1.0_ddde560/examples/ble_peripheral/surag_sense/main.c:1067:53: warning: passing argument 2 of ‘ble_aas_value_update’ from incompatible pointer type [-Wincompatible-pointer-types]
    1> In file included from /home/engineer/nRF-Dev/nRF5_SDK_17.1.0_ddde560/examples/ble_peripheral/surag_sense/main.c:94:
    1> ./ble_aas.h:99:60: note: expected ‘uint8_t *’ {aka ‘unsigned char *’} but argument is of type ‘uint8_t (*)[244]’ {aka ‘unsigned char (*)[244]’}
    1> Compiling 'ble_aas.c'
    1> /home/engineer/nRF-Dev/nRF5_SDK_17.1.0_ddde560/examples/ble_peripheral/surag_sense/pca10040/s132/ses/ble_aas.c: In function ‘on_write’:
    1> /home/engineer/nRF-Dev/nRF5_SDK_17.1.0_ddde560/examples/ble_peripheral/surag_sense/pca10040/s132/ses/ble_aas.c:134:43: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
    1> Compiling 'utils.c'
    2> Compiling 'SEGGER_RTT.c'
    1> Compiling 'SEGGER_RTT_Syscalls_SES.c'
    2> Compiling 'SEGGER_RTT_printf.c'
    1> Compiling 'auth_status_tracker.c'
    1> Compiling 'ble_advdata.c'
    2> Compiling 'ble_advertising.c'
    2> Compiling 'ble_conn_params.c'
    1> Compiling 'ble_conn_state.c'
    2> Compiling 'ble_srv_common.c'
    2> Compiling 'gatt_cache_manager.c'
    2> Compiling 'gatts_cache_manager.c'
    2> Compiling 'id_manager.c'
    1> Compiling 'nrf_ble_gatt.c'
    1> Compiling 'nrf_ble_qwr.c'
    1> Compiling 'peer_data_storage.c'
    1> Compiling 'peer_database.c'
    2> Compiling 'peer_id.c'
    2> Compiling 'peer_manager.c'
    2> Compiling 'peer_manager_handler.c'
    1> Compiling 'pm_buffer.c'
    1> Compiling 'security_dispatcher.c'
    1> Compiling 'security_manager.c'
    2> Compiling 'utf.c'
    1> Compiling 'nrf_sdh.c'
    1> Compiling 'nrf_sdh_ble.c'
    2> Compiling 'nrf_sdh_soc.c'
    1> Generating linker script 'ble_app_template_pca10040_s132.ld'
    1> Linking ble_app_template_pca10040_s132.elf
    Build complete
    

  • Hello,

    You an look at this blog post (+) An introduction to error handling in nRF5 projects - Blogs - Nordic Blog - Nordic DevZone (nordicsemi.com) . It is a bit old but still relevant for debugging on segger. 

    You said that you application causes the application to run into an error while all 4 LEDs on your nRf52 DK are on. So, I guess APP_ERROR_CHECK() has been called with an error code. You should make sure you do a debug build (most SES example projects lets you select ''Debug'' from the build target from drop down menue. Then if you use logging, the log should state which error code was detected in which file at which line. From that point you know, were you should look further. If you do not use logging you can put a breakpoint in the error handler and check with a debugger.

    Thanks.

    Best Regars,

    Kazi Afroza Sultana

Related