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.

Parents
  • Hi,

    It is not clear to mere where the data gets corrupted (in one way or another). Can you try to log the data both when received via I2S, and when you have packed it for BLE transfer and when received just to pinpoint where things get mixed up, to verify if this is an issue of alignment or something else?

    Regarding I2S_CONFIG_ALIGN 0 here means left-aligned, so you should set to 1 if you want right-aligned and use this when configuring the driver (it is mapped to NRFX_I2S_CONFIG_ALIGN, which in turn is used in NRFX_I2S_DEFAULT_CONFIG). If you don't use this and instead provide your ow struct to nrf_drv_i2s_init(), then you need to update the alignment there to change it.

  • Hi Einar,

    thanks for your reponse. I verified now that my data is in fact left aligned, so discarding the first 8 bits should be correct and not cause this problem.

    I also checked the data that are received by the i2s, i.e., I checked the data in p_released->p_rx_buffer in the data_handler(). Turns out that the data is already corrupted here - I'm expecting a sine wave, but the signal I receive is far from that. I will try to create some plots of the logged data and post them the coming days. 

    In the meantime, could it be that BLE and I2S interfere with each other? That some BLE interrupts cause the I2S to not read correcly from the bus?

  • Moritz_S said:
    In the meantime, could it be that BLE and I2S interfere with each other? That some BLE interrupts cause the I2S to not read correcly from the bus?

    The I2S peripheral use DMA, so the CPU is not used during a transaction, so BLE (or other) activity should not cause any problems in that case. But the pre- and post processing could potentially be delayed if higher priority interrupts happen, including BLE related from the SoftDevice. (It should be quick to check if this is related though, by simply stripping away the BLE part and logging the samples via for instance RTT to check if there is any difference)

  • Hi,

    thanks for the info. Could you tell me how to log a large array? I was trying with RTTLogger (which basically works for simple debug messages), but when it comes to logging the data array the chip freezes (All LEDs on). I was trying NRF_LOG_INFO() and NRF_LOG_HEXDUMP_INFO().

    I was using

    NRF_LOG_HEXDUMP_INFO(mp_block_to_check,(I2S_DATA_BLOCK_WORDS*4));

    in the data_handler which is provided in my initial post. mp_block_to_check points towards p_released->p_rx_buffer and I2S_DATA_BLOCK_WORDS is the size of this buffer in uint32_t samples.

  • Hi,

    If you need to log a lot of data fast you could consider not using deferred logging (that might cause timing problems for you app, but if would make logging more efficient). You could also increase buffer sizes in sdk_config.h both for RTT and the logger module. And lastly, perhaps you don't need to log much/for a long time if this is consistent?

  • 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?

Reply
  • 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?

Children
No Data
Related