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:

Fullscreen
1
2
3
4
5
6
7
8
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
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Here are the relevant declarations:

Fullscreen
1
2
3
4
5
6
7
8
#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};
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Here is my main function:

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**@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.");
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

My start_i2s function:

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

and my data handler:

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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)
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX