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

I2S: Bit-Alignment in 24bit Mono mode

Hi,

I'm using a nRF52 DK (nRF52832) and nRF5 SDK 17.0.1. to read data from an external ADC via I2S. I can read some data from the I2S bus, but I'm confused about the bit alignment.

The ADC is configured to provide 24 bit samples. The Product Specification of the nRF52832 states (on page 453):

The size of the buffers is specified in a number of 32-bit words. Such a 32-bit memory word can either contain four 8-bit samples, two 16-bit
samples or one right-aligned 24-bit sample sign extended to 32 bit.

Since I need to send the data via BLE, I want to get rid of the sign extension and only send the 24 bit samples (or 3 x 8 bits). So my strategy was the following:

1. the data handler which is called on a new interrupt by the i2s driver, writes the data to mp_block_to_check (see also the i2s example of the SDK):

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
    {
        mp_block_to_check = 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));

    }
}

2. a custom function (similar to the check_samples function of the SDK's i2s example), iterates over all 32-bit samples in mp_block to check and discards the first 8 bits (because each sample should be right-aligned and sign extended to 32-bits):

static void queue_data()
{
    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];

        sample[0] = ((uint8_t const *)p_word)[1];
        sample[1] = ((uint8_t const *)p_word)[2];
        sample[2] = ((uint8_t const *)p_word)[3];
        //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));
        APP_ERROR_CHECK(err_code);
    }
}

3. As you can see, the 3 x 8 bits samples are added to a queue from where they are sent via BLE.

The problem is, that the data on the receiver's side (nrf connect for desktop with nRF42840 dongle) does not match the expected data. I also saw that the sdk_config.h specifies:

//  <o> I2S_CONFIG_ALIGN    -   Alignment

//  <0=>    Left
//  <1=>    Right

#ifndef I2S_CONFIG_ALIGN
#define I2S_CONFIG_ALIGN    0
#endif

So I'm confused. Is the data now right-aligned and sign-extended like stated in the Product Specs or is it aligned as I specify in the sdk_config.h?

Am I handling the bytes in the correct way or am I doing something wrong? Could my problem (received data does not match the exptected data) could have other problems?

Thanks a lot for any hints.

  • Hi,

    I managed to print the data. I realized that the amount of print statements has an influence on the data distortion.

    If I print my data in a loop in my data_handler(), the result is an almost perfect sine wave.

    Printing in the data_handler() AND the queue_data() function will not work (only the prints in the data_handler() will show up), I have to remove the log statement in the data_handler() and add it in the queue_data(). When I plot the data here, the sinus is still recognizable, but already distorted:

    What could be the reason for that?

    My guess is, that while I'm processing the data in queue_data(), the data_handler() is called again and overwrites the data in mp_block_to_check while I'm still processing it.

    for your reference, I'm adding my main loop, which checks if data has been written to mp_block_to_check by the data_handler() and, if yes, passes the address to queue_data().

    // variables are declared in the beginning of the file as global variable
    // I just add it here for reference
    static uint32_t const * volatile mp_block_to_check = NULL;
    static uint8_t m_array[243] = {0};
    
    // my main loop, simplyfied to the most important parts
    for (;;)
    {
        if (mp_block_to_check)
        {
            queue_data(mp_block_to_check);
            mp_block_to_check = NULL;
        }
    
        //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) &&
                (err_code != NRF_ERROR_NOT_FOUND)
           )
        {
            APP_ERROR_CHECK(err_code);
        }
    
        // send data via BLE
        if (err_code != NRF_ERROR_NOT_FOUND)
        {
            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);
            }
            else if (err_code == NRF_ERROR_RESOURCES)
            {
                ble_ready = false;
                while (!ble_ready)
                {
                    idle_state_handle();
                }
                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);
                }
            }
        }
    }

    The function queue_data() is already included in my initial post. ble_aas_value_update just sends the data via BLE.

    Any idea what could cause the data distrotion and how to improve my code to avoid that?

  • I found the answer to my problem. I used the queue in overflow mode, which caused the data distortion + I think NRF_LOG_INFO() and printf() should be avoided for debugging such issues. It seems they also do not output all the data. However, I also don't know another efficient way to debug this.

    More details about this can be found here: devzone.nordicsemi.com/.../sending-i2s-data-via-ble-using-queue

Related