Unknown duplicated data from I2C sensor being sent via BLE

Hi everyone,

We are using nRF52832, S132 with nRF5_SDK_17.1.0_ddde560 SDK. Our application collects data from a sensor via I2C and then sends this data to a smartphone via BLE. The sensor has an internal FIFO buffer, which takes about 80ms to fill up. We use an app timer of 60ms, which will send a TWI command to the sensor to read how many samples are available in the buffer, and then read the burst of data from the sensor buffer back to the MCU. So far, we implemented blocking TWI commands to read and write from the sensor, which works flawlessly.

Since our app prioritizes low consumption, we want to implement non-blocking TWI. So far, we have implemented it successfully as it can read data from sensors and we see a significant reduction in power consumption before and after implementing this change. However, there is a very weird issue arises after the change. Once we power reset the device, the first round of dataset collection goes perfectly fine. We make our computer app to visualize the data received via BLE and it looks exactly the waveform we want it to be. However, if we terminate this dataset and start a new one (which will send the command to stop and restart the sensor), the first few seconds of the dataset will show weird data on the graph. From our experience, this data should be 1 single data point being sent multiple times. It is like expecting a sinusoidal waveform, but you see a staircase signal instead. This issue only lasts for about 3-4 seconds and it goes away and the dataset will run without this issue again. However, if we terminate this dataset and start another new one, this issue will happen again for the first 3-4 seconds. Following dataset may or may not have this issue, however, the chance runs into this issue for any dataset after the first dataset is high (90% chance). Only the first dataset after the power reset won't have this issue at all.

Below is how the normal signal should look like

Below is how the signal looks like when this issue happens. The x-value is ms so each BLE packet will take a very small part of the graph (60ms). Those tiny ripples before 7000 x-value are made of the micro horizontal lines, which are due to 1 single datapoint filled up the whole BLE packet, instead of multiple data points from sensor sending to the computer to create a smooth graph.

We have done many tests and ensured it is not because of another i2c slave. There is a 1000-byte local buffer on the MCU that sits between the sensor buffer and the TX buffer of the BLE packet. This buffer is to package the data from the sensor into the format we want before sending it to the smartphone via BLE packet. This buffer is also clear at the beginning of a dataset so local buffer should not be the reason. We also ensure BLE signal strength is good during the test. Sharing the same hardware and almost similar firmware (non-blocking TWI vs blocking TWI), we cannot understand why we have this issue and how we can solve this problem as we have aimed at any possible culprit we can think of, including BLE, timer. TWI priority is APP_IRQ_PRIORITY_LOW_MID. BLE parameters have more than enough bandwidth to handle the BLE packet at the sensor data rate. 

We hope someone can give us a hint on how to tackle this problem. Thank you very much for your help.
Best regards,
Xander
Parents
  • Hi!

    1) 

    However, if we terminate this dataset and start a new one (which will send the command to stop and restart the sensor), the first few seconds of the dataset will show weird data on the graph

    You only restart the sensor, correct? The nRF52832 is powered on all the time during this?

    2) Is there a startup time for the senor in the senor datasheet?

    3) This is only happening with the non-blocking TWI implementation ?

  • Hi Sigurd,

    You only restart the sensor, correct? The nRF52832 is powered on all the time during this?

    Yes, the whole device has continuous power supply. No sensors have the power cutoff at all during the app runtime. When we restart the sensor, we send a specific I2C command dedicated to restart the sensor, and not hardware power resetting the sensor.

    Is there a startup time for the senor in the senor datasheet?

    it should be. We will verify this again and let you know. This could be 1 of the potential culprit we haven't thought about.

    This is only happening with the non-blocking TWI implementation ?

    Yes, it is

    Best regards,
    Xander

  • Xander To said:
    it should be. We will verify this again and let you know. This could be 1 of the potential culprit we haven't thought about.

    Ok, let me know

Reply Children
  • Hi Sigurd,

    We have found the actual reason. It's not because of the startup time of sensor. We still don't understand why this change can fix the problem but here is what we changed.

    Originally, to read from the sensor FIFO, we schedule a non-blocking TWI command using the TWI manager to get the sensor FIFO read pointer. We then schedule another non-blocking TWI command that gets the sensor FIFO write pointer, with a callback function to calculate how many samples to read from the FIFO for processing.

    #define SENSOR_READ(p_reg_addr, p_buffer, byte_cnt) \
        NRF_TWI_MNGR_WRITE(SENSOR_ADDRESS, p_reg_addr, 1,        NRF_TWI_MNGR_NO_STOP), \
        NRF_TWI_MNGR_READ (SENSOR_ADDRESS, p_buffer,   byte_cnt, 0)
    
    void send_i2c_sensor(void (*callback)(ret_code_t result, void *p_user_data), void* p_user_data, const nrf_twi_mngr_transfer_t* transfers, const uint8_t num_of_transfer){
        static nrf_twi_mngr_transaction_t NRF_TWI_MNGR_BUFFER_LOC_IND p_transaction;
        p_transaction.callback = callback;
        p_transaction.p_user_data = p_user_data;
        p_transaction.p_transfers = transfers;
        p_transaction.number_of_transfers = num_of_transfer;
        p_transaction.p_required_twi_cfg = NULL;
        APP_ERROR_CHECK(nrf_twi_mngr_schedule(sensor_p_nrf_twi_mngr, &p_transaction));
    }
    void sensor_checkNewData(){
        static uint8_t pointers_value[2] = {0, 0};
        // get Read pointer
        static sensor_reg readPointerAddr_reg  = SENSOR_FIFOREADPTR;
        static nrf_twi_mngr_transfer_t const readPointertransfers[] = {SENSOR_READ(&readPointerAddr_reg, &pointers_value[0], 1)};
        send_i2c_sensor(NULL, NULL, readPointertransfers, sizeof(readPointertransfers) / sizeof(readPointertransfers[0]));
        // get Write pointer
        static sensor_reg writePointerAddr_reg  = SENSOR_FIFOWRITEPTR;
        static nrf_twi_mngr_transfer_t const writePointertransfers[] = {SENSOR_READ(&writePointerAddr_reg, &pointers_value[1], 1)};
        send_i2c_sensor(twi_pointer_cb, &pointers_value, writePointertransfers, sizeof(writePointertransfers) / sizeof(writePointertransfers[0]));
    }

    We discovered that when we instead combine the transfers into one non-blocking TWI command schedule, instead of two schedules, the problem goes away. We also use a repeated start condition between the transfers instead of a stop and then a new start condition between transfers in the new code

    void sensor_checkNewData(){
        static uint8_t pointers_value[2] = {0, 0};
        // get Read pointer
        static sensor_reg readPointerAddr_reg  = SENSOR_FIFOREADPTR;
        //get Write pointer
        static sensor_reg writePointerAddr_reg  = SENSOR_FIFOWRITEPTR;
    
        static nrf_twi_mngr_transfer_t transfers[4];
    
        //Write Transfer
        transfers[0].p_data = &readPointerAddr_reg;
        transfers[0].length = 1;
        transfers[0].operation = NRF_TWI_MNGR_WRITE_OP(SENSOR_ADDRESS); //(((SENSOR_ADDRESS) << 1) | 0)
        transfers[0].flags = NRF_TWI_MNGR_NO_STOP;
    
        //Read Transfer
        transfers[1].p_data = &pointers_value[0];
        transfers[1].length = 1;
        transfers[1].operation = NRF_TWI_MNGR_READ_OP(SENSOR_ADDRESS); //(((SENSOR_ADDRESS) << 1) | 1)
        transfers[1].flags = NRF_TWI_MNGR_NO_STOP;
    
        //Write Transfer
        transfers[2].p_data = &writePointerAddr_reg;
        transfers[2].length = 1;
        transfers[2].operation = NRF_TWI_MNGR_WRITE_OP(SENSOR_ADDRESS); //(((SENSOR_ADDRESS) << 1) | 0)
        transfers[2].flags = NRF_TWI_MNGR_NO_STOP;
    
        //Read Transfer
        transfers[3].p_data = &pointers_value[1];
        transfers[3].length = 1;
        transfers[3].operation = NRF_TWI_MNGR_READ_OP(SENSOR_ADDRESS); //(((SENSOR_ADDRESS) << 1) | 1)
        transfers[3].flags = 0;
    
        send_i2c_sensor(twi_pointer_cb, &pointers_value, transfers, sizeof(transfers) / sizeof(transfers[0]));
    }

    Do you have any idea why getting the read and write pointers separately with two non-blocking TWI manager schedules could trigger this issue? I thought this would be the same as calling 2 separate blocking TWI commands, each ending with a stop flag.

    Best regards,

    Xander

  • Happy to hear the issue is resolved.

    Xander To said:
    Do you have any idea why getting the read and write pointers separately with two non-blocking TWI manager schedules could trigger this issue? I thought this would be the same as calling 2 separate blocking TWI commands, each ending with a stop flag.

    With the repeated start condition, the i2c bus is not released between the transfers, the i2c master maintains control of the bus in the entire operation, and this can help to ensure that the entire operation is completed without interruption, timing issues, etc. 

Related