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!